目录
1. 模块介绍
1.1 基本概念
1.2 软件层次
2. 工作流程
2.1 API使用
2.3 控制流
2.3.1.createByType
2.3.2.configure
2.3.3.start
2.3.4.queue/dequeue buffer
2.3.5.stop
2.3.6.release
2.4.数据流
2.4.1.申请
2.4.2.传输
2.4.3.释放
3.参考
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 开发层 |
MediaCodec是在android 4.1(API level 16)时引入的API,可用于在Java层访问硬件多媒体encoder/decoder。MediaCodec使用OpenMAX IL接口来实现。
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总结。
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;
Apk与MediaCodec的交互如下:
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。
本节以H265 Video Decoder为例子,分别讨论MediaCodec API的实现。createByType、configure、start、queue/dequeue buffer、stop、release。
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。
configure()向component调用setParameter配置参数。
注意:紫色API为显示相关操作。
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。
queueBuffer和dequeueBuffer是一个OMX buffer管理的机制。
ACodec通过emptyBuffer()/OnEmptyBufferDone()/fillBuffer()/OnFillBufferDone()来实现input buffer与output buffer的管理,也使component能从input buffer中读取数据,往output buffer中填充数据。
stop()后,client状态变为LoadedState;component的state先设置为OMX_StateIdle,stop结束后再设置为OMX_StateLoaded。stop()是start()的逆操作。
release()卸载相应动态库,并将client状态设置为UninitializedState,是createByType()的逆操作。
以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作为数据传输通道,示意图如下:
内存的申请是在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的大小。
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个函数完成数据传输;
示意图如下:
内存的释放是在ExecutingToIdleState::changeStateIfWeOwnAllBuffers()中完成的,即在变成Idle状态前完成。
1.MemoryDealer调用clear(),释放input buffer;
2.cancelBufferToNativeWindow()将从Native Window中申请的Buffer还回到Native Window,释放output buffer。
1.《OpenMAX_IL_1_2_0_Specification.pdf》
2. Android5.0 Codes