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
相关产品推荐
相关产品推荐

