在这里讲的是Opencore和OMX 的调用sequence,基本来说是根据OMX的API过程来实现的,属于将OMX集成到Opencore的原理 部分
对于omx IL的API,大家可以参考http://omxil.sourceforge.net/docs/modules.html
1 、将一个 CODEC 集成到 PV Opencore multimedia framewok 下的方式主要有三种:作为一个 compressed MIO(media I/O) component ,或者作为个 node ,或者作为一个 Openmax component 集成到 Opencore framework 下的 Openmax codecs nodes 。对于各种codec 尤其是那些包含了 HW acceleratin 的 codec 来说, Openmax IL interface 是最直接的和有效的集成方式,为上层提供统一的接口。
2 、具体 omx component 和 omxcore 的实现在 developer 文档在讨论,在这里只讨论 Opencore 和 omx core/component 的交互作用过程,即 call sequence 。 PVMF 对 omx 的调用大概可以用下面的图来描述:
所有这些过程都是通过 PVMF 来控制的,由 PVMF 发出 command 来控制 Opencore component 的状态转换。
3 、 Opencore 和 OMX core/component 的 interaction 过程分为下面几个过程,所有这些过程都是根据 API 的典型 sequence 来实现的。
3.1 、 PVMF 对 OMX 的调用首先是从对 OMX core 的初始化开始的:
@ PVMF 首先调用 OMX_Init 来初始化 omx core
@ 然后 PVMF 会向 omx core 请求所有他所拥有的 component ,并根据获得的 component name ,来请求有效 component 的 role, 然后对有效的组件及其 role 建立一个 registry 。
OMX_Init() 用来对 omx core 进行初始化,它必须是进入 omx 前调用的第一个函数,而且在调用 deinit 之前只能被调用一次。
OMX_ComponentNameEnum() 实质上就是检测出 system run - time 时所有的有效 component 。
OMX_GetRolesOfComponent() 实际上根据 component name 来取得 component 所支持的 role 的 number 。
具体流程如下:
3.2 、完成对 omx core 的初始化之后就要根据开始获得的 comonent name ,对 omx component 进行初始化,并且列举出相应 component 的 capabilities 以及获得 port 目录。 PVMF 的工作流程如下:
@根据 component name 和 role 使用 OMX_GetHandle 来初始化指定的 omx component 。
@使用所有权 index“PV_OMX_CAPABILITY_TYPE_INDEX” 根据 OMX_GetParameter() 取得 component 的 capabilities ,根据 capabilities 就可以知道 component 是否支持部分祯,是否支持 UseBuffer 或者 AllocateBuffer 等等内容。如果 OMX componentd 对所有权 index 返回“ OMX_ErrorUnsupportedIndex” 或者其他错误信息, PVMF 必须为 component capabilities 指定一组默认的数据 。
@使用 OMX_GetParameter 来获得 Audio/Video component 的有效 port 数[ input/output port ],然后遍历所有的输入输出端口,来获得输入输出端口的索引。
PVMF 对“ PV_OMX_COMPONENT_CAPABILITY_TYPE_INDEX” index 的定义如下:
#define PV_OMX_COMPONENT_CAPABILITY_TYPE_INDEX 0xFF7A347
实际上所谓的 index 包括后面获取 Audio/Video component port 数使用的“ OMX_IndexAudioInit” 和“ OMX_IndexAudioInit” index 都是传到 OMX_GetParameter() 函数的第一个参数。
PVMF 需要从 component 取得的 capabilities 被包裹在下面的 structure 中,需要 component 对这个 structure 进行赋值:
typedef struct PV_OMXComponentCapabilityFlagsType
{
OMX_BOOL ilsOMXComponentMultiThreaded;
OMX_BOOL iOMXComponentSupportsExternalOutputBufferAlloc;
OMX_BOOL iOMXComponentSupportsExtrenalInputBufferAlloc;
OMX_BOOL iOMXComponentSupportsMovableInputBuffers;
OMX_BOOL iOMXComponentSupportsPartialFrames;
OMX_BOOL iOMXComponentUsesNALStartCode;
OMX_BOOL iOMXComponentCanHandleIncompleteFrames;
OMX_BOOL iOMXComponentUsesFullAVCFrames;
}
iIsOMXComponentMultiThread 默认值为 OMX_TRUE, 虽然 OMX component 一般运行在一个独立的线程,但是通过同步调用等方式也可以使其和 PVMF 运行在一个线程。
iOMXComponentSupportExternalOutput/inputBufferAlloc 的默认值为 OMX_TRUE , OMX spec 要求 OMX component 必须支持在外部分配的额外 buffer( 比如 OMX_UseBuffer 调用外部的 buffer) ,如果 Component 不支持这个,它必须将其值设为 OMX_FALSE ,并且和 PVMF 进行协商,从而使 PVMF 使用 OMX_AllocateBuffer 。
IOMXComponentSupportsMovableInputBuffers 这个值默认也是 OMX_TRUE, 他类似于一种动态存储的概念,就是对于 external allocated input buffer ,可以将其的 omx 头部和实际数据进行分离,由 omx 头部的指针来指定实际数据的位置,如果这个指针可变,则 component 的可以访问不同的数据区域。如果 omx component 要求其起 header 总是指向同一个数据区域,则这个值必须设置 成 false 。
iOMXComponentSupportsPartialFrames 这个值的默认值为 OMX_TRUE, 根其字面意义一样,就是 component 是否支持部分桢,当桢 /NAL 的大小大于 buffer 的大小的时候,必须把桢 /NAL 分割成几块, PVMF 会在每个桢的结束位添加“OMX_BUFFERFLAG_ENDOFFRAME”, 从而使桢的组装变得很简单,如果 component 需要由 PVMF 完成对祯 /NAL 的组装并提供给 component 完整的桢 /NAL, 则必须设置这个值为 false ,不支持部分桢会对性能造成很大的影响。
iOMXComponentUsesNALStartCode 这个值默认是 OMX_false ,这个标志位主要影响到 H264 的解码,主要是设计到 H264 NAL 格式的问题,就是说 PVMF 能够重构 H264 的 NAL ,不需要再让 omx component 来解些比特流,如果 OMX H264 component 要求解些比特流,需要提供 0x0001 NAL start codes ,则这个值必须设置为 true 。
iOMXComponentCanHandleIncompleteFrames 这个值默认为 OMX_TRUE ,与对 partial 的支持不同,不完整的桢的是指由于丢包或其他原因导致的桢 /NAL 不完整,即已经被破坏的桢, comopnent 是否能提供恰当的处理,如果 component 不能相应的处理则必须设置这个值为 false 。
iOMXComponentUsesFullAVCFrames 的值默认为 OMX_FALSE ,这个值定义了 PVMF 在使用 AVC 的情况下,以何种模式为 Component 提供数据。在 OMX_FALSE 情况下是采用 NAL mode ,即 PCMF 提供给 comonent 的数据是 NAL 单元,相反的在 OMX_TRUE 情况下采用的是Frame. mode ,在 Frame. mode 情况下 PVMF 必须将 NAL 单元累在一起,然后为 component 提供完整的一桢数据。
OMX_GetHandle() 根据 component name 载入指定的 component 到内存,并根据 component 的方法来为其创建一个 component 的 instance 。
OMX_GetParameter() 这实际是一个宏,用来获得 component 的参数,根据第一个参数 handle pointer 的不同,可能返回值也是不同的。
具体过程如下:
3.3 完成对 omx core 和 omx component 的初始化之后,第三个过程就是对 omx component input/output buffer 的协商。
@首先使用 OMX_GetParameter() 获得 input/output port buffer parameters ,然后对获得的 parameters 进行一个 verify ,最后调用 OMX_SetParameter() 对 input/output port 设置一些 buffer parameters 。
Component input/output port Buffer size 在很大程度上影响着 component 的性能,如果一个 buffer 能载入多个桢 /NAL 就不存在分割的问题, PVMF 在每个 full 桢 /NAL 后面添加一个“ OMX_BUFFERFLAG_ENDOFFRAME” 标记。如果一个 buffer 不够容纳一个桢/NAL ,则不管是在输入还是输出都要把桢 /NAL 分割成多个 buffer ,并在最后一部分上打上一个 end flag 的标记。
看 OMX API 对 OMX_AllocateBuffer 和 OMX_UseBuffer 的定义后觉得,所谓分配 buffer 就是, component 每次都要自己分配一个新的 buffer 和 buffer header ,而使用 buffer ,是指可以使用其他 component 或者 IL client 分配好的 buffer ,但必须自己分配一个相应的 buffer header ,两种方式
具体过程如下:
3.4 完成初始化以及对交换区 buffer 的协商之后, OMX component 就被 loaded 了,载入后的 component 首先要进入 Idle 状态。
@首先 PVMF 会通过 OMX_SendCommand 函数向 OMX component 发送状态改变命令 ,将其状态从 OMX_StateLoaded 变为 OMX_StateIdle 。
@然后使用两种分配 buffer 方式 (use or allocate) 的任一种来为所有的 input/output port 分配 buffer ,分配 buffer 的函数会根据 NumInputBuffer 和 NumOutputBuffer 的值被调用多次 [ 这里有点小疑问,这个 Num 是不是就是 port 的数目? ]
@当 component 完成 state transition 以后,会通过 envenhandle callback 来通知 PVMF ( OMX_EventCmdComplete )。
根据 OMX spec ,所有的标准 OMX component 必须支持 OMX_UseBuffer/Allocate 两种分配 buffer 的方式,如果由于内部限制等原因不支持的话, component 必须与 PVMF 进行协商。
一般来说 INPUT bufferes 一般分配在 component 外部,这样输入数据就可以省去不必要的 copy 操作。
具体过程如下: