Android中MediaCodec解码FFServer推送的AAC音频无输出无声音问题求助
解决Android MediaCodec解码AAC RTP包输出缓冲区为空的问题
看起来你已经走对了大部分路,但解码输出空缓冲区的问题通常和原始数据格式不匹配或者RTP包处理不当有关,我来帮你一步步排查:
1. 先确认你拿到的是纯AAC帧(剥离RTP头)
RTP数据包包含固定的12字节头部(默认无扩展头),你必须先把这部分剥离,只保留后面的AAC payload。如果直接把整个RTP包喂给MediaCodec,解码器根本识别不了,自然输出空。
你可以这样处理:
// 假设rtpPacket是接收到的完整RTP字节数组 byte[] aacPayload = Arrays.copyOfRange(rtpPacket, 12, rtpPacket.length);
如果你的RTP流带扩展头部,需要先解析扩展长度再调整偏移,FFServer默认AAC流一般不会带,先按12字节处理试试。
2. 给RAW AAC帧添加ADTS头
libvo_aacenc编码出来的AAC是RAW AAC帧(不带ADTS头),而Android的MediaCodec AAC解码器默认需要带ADTS头的帧才能正确解析。你需要手动给每个AAC帧前面加上ADTS头,构造方法如下:
private byte[] addADTSHeader(byte[] rawAAC, int sampleRate, int channels, int bitrate) { int profile = 2; // AAC LC(libvo_aacenc默认是这个profile) int sampleRateIndex = getSampleRateIndex(sampleRate); int channelConfig = channels; int frameLength = rawAAC.length + 7; // ADTS头固定7字节 byte[] adtsHeader = new byte[7]; adtsHeader[0] = (byte) 0xFF; adtsHeader[1] = (byte) 0xF9; adtsHeader[2] = (byte) (((profile - 1) << 6) + (sampleRateIndex << 2) + (channelConfig >> 2)); adtsHeader[3] = (byte) (((channelConfig & 3) << 6) + (frameLength >> 11)); adtsHeader[4] = (byte) ((frameLength & 0x7FF) >> 3); adtsHeaderHeader[5] = (byte) (((frameLength & 7) << 5) + 0x1F); adtsHeader[6] = (byte) 0xFC; // 合并ADTS头和原始AAC帧 byte[] result = new byte[frameLength]; System.arraycopy(adtsHeader, 0, result, 0, 7); System.arraycopy(rawAAC, 0, result, 7, rawAAC.length); return result; } private int getSampleRateIndex(int sampleRate) { switch (sampleRate) { case 96000: return 0; case 88200: return 1; case 64000: return 2; case 48000: return 3; case 44100: return 4; // 你的采样率对应索引4 case 32000: return 5; case 24000: return 6; case 22050: return 7; case 16000: return 8; case 12000: return 9; case 11025: return 10; case 8000: return 11; case 7350: return 12; default: return 4; } }
这一步大概率是核心问题,把处理后的AAC数据再喂给MediaCodec试试。
3. 检查MediaCodec的配置是否正确
确保初始化MediaCodec时的MediaFormat参数完全匹配编码参数:
MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2); format.setInteger(MediaFormat.KEY_BIT_RATE, 128000); format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); // 初始化解码器 MediaCodec decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); decoder.configure(format, null, null, 0); decoder.start();
注意:MIME类型要用MediaFormat.MIMETYPE_AUDIO_AAC,AAC_PROFILE必须和libvo_aacenc的编码profile一致,默认是LC。
4. 确认输入缓冲区的提交逻辑
- 提交数据时必须设置正确的时间戳(PTS),实时流可以用系统时间递增(比如
System.nanoTime() / 1000),如果RTP包自带PTS,直接转换使用更准确。 - 不要过早提交数据,确保解码器已经调用
start()进入RUNNING状态。 - 实时流不要设置
BUFFER_FLAG_END_OF_STREAM,除非流结束。
示例提交代码:
int inputBufferIndex = decoder.dequeueInputBuffer(10000); // 等待10ms if (inputBufferIndex >= 0) { ByteBuffer inputBuffer = decoder.getInputBuffer(inputBufferIndex); inputBuffer.clear(); byte[] processedAAC = addADTSHeader(aacPayload, 44100, 2, 128000); inputBuffer.put(processedAAC); long pts = System.nanoTime() / 1000; // 转换为微秒 decoder.queueInputBuffer(inputBufferIndex, 0, processedAAC.length, pts, 0); }
5. 输出缓冲区的处理逻辑
确保你正确处理了dequeueOutputBuffer的返回值:
- 如果返回
INFO_OUTPUT_FORMAT_CHANGED,需要重新获取输出格式并配置AudioTrack。 - 如果返回有效索引,要读取缓冲区数据,然后释放缓冲区给解码器复用。
示例处理代码:
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 10000); while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = decoder.getOutputBuffer(outputBufferIndex); if (bufferInfo.size > 0) { // 拿到解码后的PCM数据,喂给AudioTrack播放 byte[] pcmData = new byte[bufferInfo.size]; outputBuffer.get(pcmData); audioTrack.write(pcmData, 0, pcmData.length); } decoder.releaseOutputBuffer(outputBufferIndex, false); outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0); } // 处理格式变化 if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { MediaFormat outputFormat = decoder.getOutputFormat(); // 重新配置AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, outputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, AudioTrack.getMinBufferSize(outputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT), AudioTrack.MODE_STREAM); audioTrack.play(); }
最后排查点
- 确认AudioTrack已正确初始化并处于播放状态(有时候解码有输出但AudioTrack没配置好也会没声音,但你当前问题是输出缓冲区空,先解决解码问题)。
- 用日志打印每个步骤的字节数,比如处理后的AAC帧长度、输入缓冲区提交的长度,确认数据确实进入了解码器。
按照上面的步骤排查,应该能解决输出缓冲区为空的问题。
内容的提问来源于stack exchange,提问作者Nicolò
相关产品推荐
相关产品推荐

