目录
1. 模块介绍
1.1 基本概念
OpenMAX是Khronos Group提出的标准,目标在于创造一个统一的接口,加速大量多媒体资料的处理。OpenMAX分成三层:应用层(Application Layer, AL), 整合层(Integration Layer, IL) 以及开发层(Development Layer, DL)。
OpenMAX (Open Media Acceleration) | |
OpenMAX AL | Application Layer 应用层 |
OpenMAX IL | Integration Layer 整合层 |
OpenMAX DL | Development Layer 开发层 |
- OpenMAX AL是多媒体应用程式(如Media Player)和平台媒体框架之间的接口;
- OpenMAX IL是多媒体框架(如MediaCodec)和多媒体元件之间接口;
- OpenMAX DL是实体硬件(如CPU)和驱动之间的接口;
MediaCodec是在android 4.1(API level 16)时引入的API,可用于在Java层访问硬件多媒体encoder/decoder。MediaCodec使用OpenMAX IL接口来实现。
1.2 软件层次
Android MediaCodec和OpenMAX IL的对应关系如下:
Android | OpenMAX IL |
APK | |
MediaCodec ( JAVA/JNI/Native ) | |
ACodec | IL client |
libstagefrighthw | IL plugin |
libOmxCore | IL core |
libOmxVdec/Venc | IL component |
其中:
MediaCodec分为3部分:Java、JNI和Native。Java是上层apk使用的接口;JNI是Java访问Native的JNI方法;Nat ive是ACodec的wrapper;
ACodec是OpenMAX IL中的OpenMAX IL client;
Libstagefrighthw是厂商的OpenMAX IL plugin;
libOMXCore是OpenMAX IL中的core;
libOMXVdec和libOMXVenc是OpenMAX IL中的component。
1.3 源码结构
android/frameworks/base/media/java/android/media/
Api层(MediaCodec.java)
android/frameworks/base/core/jni/
JNI层(android_media_MediaCodec.cpp)
android/frameworks/av/media/libstagefright/
Native层(MediaCodec.cpp)
android/hardware/qcom/media/libstagefrighthw/
libstagefrighthw实现
android/hardware/qcom/media/mm-core/
libOMXCore实现
android/hardware/qcom/media/mm-video-v4l2/vidc/
libOMXVdec/libOMXVenc实现
注意:本文基于Android5.0 API总结。
2. 工作流程
2.1 API使用
ediaCodec API的使用demo如下:
MediaCodec codec = MediaCodec.createByType(type);
codec.configure(format, ...);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;) {
int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferIndex >= 0) {
// fill inputBuffers[inputBufferIndex] with valid data
...
codec.queueInputBuffer(inputBufferIndex, ...);
}
int outputBufferIndex = codec.dequeueOutputBuffer(timeoutUs);
if (outputBufferIndex >= 0) {
// outputBuffer is ready to be processed or rendered.
...
codec.releaseOutputBuffer(outputBufferIndex, ...);
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
MediaFormat format = codec.getOutputFormat();
...
}
}
codec.stop();
codec.release();
codec = null;
- MediaCodec.configure()和MediaCodec.start()初始化;
- MediaCodec.getInputBuffers()和MediaCodec.getOutputBuffers()取出MediaCodec的input/output buffer;
- MediaCodec.dequeueInputBuffer()和MediaCodec.queueInputBuffer()用于将码流传进MediaCodec;
- MediaCodec.dequeueOutputBuffer()和MediaCodec.releaseOutputBuffer()用于取得MediaCodec传出来的frame buffer;
- MediaCodec.stop()和MediaCodec.release()结束;
Apk与MediaCodec的交互如下:
2.2 状态
ACodec中定义的状态机如下:
OpenMAX IL中定义的状态机如下:
Acodec和OpenMAX IL状态机不尽相同:少了WaitForResources State和Paused State,多了Flushing State,简述如下:
Uninitialized:最开始的状态,未初始化;
Loaded:打开了相应的component;
Idle:申请了in port和out port的input buffer和output buffer;
Executing:开始读入input buffer,写到output buffer,即component开始处理数据;
Flushing:清空input buffer和output buffer。
2.3 控制流
本节以H265 Video Decoder为例子,分别讨论MediaCodec API的实现。createByType、configure、start、queue/dequeue buffer、stop、release。
2.3.1.createByType
createByType根据type="video/hevc",(1)选择相应的decoder component name="OMX.xxx. video.hevc.decoder",(2)将动态库libstagefright.so/libOmxCore.so/libOmxVdecHevc.so加载,OMX API映射建立。ACodec state从UninitializedState改成LoadedState。Component state从OMX_StateInvalid改成OMX_StateLoaded。
2.3.2.configure
configure()向component调用setParameter配置参数。
注意:紫色API为显示相关操作。
2.3.3.start
start()时,client将component状态设置为OMX_StateIdle,然后在本地申请出input buffer,NativeWindow dequeue出output buffer。Buffer申请完成后,client将component状态设置为OMX_StateExecuting。使用OMX->useBuffer()将这些buffer传给component,再使用useGraphicBuffer ()将output buffer的使用权交给component。最后client状态设置为ExecutingState。
2.3.4.queue/dequeue buffer
queueBuffer和dequeueBuffer是一个OMX buffer管理的机制。
- emptyBuffer:Client通过调用此函数传递input Buffer给Component,让其读取其中的数据进行编解码等处理。此函数会调用OMX标准接口OMX_ EmptyThisBuffer ()。
- fillBuffer:Client通过调用此函数传递空的output Buffer给Component,让其将处理好的数据填入其中。此函数会调用OMX标准接口OMX_FillThisBuffer()。
- OnFillBufferDone:Component完成相应处理将输出数据填入output Buffer后,调用此回调函数,向Client发送FillBufferDone消息。
- OnEmptyBufferDone:Component完成对input buffer的读取后,调用此回调函数,向Client发送EmptyBufferDone消息。
ACodec通过emptyBuffer()/OnEmptyBufferDone()/fillBuffer()/OnFillBufferDone()来实现input buffer与output buffer的管理,也使component能从input buffer中读取数据,往output buffer中填充数据。
2.3.5.stop
stop()后,client状态变为LoadedState;component的state先设置为OMX_StateIdle,stop结束后再设置为OMX_StateLoaded。stop()是start()的逆操作。
2.3.6.release
release()卸载相应动态库,并将client状态设置为UninitializedState,是createByType()的逆操作。
2.4.数据流
以hevc/video decoder为例子,OMX component Name="OMX.xxx. video.hevc.decoder" ,对应libOmxVdecHevc。将input buffer的H265裸码流bit stream,解码为output buffer的YUV 数据frame buffer,component本身包含input port和output port作为数据传输通道,示意图如下:
2.4.1.申请
内存的申请是在ACodec中完成,即在OMX IL的client中完成,具体是在start()函数中完成的。
Input:
1.使用getParameter(OMX_IndexParamPortDefinition, input)取得nBufferCountActual和nBufferSize。
2.在ACodec中使用MemoryDealer申请出nBufferCountActual个InputBuffer共享内存;
Output:
如果支持StoreMetaDataInOutputBuffers,只申请出MetaDataBuffer,否则向NativeWindow申请出frame buffer内存。
1.使用getParameter(OMX_IndexParamPortDefinition, output)取得nBufferCountActual和nBufferSize;
2.使用mNativeWindow->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS)取得window需要的最少buffer数。
3.使用native_window_dequeue_buffer_and_wait()申请出最少buffer数目 + 1个frame buffer,native_window_set_buffers_geometry()配置frame buffer的大小。
2.4.2.传输
1.申请出buffer后,ACodec会将所有output buffer使用FillThisBuffer()传到component的output port;
2.在apk填充完input buffer调用MediaCodec.queueInputBuffer()时,ACodec会将input buffer通过EmptyThisBuffer()传到input port;
3.component读取完input buffer中的数据后,调用EmptyBufferDone()将input buffer使用权交回ACodec;
4.component完成一帧数据的处理后填充output buffer,填充完成后调用FillBufferDone()将output buffer使用权交回ACodec;
5.这样通过这4个函数完成数据传输;
示意图如下:
2.4.3.释放
内存的释放是在ExecutingToIdleState::changeStateIfWeOwnAllBuffers()中完成的,即在变成Idle状态前完成。
1.MemoryDealer调用clear(),释放input buffer;
2.cancelBufferToNativeWindow()将从Native Window中申请的Buffer还回到Native Window,释放output buffer。
3.参考
1.《OpenMAX_IL_1_2_0_Specification.pdf》
2. Android5.0 Codes