MediaCodec启动流程概览:从创建到开始解码
做多媒体开发这么多年,我经常遇到一个问题: MediaCodec启动到底慢在哪? 其实,绝大多数卡顿就藏在从create到第一次拿到输出缓冲区那段路上。这一章我们就把这条路的每个路口摸清楚,后面排查卡顿才有方向感。
1. 整体流程速览
说白了,MediaCodec的启动就是三步走:创建 → 配置 → 开始。但你别小看这三步,每一步内部都藏着不少东西。我习惯把整个流程拆成两个阶段:
- 准备阶段:从创建出对象,到调用
start()返回之前。 - 就绪阶段:从
start()返回,到第一次通过dequeueOutputBuffer()拿到有效帧。
为什么这么分?因为卡顿通常更爱光顾第二个阶段。我在项目中遇到过,有些设备上start()调用本身只花了几毫秒,但之后的内部初始化却悄悄吃掉了30多毫秒。
2. 创建与配置阶段
创建很简单——你传个MIME类型或者编码器名字,系统返回一个实例。注意这里可能抛MediaCodec.CodecException,如果设备不支持该编码格式。
MediaCodecList先查一下支持的解码器,而不是直接硬编码名字。比如有些低端芯片只支持底层的avc,你一上来就指定h265,创建直接挂掉。
配置(configure) 需要你提供一个MediaFormat对象,里面填上分辨率、帧率、比特率等参数。这一步基本没有耗时操作,纯粹是参数校验和内部状态切换。
MediaCodec codec = MediaCodec.createDecoderByType("video/avc");
MediaFormat format = MediaFormat.createVideoFormat("video/avc", 1920, 1080);
format.setInteger(MediaFormat.KEY_BIT_RATE, 8000000);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
codec.configure(format, surface, null, 0); // 输出到surface
configure()传surface(视频解码时)导致黑屏。如果你解码后要直接渲染,第二个参数不能为null。另外,音频解码时传null没问题。
3. 启动阶段内部细节
调了start()之后,MediaCodec才真正开始干活。这一步内部会做几件事:
- 分配输入输出缓冲区——通常每个方向有4~8个Buffer,数量由底层驱动决定。
- 启动解码线程——如果是软件解码,会拉起一个Worker线程;硬件解码则需要和HAL层握手。
- 硬件资源初始化——打开固件、分配DMA内存、设置码流参数。
- 等待硬件就绪——硬件解码器可能需要几十毫秒才能完成内部状态复位。
你会发现,start()方法本身可能很快就返回了(比如只花2ms),但前面那几步里,硬件初始化往往是最耗时的。我见过一台酷派手机上,纯硬件解码的start()从调用到返回用了85ms,光是等待DSP就绪就占了60ms。
| 阶段 | 典型耗时 | 卡顿风险 |
|---|---|---|
| create | < 1ms | 低 |
| configure | < 1ms | 低 |
| start(申请缓冲区) | 1 ~ 5ms | 中 |
| start(硬件初始化) | 5 ~ 80ms | 高 |
| 第一帧输出准备 | 0 ~ 30ms | 中高 |
嗯,这里要注意:start()返回之后,你马上就能调dequeueInputBuffer()往里面扔数据了。但扔进去的数据要经过内部解码管线,最终通过dequeueOutputBuffer()吐出来。这段时间的长短,和你的码流、分辨率、硬件能力都有关系。
4. 从开始到解码第一帧
你想想看,调用start()之后还没完。你需要:
- 通过
dequeueInputBuffer()拿到一个空的输入Buffer。 - 把码流数据塞进去,然后用
queueInputBuffer()提交。 - 循环调用
dequeueOutputBuffer(),直到返回INFO_OUTPUT_FORMAT_CHANGED或BUFFER_FLAG_KEY_FRAME等有效信号。
我第一次做播放器时,以为start()完就能立刻渲染。结果发现要等好几帧数据喂进去,解码器才肯输出第一帧。特别是对于B帧较多的码流,内部可能先缓存几帧才能输出。所以标准的做法是:先喂5~10帧数据,再去检查输出。
好了,现在你对MediaCodec的启动流程心里有谱了吧?后面的章节我们会沿着这些步骤,逐一打点计时,看看到底是哪个环节拖了后腿。