深入浅出安卓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,你就能玩转安卓音视频处理的"核心引擎"! 🎬🔊

相关推荐
androidwork26 分钟前
Kotlin扩展函数提升Android开发效率
android·开发语言·kotlin
居然是阿宋1 小时前
Android SDK 开发中的 AAR 与 JAR 区别详解
android·java·jar
练习本2 小时前
AI大模型驱动的智能座舱研发体系重构
android·人工智能·重构·架构
姜行运2 小时前
C++【继承】
android·开发语言·c++
pq113_62 小时前
OrangePi Zero 3学习笔记(Android篇)1 - 搭建环境
android·orangepi zero 3
志存高远667 小时前
kotlin 扩展函数
android·开发语言·kotlin
小镇敲码人9 小时前
【深入浅出MySQL】之数据类型介绍
android·数据库·mysql
柯南二号10 小时前
Android 实现一个隐私弹窗
android
UzumakiHan12 小时前
flutter权限允许访问
android·flutter
wangz7614 小时前
kotlin、jetpack compose、Android加速度传感器调用
android·kotlin·jetpack compose·加速度传感器