You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中用MediaCodec将WAV转3gp:生成文件无效求解决

WAV转3GP无效文件问题排查与修复

核心问题分析

你的循环逻辑存在几个关键错误,导致生成的3GP文件无法被正常解析:

1. 输入缓冲区写入长度错误

codecBuffer?.put(inputBuffer)直接将整个缓冲区写入,但实际仅读取了sampleSize字节的有效数据,会混入缓冲区残留的旧数据,导致编码数据损坏。正确写法应限制写入长度:

codecBuffer?.put(inputBuffer, 0, sampleSize)

2. 编解码队列处理顺序错误

你在每次读取输入样本后立刻尝试获取输出,但编码是异步操作,当前输入可能尚未完成编码就去取输出,会漏写大量有效编码数据。正确流程应分为两步:

  • 先将所有输入样本全部喂给编码器
  • 再单独循环获取所有输出数据,直到编码器发出结束信号

3. 未发送编码器结束信号

输入数据全部喂完后,必须向编码器发送结束标记,否则编码器不会输出最后一批数据,文件无法正常收尾:

// 所有输入处理完成后发送结束信号
val inputBufferId = codec.dequeueInputBuffer(codecTimeoutUs)
if (inputBufferId >= 0) {
    codec.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
}

4. 未处理输出结束标记

当输出缓冲区返回MediaCodec.BUFFER_FLAG_END_OF_STREAM标记时,应停止写入并退出循环,避免写入无效数据。

修复后的完整循环代码

// 第一步:喂入所有输入数据
while (true) {
    val sampleSize = extractor.readSampleData(inputBuffer, 0)
    if (sampleSize < 0) break

    val presentationTimeUs = extractor.sampleTime
    val flags = extractor.sampleFlags

    // 等待可用的输入缓冲区,避免因缓冲区满丢数据
    var inputBufferId = codec.dequeueInputBuffer(codecTimeoutUs)
    while (inputBufferId < 0) {
        inputBufferId = codec.dequeueInputBuffer(codecTimeoutUs)
    }

    val codecBuffer = codec.getInputBuffer(inputBufferId)
    codecBuffer?.clear()
    codecBuffer?.put(inputBuffer, 0, sampleSize)
    codec.queueInputBuffer(inputBufferId, 0, sampleSize, presentationTimeUs, flags)

    extractor.advance()
}

// 发送输入结束信号
val inputBufferId = codec.dequeueInputBuffer(codecTimeoutUs)
if (inputBufferId >= 0) {
    codec.queueInputBuffer(inputBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
}

// 第二步:获取所有输出数据
while (true) {
    val outputBufferId = codec.dequeueOutputBuffer(bufferInfo, codecTimeoutUs)
    when {
        outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER -> continue
        outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> {
            // 若格式变化,更新复用器轨道信息
            val newFormat = codec.outputFormat
            outputTrackIndex = muxer.addTrack(newFormat)
            muxer.start()
        }
        outputBufferId >= 0 -> {
            val codecBuffer = codec.getOutputBuffer(outputBufferId)
            if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
                codec.releaseOutputBuffer(outputBufferId, false)
                break
            }
            muxer.writeSampleData(outputTrackIndex, codecBuffer!!, bufferInfo)
            codec.releaseOutputBuffer(outputBufferId, false)
        }
        else -> break
    }
}

// 资源收尾
codec.stop()
codec.release()
extractor.release()
muxer.stop()
muxer.release()

额外检查点

  • 确认MediaExtractor已正确关联WAV文件数据源,并选中了正确的音频轨道
  • 确认MediaCodec初始化时使用3GP支持的编码格式(如audio/mp4a-latm,对应AAC编码)
  • 确认MediaMuxer的输出格式设置为MUXER_OUTPUT_3GPP

内容的提问来源于stack exchange,提问作者oikumo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 09:48:23