MediaCodec 概述:定义、应用场景与角色

熟悉我的同学都知道,我做了好多年的 Android 多媒体底层优化。今天聊的 MediaCodec,说白了就是个硬件编解码的官方接口。你想想看,手机要拍视频、播视频、做直播,底层谁在干活?不是 CPU,而是 GPU、DSP 这些专用硬件。MediaCodec 就是帮我们跟这些硬件打交道的。

一句话定义:MediaCodec 是 Android 提供的底层音视频编解码 API,用于访问设备上的硬件或软件编解码器。

它到底长什么样?

不说虚的。MediaCodec 本质上是一个状态机,有四个状态:Stopped、Executing、Flushed、EndOfStream。我们平时调用的 createByCodecName()、configure()、start() 就是在不同状态间跳转。

给我印象最深的是实例化阶段——就是 createByCodecName() 和 configure() 这两步。为什么单独拿出来做一节课?因为我在项目里吃过亏:线上视频通话卡顿,查了半天,发现每次建立通话前 MediaCodec 实例化居然要 200ms+。嗯,这个时间对用户来说就是「转圈圈」。

应用场景有哪些?

我按自己接触过的项目总结一下:

  • 直播推流:把摄像头采集的画面用 H.264/H.265 编码,推给 CDN。
  • 视频播放:解码本地或网络的视频文件。
  • 视频编辑:裁剪、拼接、转码,比如抖音的滤镜处理。
  • 实时通信:WebRTC 场景,编码后通过 RTP 传出去。
  • 屏录/投屏:把屏幕内容编码成流。

你留意到没?这些场景里 MediaCodec 实例化都会被频繁调用。比如直播切后台再回来、切换摄像头、分辨率变化——每次都得重新创建编解码器。所以优化实例化阶段的耗时,直接影响用户体验的流畅度。

在 Android 多媒体框架里的角色

我们可以把 Android 多媒体框架想象成三层结构:

层级 组件 我的理解
应用层 MediaCodec, MediaMuxer, MediaExtractor 咱们开发者直接用的 API,封装了底层细节
框架层 Stagefright、NuPlayer、ACodec 状态管理、缓冲池、编解码器路由
硬件抽象层 OMX(OpenMAX IL)、Codec2 跟具体芯片对接,高通、联发科各有一套实现

MediaCodec 就坐在应用层,但它实际干活时是一路往下走。你调 createByCodecName("OMX.qcom.video.encoder.avc"),框架会去 HAL 层找对应的 OMX 组件,加载驱动,分配内存。

我曾经踩过的坑:在低端机上用 MediaCodec 编码 4K 视频,结果创建编解码器耗时超过 1 秒。后来发现是因为 OMX 组件在初始化时申请了大量连续物理内存,碎片多了就慢。这告诉我们:实例化阶段的耗时跟硬件平台、内存状态强相关。

核心知识结构图

下面这张图我把 MediaCodec 实例化涉及的核心节点画出来,方便你理解全局。

MediaCodec 实例化阶段核心知识结构 —— 应用层 —— MediaCodec API —— 框架层 —— ACodec (状态机) MediaCodecService Buffers & Memory —— 硬件抽象层 —— OMX Plugin Codec2 (新架构) Hardware Driver —— 实例化耗时关键节点 —— createByCodecName 组件查找 + 权限检查 configure 格式协商 + 内存分配 start 队列初始化 + 驱动启动 input/output 等 第一个帧处理 注:实例化阶段主要耗时在 createByCodecName 和 configure,后续课程将逐一分解。

为什么实例化阶段值得单独拿出来讲?

我直接给你看个真实数据。某款中端手机,使用软件编解码器时实例化大概 30ms,但用硬件编解码器(OMX)时却要 200-400ms,其中 createByCodecName 占了 80ms,configure 占了 150ms。

关键结论:硬件编解码器的实例化时间远高于软件编解码器,且主要瓶颈在 createByCodecName 和 configure 内部。

你可能会问:为什么硬件编解码器反而慢?原因有很多:

  • 组件查找时需要在 /vendor/lib 下加载动态库,I/O 耗时
  • 驱动需要初始化硬件状态、申请 DMA-BUF 内存
  • 跨进程通信(Binder)也会增加额外延迟

嗯,这些都是后面课程会细讲的内容。这里先建立个整体认知就好。

一个小总结

MediaCodec 实例化阶段就像「开工厂」:先找厂房(createByCodecName),再配置流水线(configure),然后启动机器(start)。我们后面的优化案例,就是解决每一环节的「开机慢」问题。

个人建议:在做多媒体 App 性能分析时,先把 MediaCodec 实例化阶段单独 profiling 一次。习惯用 Trace.beginSection() 打点,抓 systrace 看耗时分布。曾经有个同事调了一个月的编码延迟,结果发现只是每次创建编码器时日志输出太多导致的 I/O 阻塞……