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

相关推荐
星空寻流年6 小时前
设计模式第五章(门面模式)
android·设计模式
fionlsq7 小时前
uniapp集成原生安卓开发的插件
android·uni-app·小组件
风起云涌~10 小时前
【Android】kotlin.flow简介
android·开发语言·kotlin
深海呐10 小时前
Android 协程的基本使用和最简要原理概述
android·协程·android 协程·协程的原理
studyForMokey11 小时前
【Kotlin进阶】泛型的高级特性
android·开发语言·kotlin
袁震11 小时前
Android-kotlin MVVM框架搭建+Retrofit二次封装
android·kotlin·mvvm·retrofit
2501_9160074711 小时前
Java界面开发工具有哪些?常用Java GUI开发工具推荐、实战经验与对比分享
android·java·开发语言·ios·小程序·uni-app·iphone
铭哥的编程日记12 小时前
《Linux 基础 IO 完全指南:从文件描述符到缓冲区》
android·linux·运维
come1123413 小时前
Go 语言中的结构体
android·开发语言·golang