深入浅出安卓MediaCodec
一、MediaCodec是啥?
MediaCodec就像视频工厂的生产线:
- 原材料:原始视频/音频数据(H.264、AAC等)
- 生产线:编码(打包)或解码(拆包)
- 产品:处理后的数据(如MP4文件或播放画面)
二、核心概念大白话
1. 编解码器类型
类型 | 作用 | 例子 |
---|---|---|
视频编码 | 把摄像头数据压缩成H.264 | 录像保存 |
视频解码 | 把H.264解压成可播放画面 | 视频播放 |
音频编码 | 把麦克风数据转成AAC | 录音 |
音频解码 | 把AAC还原成声音波形 | 音乐播放 |
2. 数据处理流程
graph LR
A[输入数据] --> B[MediaCodec]
B --> C{编码/解码}
C --> D[输出数据]
三、MediaCodec工作五步走
第一步:创建编解码器
java
// 创建H.264解码器
MediaCodec decoder = MediaCodec.createDecoderByType("video/avc");
// 创建AAC编码器
MediaCodec encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
第二步:配置参数
java
// 视频解码配置(以H.264为例)
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);
decoder.configure(format, surface, null, 0); // surface用于渲染
第三步:启动生产线
java
decoder.start();
encoder.start();
**第四步:喂数据 & 取结果
java
// 输入数据(生产者)
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
int inputIndex = decoder.dequeueInputBuffer(timeout);
if (inputIndex >= 0) {
ByteBuffer buffer = inputBuffers[inputIndex];
buffer.put(rawData); // 填入原始数据
decoder.queueInputBuffer(inputIndex, 0, rawData.length, timestamp, 0);
}
// 获取结果(消费者)
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int outputIndex = decoder.dequeueOutputBuffer(info, timeout);
if (outputIndex >= 0) {
ByteBuffer buffer = outputBuffers[outputIndex];
// 处理解码后的数据...
decoder.releaseOutputBuffer(outputIndex, true); // true表示渲染到surface
}
第五步:收尾工作
java
decoder.stop();
decoder.release(); // 必须释放!
四、关键技巧
1. 异步模式(推荐)
java
// 设置回调
decoder.setCallback(new MediaCodec.Callback() {
@Override
void onInputBufferAvailable(MediaCodec mc, int index) {
// 填充数据...
}
@Override
void onOutputBufferAvailable(...) {
// 获取解码数据...
}
});
2. Surface直接输入/输出
java
// 从Camera直接输入
Surface inputSurface = encoder.createInputSurface();
camera.setPreviewSurface(inputSurface);
// 直接输出到SurfaceView
Surface outputSurface = surfaceView.getHolder().getSurface();
decoder.configure(format, outputSurface, null, 0);
3. 码率控制模式
java
format.setInteger(MediaFormat.KEY_BITRATE_MODE,
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR); // 可变码率
五、避坑指南
1. 常见崩溃原因
- 未释放资源:每次create后必须release
- 格式不匹配:输入数据必须符合配置格式
- 线程阻塞:同步模式下别在主线程操作
2. 兼容性问题
java
// 检查设备支持的编解码器
MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
MediaCodecInfo[] infos = list.getCodecInfos();
for (MediaCodecInfo info : infos) {
if (info.isEncoder()) { // 是否是编码器
String[] types = info.getSupportedTypes();
// ...
}
}
3. 性能优化
- 使用硬件加速 :优先选择
OMX.qcom
/OMX.exynos
开头的编解码器 - 批处理帧:减少频繁调用queue/dequeue
- 合理设置Buffer大小:根据分辨率调整
六、实战案例
案例:手机直播推流
- 视频流 :
- Camera → Surface → MediaCodec(H.264编码) → RTMP打包
- 音频流 :
- AudioRecord → MediaCodec(AAC编码) → RTMP打包
- 合并 :
- 用
MediaMuxer
合成音视频流
- 用
七、MediaCodec高级玩法
- 视频编辑:解码→处理(滤镜/裁剪)→编码
- 屏幕录制 :
MediaProjection
+VirtualDisplay
- 硬解播放器 :替代
MediaPlayer
实现自定义渲染
八、终极口诀
"创建配置要记牢,输入输出两条道
同步异步看场景,Surface直通效率高
格式匹配很重要,用完释放不泄漏
硬编硬解真给力,音视频处理离不了"
掌握MediaCodec,你就能玩转安卓音视频处理的"核心引擎"! 🎬🔊