Android MediaCodec 架构与实现解析

Android MediaCodec 架构与实现解析

文档时间: 2026-03

本文梳理 Android MediaCodec 的代码位置、分层架构、数据流与线程模型,并说明软解/硬解路径、与 FFmpeg 的关系以及核心 C++ 调用链,便于查阅与二次开发。


目录

  1. 一、代码位置与仓库
  2. [二、MediaCodec 架构全景](#二、MediaCodec 架构全景)
  3. [三、Java API 层](#三、Java API 层)
  4. [四、JNI 层](#四、JNI 层)
  5. [五、C++ Native 与编解码器信息层](#五、C++ Native 与编解码器信息层)
  6. 六、设计模式与数据流
  7. [七、MediaCodec 是否使用 FFmpeg](#七、MediaCodec 是否使用 FFmpeg)
  8. [八、软解与硬解是否都走 C++](#八、软解与硬解是否都走 C++)(含内存路径差异、编解码器发现)
  9. 九、总结与延伸阅读

一、代码位置与仓库

MediaCodec 是 Android 媒体框架的核心,代码分布在 platform_frameworks_baseframeworks/av 等仓库。

层级 路径(frameworks_base) 说明
Java API media/java/android/media/MediaCodec.java 应用层调用的主 API(约 6300+ 行)
media/java/android/media/MediaCodecList.java 列出可用编解码器
media/java/android/media/MediaCodecInfo.java 编解码器元数据与能力
JNI media/jni/android_media_MediaCodec.cpp MediaCodec 的 JNI 实现(约 4400+ 行)
media/jni/android_media_MediaCodecList.cpp 编解码器列表的 JNI
C++ 实现 主要在 frameworks/av/media 仓库 libstagefright、Codec2.0、OMX、软件编解码库等

二、MediaCodec 架构全景

MediaCodec 采用四层架构:Java API → JNI → C++ Native → HAL/硬件。

复制代码
┌─────────────────────────────────────┐
│     Java API Layer                  │  应用接口
│  MediaCodec.java                    │
├─────────────────────────────────────┤
│     JNI Layer                       │  跨语言桥接
│  android_media_MediaCodec.cpp       │
├─────────────────────────────────────┤
│     C++ Native Layer                │  核心逻辑与编解码
│  libstagefright, CodecBase, ACodec, C2Codec
├─────────────────────────────────────┤
│     HAL / Hardware Layer            │  硬件抽象
│  OMX IL, Codec2.0 HAL, V4L2, 厂商 VPU/DSP │
└─────────────────────────────────────┘

说明 :部分平台还会通过 V4L2 等接口访问硬件编解码能力;C++ 实现主要在 frameworks/av 仓库(如 libstagefrightcodecs/),与 frameworks_base 的 JNI 配合使用。


三、Java API 层

3.1 生命周期与创建方式

阶段 方法/说明
创建 createDecoderByType() / createEncoderByType() 按 MIME 创建;createByCodecName() 按名称创建;createByCodecNameForClient() 为指定客户端创建
配置 configure(MediaFormat, Surface, MediaCrypto, flags) --- 分辨率、码率、Surface、DRM 等
状态 start()stop() / flush() / reset()release()

状态机:Uninitialized → Configured → Executing;flush 回到 Executing,stop/reset 回到 Uninitialized。下图概括允许的转换关系:

复制代码
                    configure()
  Uninitialized ──────────────────► Configured
       ▲                                  │
       │                                  │ start()
       │ reset() / release()              ▼
       │                            ┌───────────┐
       └────────────────────────────│ Executing │◄──── flush()
                                    └───────────┘
                                          │
                                    stop() / release()

典型调用顺序 :创建(如 createDecoderByType("video/avc"))→ configure(MediaFormat, Surface, ...)start() → 循环 dequeueInputBuffer / queueInputBufferdequeueOutputBuffer / releaseOutputBuffer(或使用 setCallback 异步处理)→ stop() / release()

3.2 数据交换模型

模式 说明
Legacy dequeueInputBuffer() → 填充数据 → queueInputBuffer()dequeueOutputBuffer() → 处理 → releaseOutputBuffer()
Block(现代) 使用 LinearBlock,支持零拷贝与异步;LinearBlock.obtain()queueLinearBlock()
Surface 编码:createInputSurface() 从 Surface 取输入;解码:setOutputSurface() 直接输出到 Surface

3.3 异步回调

MediaCodec.Callback 接口:

  • onInputBufferAvailable(MediaCodec, int index)
  • onOutputBufferAvailable(MediaCodec, int index, BufferInfo info)
  • onOutputFormatChanged(MediaCodec, MediaFormat format)
  • onError(MediaCodec, CodecException e)

内部通过 EventHandler 在指定线程分发事件。使用 setCallback(Callback, Handler) 可指定回调所在线程(不传 Handler 则使用 Codec 内部线程)。

Callback 时序(解码为例)

异步模式下,回调的典型触发顺序与时机如下(解码一路数据时):

复制代码
  start() 之后
       │
       ▼
  onInputBufferAvailable(index)   ← 有可用输入 buffer,可往里填数据
       │
       │  app 调用 queueInputBuffer(index, ...)
       ▼
  (可能多次)onInputBufferAvailable / queueInputBuffer  交替
       │
       │  当有解码结果时
       ▼
  onOutputFormatChanged(format)   ← 首次有输出前或格式变化时(如分辨率变更)
       │
       ▼
  onOutputBufferAvailable(index, BufferInfo)   ← 有一帧解码结果可消费
       │
       │  app 处理完后 releaseOutputBuffer(index, render)
       ▼
  (循环)onOutputBufferAvailable / releaseOutputBuffer
       │
       │  若 queueInputBuffer(..., BUFFER_FLAG_END_OF_STREAM)
       ▼
  最后若干次 onOutputBufferAvailable,其中 BufferInfo.flags 含 EOS
       │
       ▼
  不再有新的 output,解码结束
回调 触发时机
onInputBufferAvailable 有空的输入 buffer 可填充;每次 releaseInputBuffer 或 Codec 内部归还 buffer 后可能触发
onOutputFormatChanged 输出格式确定或发生变化(如首帧解码出、分辨率/色彩格式变更),应在使用输出 buffer 前处理新 format
onOutputBufferAvailable 有一帧解码/编码结果可读,需在合适时机 releaseOutputBuffer(index, render) 释放
onError 编解码错误、配置错误等,Codec 进入错误状态,通常需 release() 后重新创建

注意 :回调与 dequeue* 互斥,使用 Callback 时不要混用同步的 dequeueInputBuffer/dequeueOutputBuffer

3.4 加密与常量

CryptoInfo :封装 DRM 信息(加密模式、子样本、IV、密钥 ID);通过 queueSecureInputBuffer() 提交加密数据。

Buffer 标志与返回值(常用常量速查):

类型 常量 含义
Buffer 标志 BUFFER_FLAG_SYNC_FRAME 1 关键帧
BUFFER_FLAG_CODEC_CONFIG 2 编解码器配置数据
BUFFER_FLAG_END_OF_STREAM 4 流结束
dequeue 返回值 INFO_TRY_AGAIN_LATER -1 暂无可用缓冲
INFO_OUTPUT_FORMAT_CHANGED -2 输出格式改变
INFO_OUTPUT_BUFFERS_CHANGED -3 输出缓冲区改变(旧版 API)

四、JNI 层

JNI 层是 Java 与 C++ 的桥接,负责类型转换、异常映射与 Native 句柄管理。

维度 说明
方法映射 JNINativeMethod 表将 Java 的 native_* 方法映射到 C++ 实现(如 native_configureandroid_media_MediaCodec_native_configure
上下文 Java 侧 mNativeContext 持有 C++ JMediaCodec;通过 getMediaCodec() / setMediaCodec() 存取
类型转换 ByteBuffer ↔ AByteBuffer;Surface ↔ ANativeWindow;MediaFormat ↔ AMessage;CryptoInfo ↔ CodecCryptoInfo
错误 throwExceptionAsNecessary() 将 C++ status_t 转为 Java CodecException / CryptoException
扩展 HardwareBuffer 映射、LinearBlock 零拷贝、Vendor 参数订阅与查询

五、C++ Native 与编解码器信息层

5.1 C++ 核心结构(frameworks/av)

类/组件 说明
MediaCodec 统一编解码管理,状态机与消息驱动
JMediaCodec JNI 适配,供 android_media_MediaCodec 调用
CodecBase 编解码器基类
ACodec OMX IL 实现
C2Codec Codec2.0 实现
MediaCodecBuffer 缓冲区抽象
ALooper / AMessage / AHandler 事件循环与异步消息

5.2 MediaCodecInfo(编解码器元数据)

  • MediaCodecInfo :名称、规范名、标志(编码/解码、软/硬);isEncoder()isSoftwareOnly()isHardwareAccelerated()
  • CodecCapabilities:AudioCapabilities、VideoCapabilities、EncoderCapabilities、CodecProfileLevel 等。
  • FeatureisFeatureSupported(String feature),如 Adaptive Playback、Secure Playback、Tunneled Playback。

六、设计模式与数据流

6.1 设计模式

模式 体现
工厂 createDecoderByType("video/avc")createByCodecName("c2.android.avc.decoder")
策略 不同后端(OMX vs Codec2.0)通过统一接口暴露
观察者 Callback 异步事件通知
桥接 JNI 层连接 Java 与 C++

6.2 解码/编码数据流

解码管线(从输入到输出):

复制代码
  应用层                     JNI                    C++ / HAL
┌─────────┐            ┌─────────────┐         ┌──────────────────┐
│ 输入数据 │──queue────►│ native_*    │────────►│ Codec 解码       │
│ (或 Surface)         │ (ByteBuffer/│         │ (软解/硬解)      │
└─────────┘            │  Surface)   │         └────────┬─────────┘
                       └─────────────┘                    │
                                                          ▼
┌─────────┐            ┌─────────────┐         ┌──────────────────┐
│ Surface │◄──release──│ dequeue     │◄────────│ 解码后 Buffer    │
│ 或 YUV  │   Output   │ OutputBuffer│         │ (或直接到 Surface) │
└─────────┘            └─────────────┘         └──────────────────┘

编码管线 :原始帧(ByteBuffer 或 createInputSurface())→ queueInputBuffer() / Surface → Native Codec → 编码完成 → dequeueOutputBuffer() 取码流。

方向 输入入口 输出出口
解码 queueInputBuffer() 或 Surface(若用 setOutputSurface() dequeueOutputBuffer() → Surface / ByteBuffer
编码 queueInputBuffer()createInputSurface() dequeueOutputBuffer() → 编码数据

6.3 线程模型

线程 职责
应用线程 调用 API
回调线程 执行 Callback(可自定义 Handler)
Native 工作线程 ALooper,处理编解码任务
HAL 线程 硬件抽象层

6.4 性能与扩展

  • 零拷贝:LinearBlock、HardwareBuffer;Surface 直连减少拷贝。
  • 多实例与资源 :通过 getRequiredResources() 查询编解码器所需资源,便于多实例场景下的资源管理。
  • 扩展:Vendor 参数、Crypto 插件、自定义 Codec2 组件。

七、MediaCodec 是否使用 FFmpeg

结论:MediaCodec 不使用 FFmpeg。

7.1 实际实现路径

路径 实现方式 说明
硬件编解码 芯片厂商通过 OMX IL / Codec2.0 HAL 高通、联发科、三星、海思等;直接使用 VPU/DSP/GPU,不依赖 FFmpeg
软件编解码 系统自带 C++ 库 位于 frameworks/av/media/libstagefright/codecs/ 等

7.2 系统软解使用的库

编解码器 说明
H.264/AVC libavc 基于 OpenMAX 等
H.265/HEVC libhevc 系统/开源实现
VP8/VP9 libvpx WebM 项目
AV1 libaom AOMedia
AAC FAAD2 或自研 音频解码
MP3 自研 避免专利与依赖

特点:专注编解码、无封装/网络等,便于集成 Codec2.0 与优化。

7.3 为何不用 FFmpeg

原因 说明
许可 LGPL/GPL,设备厂商常需更宽松策略
架构 FFmpeg 是完整工具链;MediaCodec 只需编解码,且需适配 OMX/Codec2.0
性能与控制 通用优化与厂商硬件路径不同;Google 需完全可控的编解码栈

7.4 若需使用 FFmpeg

  • NDK 自集成:JNI 调用自编 C++,内部使用 FFmpeg API。
  • 封装为 Codec2 组件:将 FFmpeg 封装成 C2 组件,经 MediaCodec 调用(实现复杂)。
  • ExoPlayer + FFmpeg 扩展:多用于格式解析等,而非替代 MediaCodec 编解码。

7.5 查看设备编解码器

java 复制代码
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
    String name = codecInfo.getName();
    boolean isSoftware = codecInfo.isSoftwareOnly();
    boolean isHardware = codecInfo.isHardwareAccelerated();
    // c2.android.* 多为软解,OMX.xxx.* 多为厂商硬解
}

八、软解与硬解是否都走 C++

结论:是。软解和硬解最终都在 C++ 中执行;Java 层仅为 API 包装。

8.1 软件解码调用链

复制代码
Java MediaCodec
    → JNI (android_media_MediaCodec.cpp)
    → MediaCodec (libstagefright)
    → C2Component / OMX 组件(软件)
    → libhevc.so / libvpx.so / libavc.so / libaac.so
    → CPU 执行解码(C++/Assembly)

8.2 硬件解码调用链

复制代码
Java MediaCodec
    → JNI (android_media_MediaCodec.cpp)
    → MediaCodec (libstagefright)
    → ACodec (OMX) 或 C2Codec (Codec2.0)
    → OMX IL HAL / Codec2.0 HAL
    → IPC (Binder/HIDL) → 厂商 HAL (C++)
    → 驱动 → VPU/DSP/专用电路

8.3 对比小结

层级 软件解码 硬件解码
Java MediaCodec.java MediaCodec.java
JNI android_media_MediaCodec.cpp 同上
框架 C++ C2Codec / ACodec C2Codec / ACodec
解码实现 libhevc、libvpx、libavc 等 厂商 HAL
执行单元 CPU VPU/DSP/专用电路
是否 FFmpeg

8.4 为何全是 C++

  • 性能:软解需密集运算,C++/Assembly 才能实时;硬解需直接操作硬件与驱动。
  • 跨平台与集成:C++ 可编译到多架构,厂商易于提供 HAL 实现。
  • 历史与标准:Android 媒体栈与 OpenMAX 均为 C/C++。
  • 安全与隔离:Native 层独立内存与权限控制(如 SELinux)。

8.5 软解与硬解的内存路径差异

路径 流程概要
软解 在 C++ 侧分配内存 → 解码到软件 Buffer(C++)→ 经 JNI 传回 Java(零拷贝或复制)
硬解 分配硬件 Buffer(C++ + 驱动)→ 解码到硬件 Buffer(DMA 等)→ 经 Surface 渲染(零拷贝,通常不经过 Java 层)

硬解路径下,解码结果多在 GPU/VPU 缓冲区内直接用于渲染,减少 CPU 与 Java 层参与。

8.6 编解码器如何被发现

MediaCodecList 通过 native 层枚举系统注册的编解码器:系统软件解码器 (如 c2.android.avc.decoder)与厂商硬件解码器 (如 OMX.qcom.video.decoder.avc)。每个编解码器在底层对应一个 C++ 组件实例,通过 JNI 暴露名称与能力给 Java。


九、总结与延伸阅读

  • 架构:四层(Java API → JNI → C++ Native → HAL/硬件);状态机与 ALooper/AMessage 驱动。
  • 数据:Legacy/Block/Surface 三种模式;Callback 异步;零拷贝与 Surface 直连优化。
  • FFmpeg:官方 MediaCodec 软硬解均不依赖 FFmpeg;若需 FFmpeg 需自行通过 NDK 或 C2 封装集成。
  • 软硬解:均经 JNI 进入 C++,软解走 libhevc/libvpx 等,硬解走 OMX/Codec2 HAL 到厂商 VPU/DSP。

延伸阅读:

  • Android 官方 MediaCodec 文档
  • frameworks/av 下 libstagefright、codecs、OMX、Codec2 源码
  • OpenMAX IL、Codec2.0 规范与 HAL 接口
相关推荐
常利兵2 小时前
Android Compose 指南:Column与LazyColumn的抉择
android
黄林晴2 小时前
Kotlin 2.3.20 发布!解构声明不怕写反了
android·kotlin
阿拉斯攀登2 小时前
第 16 篇 摄像头驱动适配,V4L2 子系统详解
android·驱动开发·rk3568·瑞芯微·rk安卓驱动
卤炖阑尾炎2 小时前
LNMP/LNAMP 架构部署实战:从环境搭建到 Discuz 论坛与动静分离实现
架构·php
lauo2 小时前
从“安全孤岛”到“信任基石”:ibbot智体机灵如何重新定义AI智能体的安全范式
人工智能·安全·智能手机·架构·开源·github
luoganttcc2 小时前
华为 昇腾 架构怎么 解决这个 NCCL 通信问题
华为·架构
zklgin2 小时前
MySQL错误-this is incompatible with sql_mode=only_full_group_by完美解决方案
android·sql·mysql
牢七2 小时前
baijiacms-master 审计实验
android
2501_933329552 小时前
从传统监测到AI主动处置:舆情系统技术架构演进与实践
人工智能·重构·架构