共计 1592 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点
在 Android 音视频开发中,编码器性能不足常常导致以下典型问题:

- 主线程阻塞:同步调用 MediaCodec API 可能导致 UI 卡顿
- 内存溢出(OOM):未及时释放输入 / 输出缓冲区引发内存泄漏
- 设备发热:软件编码 CPU 占用率高导致功耗飙升
- 编码延迟:复杂场景下帧处理时间超过 33ms(30FPS 要求)
技术方案对比
| 方案类型 | 延迟水平 | 功耗表现 | 兼容性范围 |
|---|---|---|---|
| MediaCodec 软件 | 高(50ms+) | 高 | 全平台支持 |
| FFmpeg 软件 | 中(30ms) | 中 | 需 NDK 集成 |
| 硬件加速 | 低(<20ms) | 低 | 需 API>=21 |
核心实现
MediaCodec 基础配置
-
初始化编码器(以 H.264 为例):
val codec = MediaCodec.createEncoderByType("video/avc") val format = MediaFormat.createVideoFormat("video/avc", width, height).apply {setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface) setInteger(MediaFormat.KEY_BIT_RATE, bitrate) setInteger(MediaFormat.KEY_FRAME_RATE, fps) setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, keyFrameInterval) } codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) -
Surface 输入模式关键代码:
// 创建输入 Surface(带行号示例)val inputSurface = codec.createInputSurface() // 启动编码线程 val outputThread = HandlerThread("EncoderOutput").apply {start() } codec.setCallback(object : MediaCodec.Callback() {override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {// 通常 Surface 模式不需要手动处理输入缓冲区} override fun onOutputBufferAvailable( codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo ) {val outputBuffer = codec.getOutputBuffer(index) // 处理编码后的数据 codec.releaseOutputBuffer(index, false) } }, Handler(outputThread.looper))
性能优化
码率模式对比测试
| BITRATE_MODE | 画质稳定性 | 带宽占用 |
|---|---|---|
| BITRATE_MODE_CBR | 低 | 恒定 |
| BITRATE_MODE_VBR | 中 | 波动 |
| BITRATE_MODE_CQ | 高 | 不可控 |
异步模式线程安全
- 确保所有 MediaCodec 调用在同一个 Looper 线程
- 输出缓冲区处理完成后必须调用 releaseOutputBuffer()
- 避免在 Callback 中执行耗时操作
避坑指南
- 编码器复用崩溃 :调用 reset() 后必须重新 configure()
- 分辨率对齐要求:部分硬件要求 16 像素对齐(如高通方案)
- CSD 数据缺失:需在第一个关键帧前发送 csd-0/csd- 1 格式数据
动手实验
使用以下命令采集编码性能数据:
adb shell am profile start <process> systrace.py --time=10 -o trace.html
分析重点指标:
– 单帧编码耗时
– GPU 拷贝时间
– 缓冲区等待时间
正文完
发表至: 移动开发
四天前
