使用MediaCodec与MediaMuxer将PCM编码为AAC时无音频输出问题
问题根因及修复方案
以下是代码中存在的核心问题,按影响优先级排序:
1. 错误丢弃AAC编码配置帧
当前对标记为BUFFER_FLAG_CODEC_CONFIG的帧直接释放不传给MediaMuxer,这是最核心的问题。该帧存放的是AAC解码必须的CSD(Codec Specific Data)信息,MediaMuxer需要用这个信息生成正确的m4a文件头,没有这个信息的话,即使后续音频帧数据正常,播放器也无法解码出有效音频,表现就是文件时长正常但无声音。
2. 标志位判断逻辑错误
用bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG判断配置帧的写法错误,flags是按位存储的标记集合,可能同时携带多个标记,必须用按位与的方式判断,否则会漏判携带其他标记的配置帧。
3. 输入PCM数据长度计算错误
encodeAudio方法中用pcmData.capacity()作为输入数据长度传给编码器,如果传入的pcmData已经设置过position/limit,实际可读数据长度应该用pcmData.remaining(),否则会传入大量无效空数据,导致编码结果为静音。
4. 缺少Muxer启动状态判断
没有判断MediaMuxer是否已经启动就调用writeSampleData,如果在onOutputFormatChanged回调(添加音轨、启动Muxer的时机)之前就有编码输出的音频帧,这些帧会被写入未启动的Muxer导致丢失。
修正后的核心代码示例
// 增加全局变量标记Muxer是否已启动 private var isMuxerStarted = false // onOutputBufferAvailable修正逻辑 override fun onOutputBufferAvailable(mc: MediaCodec, outputBufferId: Int, bufferInfo: MediaCodec.BufferInfo) { val outputBuffer = mc.getOutputBuffer(outputBufferId) ?: run { audioEncoder.releaseOutputBuffer(outputBufferId, false) return } // 配置帧需要传给Muxer,不要丢弃 if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { if (isMuxerStarted) { outputBuffer.position(bufferInfo.offset) outputBuffer.limit(bufferInfo.offset + bufferInfo.size) muxer!!.writeSampleData(audioTrackIndex, outputBuffer, bufferInfo) } audioEncoder.releaseOutputBuffer(outputBufferId, false) return } if (bufferInfo.size > 0 && isMuxerStarted) { outputBuffer.position(bufferInfo.offset) outputBuffer.limit(bufferInfo.offset + bufferInfo.size) muxer!!.writeSampleData(audioTrackIndex, outputBuffer, bufferInfo) } audioEncoder.releaseOutputBuffer(outputBufferId, false) if ((bufferInfo.flags and BUFFER_FLAG_END_OF_STREAM) != 0) { cleanupAudioEncoder() } } // onOutputFormatChanged修正逻辑 override fun onOutputFormatChanged(mc: MediaCodec, mf: MediaFormat) { audioTrackIndex = muxer!!.addTrack(audioEncoder.outputFormat) startMuxer() isMuxerStarted = true // 启动后更新标记 } // encodeAudio修正逻辑 fun encodeAudio(pcmData: ByteBuffer) { if (audioInputBufferIds.isNotEmpty()) { val bufferId = updateRemoveAudioBufferIndex() val buffer = audioEncoder.getInputBuffer(bufferId) ?: return buffer.clear() val pcmReadableLength = pcmData.remaining() // 取实际可读长度而非capacity buffer.put(pcmData) val flags = if (encodingStopped) BUFFER_FLAG_END_OF_STREAM else 0 audioEncoder.queueInputBuffer(bufferId, 0, pcmReadableLength, presentationTimeAudio.toLong(), flags) presentationTimeAudio += bufferIntervalInMicroseconds } }
额外检查项
确认你输入的PCM参数和MediaFormat配置完全一致:PCM为16位小端格式、单声道、采样率32000,如果PCM实际参数不匹配,也会导致编码出的音频为静音或杂音。
内容的提问来源于stack exchange,提问作者Johann

