Android MediaCodec 音视频编解码技术详解
一、MediaCodec 简介
MediaCodec 是 Android 平台提供的底层音视频编解码 API,自 API 16 (Android 4.1) 引入,用于访问设备硬件加速的编解码器。作为 Android 多媒体框架的核心组件,它通常与 MediaExtractor(媒体提取)、MediaMuxer(媒体封装)、Surface(视频渲染)等组件配合使用,实现高效的音视频处理。其核心优势在于直接调用硬件编码器/解码器,相比纯软件实现(如 FFmpeg)具有更低功耗和更低延迟的特点,适用于实时通信、直播推流、视频编辑等场景。
二、核心概念与架构
2.1 状态机模型
MediaCodec 生命周期分为三种状态,状态转换需严格遵循下图逻辑:
┌───────────── Stopped 状态 ───────────┐
│ ┌─────────┐ ┌─────────┐ ┌──────┐ │
│ │未初始化 │→│已配置 │→│错误 │ │
│ └─────────┘ └────┬────┘ └──────┘ │
└────────────────────┼──────────────────┘↓
┌───────────── Executing 状态 ───────────┐
│ ┌─────────┐ ┌─────────┐ ┌────────┐ │
│ │刷新中 │→│运行中 │→│流结束 │ │
│ └─────────┘ └─────────┘ └────────┘ │
└────────────────────┬──────────────────┘↓┌───────────┐│ Released │└───────────┘
- Stopped:初始状态,包含 Uninitialized(创建后)、Configured(
configure()
后)、Error(异常时)子状态。 - Executing:调用
start()
后进入,包含 Flushed(初始)、Running(处理数据)、End-of-Stream(输入结束)子状态。 - Released:调用
release()
后释放资源,不可再使用。
2.2 缓冲区队列模型
MediaCodec 采用 生产者-消费者模型,通过两组缓冲区队列实现数据流转:
- 输入缓冲区队列:接收原始数据(如 YUV 视频帧、PCM 音频),由开发者填充后提交给编解码器。
- 输出缓冲区队列:返回编解码后的数据(如 H.264 码流、AAC 音频),由开发者提取并处理。
核心流程:
开发者 → 申请输入缓冲区 → 填充数据 → 提交输入缓冲区 → 编解码器处理
编解码器 → 输出缓冲区就绪 → 开发者提取数据 → 释放输出缓冲区 → 重复
2.3 数据类型
MediaCodec 支持三类数据处理:
- 压缩数据:如 H.264 视频流(解码器输入/编码器输出)。
- 原始视频数据:通常为 YUV 格式(如 Camera 采集的 NV21),需注意颜色格式兼容性。
- 原始音频数据:通常为 PCM 格式(如 AudioRecord 采集的 16 位有符号整数)。
三、编解码工作流程
以视频编码为例,完整流程如下:
3.1 创建与配置
// 1. 创建 MediaFormat,指定编码格式、分辨率等参数
MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, // H.264 编码1280, 720 // 分辨率
);
format.setInteger(MediaFormat.KEY_BIT_RATE, 4_000_000); // 码率 4Mbps
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30); // 帧率 30fps
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); // I帧间隔 5秒
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // Surface输入// 2. 创建编码器实例
MediaCodec encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); // 编码模式
3.2 启动与缓冲区操作
// 3. 启动编码器
Surface inputSurface = encoder.createInputSurface(); // 获取输入Surface(摄像头数据直接接入)
encoder.start();// 4. 循环处理缓冲区(同步模式)
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
boolean isEncoding = true;while (isEncoding) {// 4.1 处理输入缓冲区int inputBufferIndex = encoder.dequeueInputBuffer(10000); // 超时10msif (inputBufferIndex >= 0) {ByteBuffer inputBuffer = encoder.getInputBuffer(inputBufferIndex);// 填充数据(如从Camera获取的YUV数据)inputBuffer.put(yuvData);encoder.queueInputBuffer(inputBufferIndex, 0, yuvData.length, System.nanoTime() / 1000, // 时间戳(微秒)0 // 无标志);}// 4.2 处理输出缓冲区int outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 10000);if (outputBufferIndex >= 0) {ByteBuffer outputBuffer = encoder.getOutputBuffer(outputBufferIndex);// 提取编码后的数据(如H.264 NALU)byte[] encodedData = new byte[bufferInfo.size];outputBuffer.get(encodedData);// 写入文件或网络推流muxer.writeSampleData(videoTrackIndex, outputBuffer, bufferInfo);// 释放缓冲区encoder.releaseOutputBuffer(outputBufferIndex, false);} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {// 输出格式变化,通常在第一帧数据前触发MediaFormat newFormat = encoder.getOutputFormat();videoTrackIndex = muxer.addTrack(newFormat);muxer.start();}
}
3.3 停止与资源释放
// 5. 停止编码
encoder.stop();
encoder.release();
muxer.stop();
muxer.release();
四、关键技术与最佳实践
4.1 颜色格式兼容性
问题:Camera 采集的 YUV 格式(如 NV21)与 MediaCodec 支持的颜色格式可能不匹配,导致花屏或变色。
解决方案:
- 查询设备支持的颜色格式:
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType); for (int format : capabilities.colorFormats) {Log.d("SupportedFormat", format + ""); // 如 COLOR_FormatYUV420Flexible }
- 使用 COLOR_FormatYUV420Flexible(Android 6.0+),兼容大多数设备。
- 必要时通过 libyuv 库转换格式(如 NV21→I420)。
4.2 低延迟优化
针对实时场景(如视频通话),可通过以下方式将延迟降至 20ms 以内:
- 启用低延迟模式(Android 11+):
format.setInteger(MediaFormat.KEY_LOW_LATENCY, 1); // 低延迟标记
- 减少缓冲区大小:避免使用默认缓冲区,通过
KEY_MAX_INPUT_SIZE
限制输入大小。 - 禁用 B 帧:B 帧虽提升压缩率,但增加延迟,实时场景建议关闭。
- 异步回调模式:API 21+ 支持
setCallback()
,避免同步等待阻塞线程:encoder.setCallback(new MediaCodec.Callback() {@Overridepublic void onInputBufferAvailable(MediaCodec codec, int index) { ... }@Overridepublic void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) { ... } });
4.3 错误处理与兼容性
常见问题 | 解决方案 |
---|---|
配置崩溃(configure() 失败) | 检查分辨率是否超过设备能力(如低端机不支持4K),或同时解码多路流导致资源耗尽。 |
解码花屏 | 确保从关键帧开始解码,避免丢帧导致参考帧缺失。 |
音频不同步 | 精确设置时间戳(presentationTimeUs ),避免累积误差。 |
设备兼容性差异 | 使用 MediaCodecList 查询设备支持的编解码器, fallback 至软编解码。 |
五、MediaCodec 与 MediaCodec2 对比
Android Q (API 29) 引入 MediaCodec2,作为 MediaCodec 的替代方案,解决碎片化问题并提升性能:
特性 | MediaCodec | MediaCodec2 (Android Q+) |
---|---|---|
架构 | ACodec + OpenMAX IL | 基于 C2 组件,支持零拷贝和组件链 |
更新方式 | 厂商定制,碎片化严重 | Mainline 模块,Google 统一维护 |
性能 | 依赖厂商实现,效率参差不齐 | 优化缓冲区管理,支持硬件加速渲染 |
兼容性 | 需适配不同厂商实现 | 标准化接口,减少设备差异 |
迁移建议:新应用优先使用 MediaCodec2,通过 MediaCodecList
动态选择编解码器:
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
String codecName = list.findEncoderForFormat(format);
MediaCodec codec = MediaCodec.createByCodecName(codecName);
六、支持格式与设备查询
6.1 主流编解码格式
类型 | 格式 | MIME 类型 | 支持版本 |
---|---|---|---|
视频 | H.264 (AVC) | video/avc | Android 4.1+ |
视频 | H.265 (HEVC) | video/hevc | Android 5.0+ |
视频 | VP9 | video/x-vnd.on2.vp9 | Android 4.4+ |
视频 | AV1 | video/av01 | Android 14+ |
音频 | AAC | audio/mp4a-latm | Android 4.1+ |
音频 | Opus | audio/opus | Android 5.0+ |
6.2 查询设备编解码能力
通过 MediaCodecInfo
获取设备支持的详细能力:
for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);if (!info.isEncoder()) continue; // 筛选编码器for (String type : info.getSupportedTypes()) {if (type.startsWith("video/")) {Log.d("Codec", info.getName() + " supports " + type);}}
}
七、应用场景与案例
- 实时视频通话:结合 Camera2 采集和 MediaCodec 硬编,实现低延迟推流。
- 短视频编辑:使用 MediaCodec 解码原始视频,叠加滤镜后重新编码。
- 直播推流:将编码后的 H.264/AAC 数据通过 RTMP 协议推送到服务器。
- 离线转码:利用 MediaMuxer 将多轨道音视频封装为 MP4 文件。
八、总结
MediaCodec 作为 Android 音视频开发的核心 API,掌握其状态管理、缓冲区操作和兼容性处理是实现高效编解码的关键。随着 MediaCodec2 的普及,未来 Android 音视频生态将更加标准化,开发者可专注于业务逻辑而非设备适配。建议结合官方示例(如 Grafika)深入实践,同时关注 Android 新版本对 AV1、HDR 等格式的支持,打造更优质的多媒体体验。
参考资料:
- Android MediaCodec 官方文档
- Android 音视频开发指南
- MediaCodec 状态机与缓冲区管理