深入浅出安卓MediaCodec

深入浅出安卓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大小:根据分辨率调整

六、实战案例

案例:手机直播推流

  1. 视频流
    • Camera → Surface → MediaCodec(H.264编码) → RTMP打包
  2. 音频流
    • AudioRecord → MediaCodec(AAC编码) → RTMP打包
  3. 合并
    • MediaMuxer合成音视频流

七、MediaCodec高级玩法

  • 视频编辑:解码→处理(滤镜/裁剪)→编码
  • 屏幕录制MediaProjection + VirtualDisplay
  • 硬解播放器 :替代MediaPlayer实现自定义渲染

八、终极口诀

"创建配置要记牢,输入输出两条道

同步异步看场景,Surface直通效率高

格式匹配很重要,用完释放不泄漏

硬编硬解真给力,音视频处理离不了"

掌握MediaCodec,你就能玩转安卓音视频处理的"核心引擎"! 🎬🔊

相关推荐
___波子 Pro Max.1 小时前
Android envsetup与Python venv使用指南
android·python
武帝为此2 小时前
【MySQL 删除数据详解】
android·数据库·mysql
顾林海2 小时前
深度解析HashMap工作原理
android·java·面试
V少年3 小时前
深入浅出DiskLruCache原理
android
鱼洗竹3 小时前
协程的挂起与恢复
android
清风~徐~来4 小时前
【Linux】进程创建、进程终止、进程等待
android·linux·运维
百锦再4 小时前
Android游戏辅助工具开发详解
android·游戏·模拟·识别·辅助·外挂
QING6185 小时前
Kotlin 类型转换与超类 Any 详解
android·kotlin·app
QING6185 小时前
一文带你了解 Kotlin infix 函数的基本用法和使用场景
android·kotlin·app
张风捷特烈5 小时前
平面上的三维空间#04 | 万物之母 - 三角形
android·flutter·canvas