Android AAC转PCM实战:从MediaCodec到音频流处理全解析
·
在语音识别、实时通话等场景中,经常需要将AAC编码的音频流转换为PCM格式。今天就来分享下我在项目中实现的完整方案,包含踩坑经验和性能优化技巧。

一、为什么需要自己实现转换?
Android系统虽然提供了MediaPlayer等高级API,但遇到这些场景时就会很头疼:
- 需要实时获取PCM数据做语音分析
- 系统播放器在不同厂商ROM上有兼容问题
- 连续处理长音频时内存暴涨导致OOM
二、技术方案选型
尝试过三种主流方案后,得出以下对比结论:
- FFmpeg方案
- 优点:兼容性最好
-
缺点:CPU占用高(实测多出30%),体积增加5-8MB
-
第三方库(如ExoPlayer)
- 优点:API友好
-
缺点:黑箱操作,无法精细控制缓冲区
-
MediaCodec原生方案
- 优点:硬件加速,延迟最低(实测80ms)
- 缺点:需要处理版本差异(重点!)
最终选择MediaCodec+AudioTrack组合,下面是核心实现:
三、MediaCodec配置要点
// 1. 初始化解码器(注意try-catch块)
val codec = MediaCodec.createDecoderByType("audio/mp4a-latm").apply {
configure(
MediaFormat().apply {
setString(MediaFormat.KEY_MIME, "audio/mp4a-latm")
setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100)
setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1)
setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1024*1024) // 关键!避免BufferOverflow
},
null, // Surface
null, // Crypto
0 // flags
)
start()
}

四、数据流转发核心逻辑
- 输入阶段
- 用dequeueInputBuffer()获取可用缓冲区索引
-
注意处理BUFFER_FLAG_END_OF_STREAM标志
-
输出阶段
- 关键代码:
val info = MediaCodec.BufferInfo() when (val outIndex = codec.dequeueOutputBuffer(info, 10000)) { MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> { // 处理采样率变化 audioTrack.sampleRate = codec.outputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) } MediaCodec.INFO_TRY_AGAIN_LATER -> { /* 重试逻辑 */ } else -> { val buffer = codec.getOutputBuffer(outIndex)!! audioTrack.write(buffer, info.size, AudioTrack.WRITE_BLOCKING) codec.releaseOutputBuffer(outIndex, false) } }
五、避坑指南
-
内存泄漏:必须在Activity的onDestroy中调用
fun release() { codec.stop() codec.release() audioTrack.stop() audioTrack.release() } -
版本适配:
- Android 8.0+建议用异步模式(setCallback)
-
华为EMUI需要额外设置format参数
-
性能数据:
- Redmi Note 11:平均延迟92ms
- 内存占用稳定在15MB以内
六、延伸思考
如果要做更极致的优化,可以考虑: 1. 用Native层实现避免JNI开销 2. 研究OMX_StoreMetaDataInBuffers扩展 3. 尝试直接DSP处理(需要厂商支持)
完整项目代码已上传GitHub,欢迎讨论优化方案~
更多推荐


所有评论(0)