You need to enable JavaScript to run this app. 使用MediaCodec与MediaMuxer将PCM编码为AAC时无音频输出问题
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 15:06:03