共计 2453 个字符,预计需要花费 7 分钟才能阅读完成。
移动端视频编码是直播推流、视频通话、本地录像等场景的核心技术,面临实时性要求高、功耗敏感、设备碎片化三大挑战。Android 通过 MediaCodec API 提供软硬件编码能力,但不同厂商实现差异和复杂配置项常导致性能瓶颈。本文将系统解决从 API 选型到内存优化的全链路问题。

一、MediaCodec 工作模式选择
1. 同步 vs 异步模式
-
同步模式 :阻塞式调用,适合简单编码场景
val codec = MediaCodec.createEncoderByType("video/avc") codec.start() val inputBufferId = codec.dequeueInputBuffer(10000) // 阻塞 10ms -
异步模式 (API 21+):事件回调驱动,推荐用于高帧率场景
codec.setCallback(new MediaCodec.Callback() { @Override void onInputBufferAvailable(MediaCodec mc, int id) {// 非阻塞获取输入缓冲区} });
2. 输入方式对比
| 特性 | ByteBuffer 输入 | Surface 输入 |
|---|---|---|
| 内存拷贝 | 需要显式拷贝 YUV 数据 | 通过 GPU 直接写入 |
| 适用场景 | 相机回调等内存数据 | Camera2/OpenGL 输出 |
| 延迟 | 较高(多一次拷贝) | 更低 |
| API 复杂度 | 需手动管理 YUV 格式 | 自动处理色彩空间转换 |
二、核心编码实现
1. MediaFormat 黄金配置
val format = MediaFormat.createVideoFormat("video/avc", width, height).apply {
// 码率计算公式:width * height * fps * motionFactor(0.1-0.3)setInteger(MediaFormat.KEY_BIT_RATE, 1920 * 1080 * 30 * 0.25)
setInteger(MediaFormat.KEY_FRAME_RATE, 30)
setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2) // GOP= 2 秒
setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible)
setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh)
}
2. 带异常处理的编码流程
try {MediaCodec codec = MediaCodec.createEncoderByType("video/avc");
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface inputSurface = codec.createInputSurface();
codec.start();
// 编码循环示例
while (isEncoding) {int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, 10000);
if (outputBufferId >= 0) {ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
// 处理编码后的 H.264 数据
codec.releaseOutputBuffer(outputBufferId, false);
}
}
} catch (IllegalStateException e) {Log.e(TAG, "编解码器状态异常:" + e.message);
// 重置编码器
} finally {codec?.stop()
codec?.release()}
三、硬件加速实战
1. 编码器检测方案
fun isHardwareSupported(mime: String): Boolean {val list = MediaCodecList(MediaCodecList.ALL_CODECS)
return list.codecInfos.any {it.isEncoder && it.name.contains("qcom")
|| it.name.contains("exynos")
&& it.supportedTypes.contains(mime)
}
}
2. 性能优化数据(测试设备:Xiaomi 12)
| 核心数 | 1080P 编码延迟 | 功耗 |
|---|---|---|
| 1 大核 | 42ms | 380mW |
| 4 大核 | 18ms | 620mW |
| 8 全开 | 15ms | 1100mW |
3. GraphicBuffer 零拷贝方案
// 需 API 26+
surface.setDefaultBufferSize(width, height);
surface.lockHardwareCanvas().apply {
// 直接绘制到 Surface
unlockCanvasAndPost(this)
}
四、避坑指南
- 资源释放时序 :
- 先 stop() 再 release()
-
Surface 需在 Codec 释放后销毁
-
色彩格式兼容性 :
- Android 7.0 以下优先使用 COLOR_FormatYUV420SemiPlanar
-
8.0+ 设备支持 COLOR_FormatYUV420Flexible
-
版本差异陷阱 :
- Android 10 修复了 Surface 输入的内存泄漏
- 部分 EMUI 系统需要手动设置 KEY_MAX_B_FRAMES=0
五、未来挑战
随着 H.265 设备覆盖率突破 60%,开发者面临编码效率与兼容性的权衡:
– 相同画质下 H.265 可节省 40% 带宽
– 但华为海思与高通方案存在 Profile 支持差异
– 是否需要实现动态编码策略?如何评估机型白名单的维护成本?这些仍待行业实践给出答案。
通过本文的配置模板和性能数据,开发者可快速构建高性价比的编码方案。建议在实际项目中采用渐进式策略:先确保基础功能稳定,再逐步引入硬件加速优化。
正文完
发表至: 移动开发
四天前
