一、介绍
Android底层多媒体模块采用的是OpenMax框架,实现方都要遵循OpenMax标准。Google默认提供了一系列的软编软解的实现,而硬编硬解则由芯片厂商完成,所以不同芯片的手机,硬编硬解的实现和性能是会有差异的。比如我手机的编解码实现部分如下
MediaCodec提供了一套访问底层多媒体模块的接口供应用层实现编解码功能。
二、工作原理和基本流程
MediaCodec使用的基本流程如下:
- createByCodeName/createEncoderByType/createDecoderByType: (静态工厂构造MediaCodec对象)--Uninitialized状态
- configure:(配置) -- configure状态
- start (启动)--进入Running状态
- while(1) {
try{
- dequeueInputBuffer (从编解码器获取输入缓冲区buffer)
- queueInputBuffer (buffer被生成方client填满之后提交给编解码器)
- dequeueOutputBuffer (从编解码器获取输出缓冲区buffer)
- releaseOutputBuffer (消费方client消费之后释放给编解器)
} catch(Error e){
- error (出现异常 进入error状态)
}
}
- stop (编解码完成后,释放codec)
- release
基本流程结合代码两张Buffer队列示意图和生命周期图一起看,整个流程就会很清晰。
下面我们重点看下核心的部分Buffer队列的操作。
MediaCodec采用了2个缓冲区队列(inputBuffer和outputBuffer),异步处理数据,
1. 数据生成方(左侧Client)从input缓冲队列申请empty buffer—》dequeueinputBuffer
2. 数据生产方(左侧Client)把需要编解码的数据copy到empty buffer,然后繁缛到input缓冲队列 —》queueInputBuffer
3. MediaCodec从input缓冲区队列中取一帧进行编解码处理
4. 编解码处理结束后,MediaCodec将原始inputbuffer置为empty后放回左侧的input缓冲队列,将编解码后的数据放入到右侧output缓冲区队列
5. 消费方Client(右侧Client)从output缓冲区队列申请编解码后的buffer —》dequeueOutputBuffer
6. 消费方client(右侧Client)对编解码后的buffer进行渲染或者播放
7. 渲染/播放完成后,消费方Client再将该buffer放回到output缓冲区队列 —》releaseOutputBuffer
三、数据格式
Mediacodec接受三种数据格式和两种载体 分别如下:
压缩数据、原始音频数据和原始视频数据
以Surface作为载体或者ByteBuffer作为载体
-
压缩数据
压缩数据可以作为解码器的输入、编码器的输出,需要指定数据的格式,这样codec才知道如何处理这些压缩数据 -
原始音频数据 — PCM音频数据帧
-
原始视频数据
视频看解码支持的常用的色彩格式有 native raw video format和 flexible YUV buffers
native raw video format : COLOR_FormatSurface,可以用来处理surface模式的数据输入输出。
flexible YUV buffers : 例如COLOR_FormatYUV420Flexible,可以用来处理surface模式的输出输出,在使用ByteBuffer模式的时候可以用getInput/OutputImage(int)方法来获取image数据。
四、生命周期
MediaCodec生命周期状态分为三种 Stopped、Executing和Released
在上面第一部分工作原理和基本流程中我们也简单提到了,下面我们详细说下
其中Stopped包含三种子状态 Uninitialized(为初始化状态)、Configured(已配置状态)、Error(异常状态)
Executing也包含三个子状态 Flushed(刷新状态)、Running(运行状态)和EOS(流结束状态)
我们重点看下Executing状态
在调用mediacodec.start()方法后编解码器立即进入Executing状态的Flush子状态,此时编解码器会拥有所有的inputBuffer
一旦第一个输入缓存inputbuffer被移出队列(即:queueInputBuffer),编解码器转为Running状态,编解码器的大部分生命周期会在此状态下。
当带有end-of-stream标记的inputBuffer入队列时(queueInputBuffer),编解码器将转入EOS状态。在这种状态下,编解码器不再接收新的inputBuffer,但是仍然产生outputBuffer,直到end-of-stream标记到达输出端。
可以在Executiong下的任何时候调用flush()使编解码器重新回到Flushed状态。
关注+后台私信我,领取2022最新最全学习提升资料包+面试题,内容包括(