参考自:https://blog.csdn.net/blue_xx/article/details/46778133
1. 导读
该篇文章对OpenMAX的数据结构和接口进行概要描述,包括OpenMAX的一些官方定义的头文件介绍,以及各种结构体数据介绍:比如OpenMAX组件结构体描述、PORT端口结构体描述。并对组件内部线程的大概结构以及组织方式进行介绍,本文章的目标是可以实现一个有基本功能的组件。
该文章中提到的IL Client可以看作是组件的使用者,负责组件的创建、销毁、命令控制等等。注意本文章需要结合例程代码去看,否则会觉得本文不知所云。代码参考OpenMAX/IL: OMX IL 学习笔记【2】- 组件一文中的OpenMAX IL sample下载链接
2. 头文件
OpenMAX官方提供了一系列的头文件,里面定义了包括组件结构体、音视频相关结构体、命令控制API等等。这些定义可以使得Client控制音频(audio)、视频(video)、图像(image)等类型的多媒体组件。其它(other)域则提供了一些额外的组件功能,比如音视频同步(可以用clock实现)。
文件 | 描述 |
---|---|
OMX_Types.h | 数据类型,里面定义了组件类型、输入输出等 |
OMX_Core.h | IL层的核心API,有命令枚举、状态枚举、组件注册/初始化等等 |
OMX_Component.h | 组件相关的API,有端口类型与定义、组件回调函数成员定义等 |
OMX_Audio.h | 与音频相关的常量和结构体定义 |
OMX_IVCommon.h | 图像与视频通用的一些常量和结构体定义 |
OMX_Video.h | 视频相关的常量和结构体定义 |
OMX_VideoExt.h | 视频相关的常量和数据结构,是对OMX_Video.h的补充扩展 |
OMX_Image.h | 图像相关的常量和结构体定义 |
OMX_Other.h | 其它部分的结构体定义 (包含A/V同步) |
OMX_Index.h | OpenMAX定义的数据结构索引 |
OMX_IndexExt.h | OpenMax 定义的数据结构扩展索引 |
OMX_ContentPipe.h | 内容的管道定义 |
值得注意的是,在OMX_Types.h文件里面有这么几个定义:OMX_IN
、OMX_OUT
、OMX_INOUT
,分别代表了仅输入、仅输出、输入输出。这些宏定义并没有对应的值,只是个空的定义,常用于标识函数的参数传递方向,便于函数调用者(一般是IL Client)搞清楚哪些参数是输入的,哪些参数是输出的,避免一些低级的编程错误。这种形式的用法在其它的编程当中也可以借鉴,有了标识的函数参数会使得函数调用者更加地省心,避免在函数使用的时候出现一些不必要的错误。
提示:OpenMax标准只有头文件,没有标准的库,设置没有定义函数接口。对于实现者,需要实现的主要是包含函数指针的结构体。
3. 数据结构
3.1 数据类型(OMX_Core.h)
主要是一些枚举变量结构体原型,其类型主要如下所示:
可以看出来,其主要分为以下类型:
-
OMX_BUFFERSUPPLIERTYPE:用于组件内部端口数据流标识。
-
OMX_COMMANDTYPE:命令相关的枚举,用于Client对组件的命令控制。
-
OMX_STATETYPE:组件状态相关的枚举,用于组件状态标识。
-
OMX_ERRORTYPE:错误相关的枚举,类似于C库的错误状态集,用于标识组件内部或者Client的出错状态。
-
OMX_EVENTTYPE:事件相关的枚举,用于组件内部向Client发送事件通知。
3.2 OMX_COMMANDTYPE
枚举成员 | 意义 | 参数 |
---|---|---|
OMX_CommandStateSet | 改变组件的状态 | OMX_STATETYPE-要转换的状态 |
OMX_CommandFlush | 冲洗组件内部某个端口的buffer队列 | OMX_U32-端口号 |
OMX_CommandPortDisable | 禁止使能组件内部的某个端口 | OMX_U32-端口号 |
OMX_CommandPortEnable | 使能组件内部某个端口 | OMX_U32-端口号 |
OMX_CommandMarkBuffer | 标记一个buffer并指定哪个组件会触发收到的事件标记 | OMX_U32-端口号(还有命令数据OMX_MARKTYPE*-buffer与组件) |
对于Client来说,命令的发送是非阻塞的,当Client把命令发送给组件时,组件会把收到的命令放到一个命令队列(或管道)里面,然后函数就会立刻返回了。当组件内部取出某个命令并执行完毕之后会进行一个事件回调来通知Client某个命令已经执行完毕,并返回命令执行的状态(失败/成功)。
根据OMX标准,定义了如下所示的组件状态集,并且它们之间还有如下的状态转换条件:
对于这几种状态的说明如下:
-
OMX_StateLoaded:组件已经被加载,但是还没有分配相关的资源。
-
OMX_StateIdle:组件已经分配了相关的全部资源,但是没有传输buffer,也不处理buffer。
-
OMX_StateExecuting:组件开始传输buffer,并且处理buffer(如果buffer可用的话)。
-
OMX_StatePause:组件暂停buffer处理(有可能从暂停的地方恢复数据的处理),该状态没有定义数据是否暂停传输,所以可以依照具体的组件实现来定,可以传输也可以不传输。
-
OMX_StateWaitForResources:组件正等待资源可用(此时资源还未被分配)。
-
OMX_StateInvalid:组件发生了不可修复的错误,此时需要转入该状态。
举例说明下传输buffer、处理buffer、拥有资源三者的区别:
-
拥有资源:buffer的缓存(内存空间),比如视频数据,在进行数据传输处理之前需要分配相应数量的buffer内存空间用来存放传输的数据,当相应的内存被分配之后,就说该组件拥有资源(has resources)。
-
传输buffer:组件之间开始传递buffer,比如将buffer从解码组件传输到显示组件,完成数据的组件间转移,称之为传输buffer。
-
处理buffer:这是对单一组件内部来说的,比如编码组件,它需要接收buffer,然后对接收到的buffer进行编码,这个编码的过程称之为处理buffer,解码组件可同理推之。
当然,在实际的编程实现当中在组件内部也不必拘泥于这几种状态,比如可以新增一些状态或者去掉一些状态,比如很大可能OMX_StateWaitForResources状态就用不到,因为资源分配总是可以在Loaded或者Idle状态中分配完成,而这两个状态中间没必要增加OMX_StateWaitForResources这样一个状态。另外对于各个状态下的数据处理也可以灵活运用,比如OMX_StatePause状态下就可以选择传输或者不传输buffer,如果传输的话就需要把buffer缓存起来,等待组件恢复运行时继续处理(可能会发生缓冲区满的情况,需要做相应的措施)。
3.3 OMX_ERRORTYPE
OMX所有的API返回错误值都在该枚举类型里面定义,该枚举类型里面包含了绝大多数的错误状态类型(实际应用当中基本够用)。当然,如果觉得还是需要自己定义错误值的话也可以,从0x90000000到0x9000FFFF这个范围内就是自定义错误的枚举值范围。
举例部分错误值的意义(错误值定义比较多),其余的在头文件里面可以看到相关错误值的详细意义注释:
-
OMX_ErrorNone:没有错误,正确返回。
-
OMX_ErrorInvalidComponent:组件没有初始化函数或者初始化函数调用返回出错。
-
OMX_ErrorUnderflow:buffer为空。
3.4 OMX_EVENTTYPE
组件通过回调机制向IL Client发送事件消息,事件消息的类型如下:
-
OMX_EventCmdComplete:组件命令执行完毕,每一个来自Client的命令被执行完毕之后,组件都会向Client返回一个事件,以此表明命令的执行结果。注意:OMX_StateInvalid状态不会产生此事件消息。
-
OMX_EventError:组件发生了一个错误。错误值会作为一个参数返回给Client,错误值就是错误枚举类型里面的成员。
-
OMX_EventMark:一个buffer标记以及到达指定的组件,此时组件会返回给Client一个此事件消息,并且Client还会通过私有的结构体指针成员获取到相关的数据信息。
-
OMX_EventPortSettingsChanged:组件改变端口的设置。比如编码组件在运行的过程中发现要编码的数据流宽高发生了变化,此时就会改变端口的宽高设置。
-
OMX_EventBufferFlag:检测到bit流结束时由组件内部产生。当输出端口检测到buffer的nFlags被设置为
OMX_BUFFERFLAG_EOS
,在返回的参数中,nData1指定输出端口的portindex,nData2则是传递nFlags给Client。如果组件是sink类型的(数据流终点),则nData1传递的是输入端口的portindex。 -
OMX_EventResourcesAcquired:组件已经接收到了相关资源(资源分配),此时状态正从OMX_StateWaitForResources转换为OMX_StateIdle state。
3.5 OMX_BUFFERSUPPLIERTYPE
用来指明tunnel(建立隧道链接)的port中哪个是数据提供者(buffer supplier port),buffer提供者port可能有属于自己的独立分配的buffer内存空间,也可能与同一组件内部其它的port共享同一个buffer内存空间,由此可见该枚举类型用于隧道链接的建立设置(tunnel setup)。该枚举类型成员有如下几个:
-
OMX_BufferSupplyUnspecified:未指定buffer提供者端口,或者根本就没有(不需要)buffer提供者。
-
OMX_BufferSupplyInput:输入端口充当buffer提供者。
-
OMX_BufferSupplyOutput:输出端口充当buffer提供者。
4. 组件实例相关结构体
4.1 OMX_COMPONENTREGISTERTYPE
原型:
typedef struct OMX_COMPONENTREGISTERTYPE
{
const char * pName; /* Component name, 128 byte limit (including '\0') applies */
OMX_COMPONENTINITTYPE pInitialize; /* Component instance initialization function */
} OMX_COMPONENTREGISTERTYPE;
该结构体负责组件的注册工作,pName
是组件的名字,通常是唯一的,pInitialize
是组件的初始化函数,该成员类型的定义是:typedef OMX_ERRORTYPE (* OMX_COMPONENTINITTYPE)(OMX_IN OMX_HANDLETYPE hComponent);
由此可见hComponent
被提前分配,然后被传递给初始化回调函数,在初始化回调函数里面进行整个组件资源分配、环境的初始化工作。
通常情况下,该结构体总是以数组成员的方式被定义在一个OMX_ComponentRegistered[]
类型的数组当中,属于全局、静态的。要创建一个组件时需要根据组件的名字来在数组里面进行匹配,如果匹配到相关的数组成员,就调用初始化回调函数正式初始化创建组件。
4.2 OMX_COMPONENTTYPE
在OMX_Component.h文件中定义了OMX_COMPONENTTYPE类型数据结构,OMX IL 用它来描述一个component,其中包含了可供调用的函数方法。
typedef struct OMX_COMPONENTTYPE
{
/** The size of this structure, in bytes. It is the responsibility
of the allocator of this structure to fill in this value. Since
this structure is allocated by the GetHandle function, this
function will fill in this value. */
OMX_U32 nSize;
/** nVersion is the version of the OMX specification that the structure
is built against. It is the responsibility of the creator of this
structure to initialize this value and every user of this structure
should verify that it knows how to use the exact version of
this structure found herein. */
OMX_VERSIONTYPE nVersion;
/** pComponentPrivate is a pointer to the component private data area.
This member is allocated and initialized by the component when the
component is first loaded. The application should not access this
data area. */
OMX_PTR pComponentPrivate;
/** pApplicationPrivate is a pointer that is a parameter to the
OMX_GetHandle method, and contains an application private value
provided by the IL client. This application private data is
returned to the IL Client by OMX in all callbacks */
OMX_PTR pApplicationPrivate;
/** refer to OMX_GetComponentVersion in OMX_core.h or the OMX IL
specification for details on the GetComponentVersion method.
*/
OMX_ERRORTYPE (*GetComponentVersion)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_STRING pComponentName,
OMX_OUT OMX_VERSIONTYPE* pComponentVersion,
OMX_OUT OMX_VERSIONTYPE* pSpecVersion,
OMX_OUT OMX_UUIDTYPE* pComponentUUID);
/** refer to OMX_SendCommand in OMX_core.h or the OMX IL
specification for details on the SendCommand method.
*/
OMX_ERRORTYPE (*SendCommand)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_COMMANDTYPE Cmd,
OMX_IN OMX_U32 nParam1,
OMX_IN OMX_PTR pCmdData);
/** refer to OMX_GetParameter in OMX_core.h or the OMX IL
specification for details on the GetParameter method.
*/
OMX_ERRORTYPE (*GetParameter)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nParamIndex,
OMX_INOUT OMX_PTR pComponentParameterStructure);
/** refer to OMX_SetParameter in OMX_core.h or the OMX IL
specification for details on the SetParameter method.
*/
OMX_ERRORTYPE (*SetParameter)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_IN OMX_PTR pComponentParameterStructure);
/** refer to OMX_GetConfig in OMX_core.h or the OMX IL
specification for details on the GetConfig method.
*/
OMX_ERRORTYPE (*GetConfig)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_INOUT OMX_PTR pComponentConfigStructure);
/** refer to OMX_SetConfig in OMX_core.h or the OMX IL
specification for details on the SetConfig method.
*/
OMX_ERRORTYPE (*SetConfig)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_INDEXTYPE nIndex,
OMX_IN OMX_PTR pComponentConfigStructure);
/** refer to OMX_GetExtensionIndex in OMX_core.h or the OMX IL
specification for details on the GetExtensionIndex method.
*/
OMX_ERRORTYPE (*GetExtensionIndex)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_STRING cParameterName,
OMX_OUT OMX_INDEXTYPE* pIndexType);
/** refer to OMX_GetState in OMX_core.h or the OMX IL
specification for details on the GetState method.
*/
OMX_ERRORTYPE (*GetState)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_STATETYPE* pState);
/** The ComponentTunnelRequest method will interact with another OMX
component to determine if tunneling is possible and to setup the
tunneling. The return codes for this method can be used to
determine if tunneling is not possible, or if tunneling is not
supported.
Base profile components (i.e. non-interop) do not support this
method and should return OMX_ErrorNotImplemented
The interop profile component MUST support tunneling to another
interop profile component with a compatible port parameters.
A component may also support proprietary communication.
If proprietary communication is supported the negotiation of
proprietary communication is done outside of OMX in a vendor
specific way. It is only required that the proper result be
returned and the details of how the setup is done is left
to the component implementation.
When this method is invoked when nPort in an output port, the
component will:
1. Populate the pTunnelSetup structure with the output port's
requirements and constraints for the tunnel.
When this method is invoked when nPort in an input port, the
component will:
1. Query the necessary parameters from the output port to
determine if the ports are compatible for tunneling
2. If the ports are compatible, the component should store
the tunnel step provided by the output port
3. Determine which port (either input or output) is the buffer
supplier, and call OMX_SetParameter on the output port to
indicate this selection.
The component will return from this call within 5 msec.
@param [in] hComp
Handle of the component to be accessed. This is the component
handle returned by the call to the OMX_GetHandle method.
@param [in] nPort
nPort is used to select the port on the component to be used
for tunneling.
@param [in] hTunneledComp
Handle of the component to tunnel with. This is the component
handle returned by the call to the OMX_GetHandle method. When
this parameter is 0x0 the component should setup the port for
communication with the application / IL Client.
@param [in] nPortOutput
nPortOutput is used indicate the port the component should
tunnel with.
@param [in] pTunnelSetup
Pointer to the tunnel setup structure. When nPort is an output port
the component should populate the fields of this structure. When
When nPort is an input port the component should review the setup
provided by the component with the output port.
@return OMX_ERRORTYPE
If the command successfully executes, the return code will be
OMX_ErrorNone. Otherwise the appropriate OMX error will be returned.
@ingroup tun
*/
OMX_ERRORTYPE (*ComponentTunnelRequest)(
OMX_IN OMX_HANDLETYPE hComp,
OMX_IN OMX_U32 nPort,
OMX_IN OMX_HANDLETYPE hTunneledComp,
OMX_IN OMX_U32 nTunneledPort,
OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup);
/** refer to OMX_UseBuffer in OMX_core.h or the OMX IL
specification for details on the UseBuffer method.
@ingroup buf
*/
OMX_ERRORTYPE (*UseBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes,
OMX_IN OMX_U8* pBuffer);
/** refer to OMX_AllocateBuffer in OMX_core.h or the OMX IL
specification for details on the AllocateBuffer method.
@ingroup buf
*/
OMX_ERRORTYPE (*AllocateBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN OMX_U32 nSizeBytes);
/** refer to OMX_FreeBuffer in OMX_core.h or the OMX IL
specification for details on the FreeBuffer method.
@ingroup buf
*/
OMX_ERRORTYPE (*FreeBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
/** refer to OMX_EmptyThisBuffer in OMX_core.h or the OMX IL
specification for details on the EmptyThisBuffer method.
@ingroup buf
*/
OMX_ERRORTYPE (*EmptyThisBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
/** refer to OMX_FillThisBuffer in OMX_core.h or the OMX IL
specification for details on the FillThisBuffer method.
@ingroup buf
*/
OMX_ERRORTYPE (*FillThisBuffer)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
/** The SetCallbacks method is used by the core to specify the callback
structure from the application to the component. This is a blocking
call. The component will return from this call within 5 msec.
@param [in] hComponent
Handle of the component to be accessed. This is the component
handle returned by the call to the GetHandle function.
@param [in] pCallbacks
pointer to an OMX_CALLBACKTYPE structure used to provide the
callback information to the component
@param [in] pAppData
pointer to an application defined value. It is anticipated that
the application will pass a pointer to a data structure or a "this
pointer" in this area to allow the callback (in the application)
to determine the context of the call
@return OMX_ERRORTYPE
If the command successfully executes, the return code will be
OMX_ErrorNone. Otherwise the appropriate OMX error will be returned.
*/
OMX_ERRORTYPE (*SetCallbacks)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_CALLBACKTYPE* pCallbacks,
OMX_IN OMX_PTR pAppData);
/** ComponentDeInit method is used to deinitialize the component
providing a means to free any resources allocated at component
initialization. NOTE: After this call the component handle is
not valid for further use.
@param [in] hComponent
Handle of the component to be accessed. This is the component
handle returned by the call to the GetHandle function.
@return OMX_ERRORTYPE
If the command successfully executes, the return code will be
OMX_ErrorNone. Otherwise the appropriate OMX error will be returned.
*/
OMX_ERRORTYPE (*ComponentDeInit)(
OMX_IN OMX_HANDLETYPE hComponent);
/** @ingroup buf */
OMX_ERRORTYPE (*UseEGLImage)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,
OMX_IN OMX_U32 nPortIndex,
OMX_IN OMX_PTR pAppPrivate,
OMX_IN void* eglImage);
OMX_ERRORTYPE (*ComponentRoleEnum)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_OUT OMX_U8 *cRole,
OMX_IN OMX_U32 nIndex);
}
EmptyThisBuffer
和 FillThisBuffer
是驱动组件运行的基本的机制,前者表示让组件消耗缓冲区,表示对应组件输入的内容;后者表示让组件填充缓冲区,表示对应组件输出的内容。
UseBuffer
,AllocateBuffer
,FreeBuffer
为和端口相关的缓冲区管理函数,对于组件的端口有些可以自己分配缓冲区,有些可以使用外部的缓冲区,因此有不同的接口对其进行操作。
SendCommand
表示向组件发送控制类的命令。
GetParameter
,SetParameter
,GetConfig
,SetConfig
几个接口用于辅助的参数和配置的设置和获取。
ComponentTunnelRequest
用于组件之间的隧道化连接,其中需要制定两个组件及其相连的端口。
ComponentDeInit
用于组件的反初始化。
提示:OpenMax函数的参数中,经常包含OMX_IN和OMX_OUT等宏,它们的实际内容为空,只是为了标记参数的方向是输入还是输出。
4.3 OMX_BUFFERHEADERTYPE
该结构体是buffer的抽象化实例对象。前面的几篇文章当中也提到过,在OpenMAX组件当中,buffer的传递经常是以buffer的描述符(往往包含buffer的地址、长宽、格式等信息)来传递的,而不是实际的buffer本身,当需要处理的时候才真正地去访问实际的buffer内存,这是为了减少大buffer的拷贝,降低CPU的负载。事实上Linux内核里面的V4L2架构也是采用buffer描述符的形式来传递buffer数据,这种设计理念与原则可以延伸到所有涉及到大量的buffer操作的系统当中,参考意义非常大。
OpenMAX中提到的buffer在大部分情况下就是说的这个结构体实例,buffer的传递就指的是该结构体实例的传递,buffer的处理才是真正访问内存的部分。
原型:
typedef struct OMX_BUFFERHEADERTYPE
{
OMX_U32 nSize; /**< size of the structure in bytes */
OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
OMX_U8* pBuffer; /**< Pointer to actual block of memory
that is acting as the buffer */
OMX_U32 nAllocLen; /**< size of the buffer allocated, in bytes */
OMX_U32 nFilledLen; /**< number of bytes currently in the
buffer */
OMX_U32 nOffset; /**< start offset of valid data in bytes from
the start of the buffer */
OMX_PTR pAppPrivate; /**< pointer to any data the application
wants to associate with this buffer */
OMX_PTR pPlatformPrivate; /**< pointer to any data the platform
wants to associate with this buffer */
OMX_PTR pInputPortPrivate; /**< pointer to any data the input port
wants to associate with this buffer */
OMX_PTR pOutputPortPrivate; /**< pointer to any data the output port
wants to associate with this buffer */
OMX_HANDLETYPE hMarkTargetComponent; /**< The component that will generate a
mark event upon processing this buffer. */
OMX_PTR pMarkData; /**< Application specific data associated with
the mark sent on a mark event to disambiguate
this mark from others. */
OMX_U32 nTickCount; /**< Optional entry that the component and
application can update with a tick count
when they access the component. This
value should be in microseconds. Since
this is a value relative to an arbitrary
starting point, this value cannot be used
to determine absolute time. This is an
optional entry and not all components
will update it.*/
OMX_TICKS nTimeStamp; /**< Timestamp corresponding to the sample
starting at the first logical sample
boundary in the buffer. Timestamps of
successive samples within the buffer may
be inferred by adding the duration of the
of the preceding buffer to the timestamp
of the preceding buffer.*/
OMX_U32 nFlags; /**< buffer specific flags */
OMX_U32 nOutputPortIndex; /**< The index of the output port (if any) using
this buffer */
OMX_U32 nInputPortIndex; /**< The index of the input port (if any) using
this buffer */
}
- nSize:buffer的总大小。
- pBuffer:buffer的数据存储地址。
- nAllocLen:分配的buffer的总大小,包括有效数据空间与未使用的内存空间。
- nFilledLen:有效的数据长度,其起始地址由pBuffer与nOffset共同决定。
- nOffset:有效数据的起始偏移值,相对于pBuffer来说的。
- pAppPrivate:指向IL Client的私有数据。
- hMarkTargetComponent:在接收到带有标记的buffer之后,处理完毕时需要发送OMX_EventMark 事件的组件句柄。
- pMarkData:指向IL Client特定的数据,等于说是一个特定的标记,不同的buffer可以由该成员来打上不同的标记数据,进而产生不同的处理结果。IL Client在发送
OMX_CommandMarkBuffer
命令的时候把标记数据的指针传递给组件,组件会把相关的标记数据拷贝到该成员中,最后传递给hMarkTargetComponent
。 - nFlags:该标志位包含了buffer的特定标志,比如EOS(流结束)标志等,以下是不同标志位的定义:
#define OMX_BUFFERFLAG_EOS 0x00000001
#define OMX_BUFFERFLAG_STARTTIME 0x00000002 //由数据源组件产生,此时buffer的nTimeStamp就是起始基准值
#define OMX_BUFFERFLAG_DECODEONLY 0x00000004
#define OMX_BUFFERFLAG_DATACORRUPT 0x00000008 //由IL Client产生,表明此buffer是坏的
#define OMX_BUFFERFLAG_ENDOFFRAME 0x00000010
举例来说明下各个成员的作用,比如说编码组件需要对一个1280x720大小的帧进行编码,那么它可能预先需要申请一个1280*720*3/4
大小的buffer(原始数据的四分之三),由于编码出来的数据大小不一定,所以nFilledLen
与nAllocLen
大小就不一致,如果编码出来的数据需要加上一个前缀(比如表示其是H264还是其它的编码之类的),那么nOffset
就不会为0了:
4.4 OMX_CALLBACKTYPE
在OpenMAX中,有一个很重要的点就是回调机制,回调机制提供了组件到IL Client的单向数据传递,组件里面的回调常用于通知IL Client某些命令已经执行完毕(比如状态设置),下面几种情况下会触发组件的回调机制:
-
当一个异步的命令被执行完毕(成功、失败或错误)时就需要产生一个回调事件以通知IL Client命令的执行情况。
-
组件发生一些错误的时候(这些错误不是IL Client产生的命令执行过程产生的)。比如组件内部产生不可恢复的错误并且需要转换到
OMX_StateInvalid
状态时就会触发回调机制。
该结构体的原型如下:
typedef struct OMX_CALLBACKTYPE
{
/** The EventHandler method is used to notify the application when an
event of interest occurs. Events are defined in the OMX_EVENTTYPE
enumeration. Please see that enumeration for details of what will
be returned for each type of event. Callbacks should not return
an error to the component, so if an error occurs, the application
shall handle it internally. This is a blocking call.
The application should return from this call within 5 msec to avoid
blocking the component for an excessively long period of time.
@param hComponent
handle of the component to access. This is the component
handle returned by the call to the GetHandle function.
@param pAppData
pointer to an application defined value that was provided in the
pAppData parameter to the OMX_GetHandle method for the component.
This application defined value is provided so that the application
can have a component specific context when receiving the callback.
@param eEvent
Event that the component wants to notify the application about.
@param nData1
nData will be the OMX_ERRORTYPE for an error event and will be
an OMX_COMMANDTYPE for a command complete event and OMX_INDEXTYPE for a
OMX_PortSettingsChanged event.
@param nData2
nData2 will hold further information related to the event. Can be OMX_STATETYPE for
a OMX_CommandStateSet command or port index for a OMX_PortSettingsChanged event.
Default value is 0 if not used. )
@param pEventData
Pointer to additional event-specific data (see spec for meaning).
*/
OMX_ERRORTYPE (*EventHandler)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_EVENTTYPE eEvent,
OMX_IN OMX_U32 nData1,
OMX_IN OMX_U32 nData2,
OMX_IN OMX_PTR pEventData);
/** The EmptyBufferDone method is used to return emptied buffers from an
input port back to the application for reuse. This is a blocking call
so the application should not attempt to refill the buffers during this
call, but should queue them and refill them in another thread. There
is no error return, so the application shall handle any errors generated
internally.
The application should return from this call within 5 msec.
@param hComponent
handle of the component to access. This is the component
handle returned by the call to the GetHandle function.
@param pAppData
pointer to an application defined value that was provided in the
pAppData parameter to the OMX_GetHandle method for the component.
This application defined value is provided so that the application
can have a component specific context when receiving the callback.
@param pBuffer
pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer
or AllocateBuffer indicating the buffer that was emptied.
@ingroup buf
*/
OMX_ERRORTYPE (*EmptyBufferDone)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
/** The FillBufferDone method is used to return filled buffers from an
output port back to the application for emptying and then reuse.
This is a blocking call so the application should not attempt to
empty the buffers during this call, but should queue the buffers
and empty them in another thread. There is no error return, so
the application shall handle any errors generated internally. The
application shall also update the buffer header to indicate the
number of bytes placed into the buffer.
The application should return from this call within 5 msec.
@param hComponent
handle of the component to access. This is the component
handle returned by the call to the GetHandle function.
@param pAppData
pointer to an application defined value that was provided in the
pAppData parameter to the OMX_GetHandle method for the component.
This application defined value is provided so that the application
can have a component specific context when receiving the callback.
@param pBuffer
pointer to an OMX_BUFFERHEADERTYPE structure allocated with UseBuffer
or AllocateBuffer indicating the buffer that was filled.
@ingroup buf
*/
OMX_ERRORTYPE (*FillBufferDone)(
OMX_OUT OMX_HANDLETYPE hComponent,
OMX_OUT OMX_PTR pAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
}
上面三个回调函数成员需要IL Client实现,并在OMX_GetHandle
函数里面设置填充到组件的自定义结构体成员里面然后就可以在组件内部去调用这些回调函数了。
- EventHandler:
组件通过该回调方法向IL Client发送各种事件,在IL Client端的该方法实现中可以对感兴趣的消息进行分发处理(可以用switch语句实现),OMX_EVENTTYPE
枚举定义了组件可以产生的事件种类。nData1
参数一般会放置OMX_COMMANDTYPE
枚举类型(该命令被组件处理完毕)或者OMX_ERRORTYPE
枚举类型(组件处理命令时发生错误或其它错误);nData2
可以放置一些额外的参数(比如状态转换命令时的目的状态OMX_STATETYPE
枚举)。pEventData
是与事件相关的特定类型的数据,类型由IL Client与组件进行协商规定,该回调是加锁调用的,所以IL Client实现该回调的时候需要在5ms之内返回。下表给出了几种事件和与之对应的各个参数组合:
eEvent | nData1 | nData2 | pEventData |
OMX_EventCmdComplete | OMX_CommandStateSet | 要到达的状态 | 空 |
OMX_CommandFlush | 端口号 | 空 | |
OMX_CommandPortDisable | 端口号 | 空 | |
OMX_CommandPortEnable | 端口号 | 空 | |
OMX_CommandMarkBuffer | 端口号 | 空 | |
OMX_EventError | 错误码 | 0 | 空 |
OMX_EventMark | 0 | 0 | 与mark相关的数据 |
OMX_EventPortSettingsChanged | 端口号 | 0 | 空 |
OMX_EventBufferFlag | 端口号 | nFlags | 空 |
OMX_EventResourcesAcquired | 0 | 0 | 空 |
- EmptyBufferDone
该回调函数用于组件从输入端口(Input Port)拿出一帧数据还回给IL Client(可以看出这种状态下默认IL Client送给组件的数据已经被拷贝到组件私有内存空间了,所以可以直接还回),但是很多时候组件并不会自己再次开辟一个内存空间,而是与IL Client公用同一个内存空间,这时IL Client需要对buffer进行引用计数,并且在组件处理完毕数据之前是不应该还回数据的,所以这个回调函数很多时候并不用实现。 - FillBufferDone
组件调用该回调从组件的输出端口(Output Port)拿出一帧数据还回给IL Client,道理同上。需要注意的是,这三个回调函数都是在加锁的情况下被调用,所以IL Client在实现这些函数的时候要保证能够在5ms之内返回。
4.5 端口定义相关的数据类型
OMX_PORT_PARAM_TYPE
typedef struct OMX_PORT_PARAM_TYPE {
OMX_U32 nSize; /**< size of the structure in bytes */
OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
OMX_U32 nPorts; /**< The number of ports for this component */
OMX_U32 nStartPortNumber; /** first port number for this type of port */
} OMX_PORT_PARAM_TYPE;
用于指定组件端口的数量(nPorts
)与起始标号(nStartPortNumber
)。
OMX_PARAM_PORTDEFINITIONTYPE
typedef struct OMX_PARAM_PORTDEFINITIONTYPE {
OMX_U32 nSize; /**< Size of the structure in bytes */
OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
OMX_U32 nPortIndex; /**< Port number the structure applies to */
OMX_DIRTYPE eDir; /**< Direction (input or output) of this port */
OMX_U32 nBufferCountActual; /**< The actual number of buffers allocated on this port */
OMX_U32 nBufferCountMin; /**< The minimum number of buffers this port requires */
OMX_U32 nBufferSize; /**< Size, in bytes, for buffers to be used for this channel */
OMX_BOOL bEnabled; /**< Ports default to enabled and are enabled/disabled by
OMX_CommandPortEnable/OMX_CommandPortDisable.
When disabled a port is unpopulated. A disabled port
is not populated with buffers on a transition to IDLE. */
OMX_BOOL bPopulated; /**< Port is populated with all of its buffers as indicated by
nBufferCountActual. A disabled port is always unpopulated.
An enabled port is populated on a transition to OMX_StateIdle
and unpopulated on a transition to loaded. */
/**< Domain of the port. Determines the contents of metadata below. */
OMX_PORTDOMAINTYPE eDomain;
union {
OMX_AUDIO_PORTDEFINITIONTYPE audio;
OMX_VIDEO_PORTDEFINITIONTYPE video;
OMX_IMAGE_PORTDEFINITIONTYPE image;
OMX_OTHER_PORTDEFINITIONTYPE other;
} format;
OMX_BOOL bBuffersContiguous;
OMX_U32 nBufferAlignment;
} OMX_PARAM_PORTDEFINITIONTYPE;
端口的实例化对象,一个该结构体类型的变量就用于代表组件的一个端口。
nPortIndex
:只读,表明该端口的索引号,对于同一个组件来说,其端口号是各不相同的,而不同的组件的端口号可以相同。eDir
:指定端口的数据流向,(OMX_DirInput
-输入,OMX_DirOutput
-输出)。bEnabled
:表明端口是否被使能。OMX_CommandPortEnable
或者OMX_CommandPortDisable
。eDomain
:表示该端口所在的域,有OMX_PortDomainAudio
,OMX_PortDomainVideo
,OMX_PortDomainImage
,OMX_PortDomainOther
四种枚举。format
:共用体类型,根据eDomain指定的域来选择相应的结构体来填充。
5. 组件相关的宏定义
下面列举部分OpenMAX官方提供的宏定义,专门用来帮助IL Client来完成对组件的各种操作,具体宏定义的原型就不再详述了,可以根据名字参照OpenMAX提供的头文件代码查看并使用。
OMX_SendCommand
:向指定的组件发送命令。OMX_CommandStateSet
:设置指定组件的状态。OMX_Get/SetParameter
:获取/设置指定组件的参数。OMX_Get/SetConfig
:获取/设置指定组件的配置。OMX_EmptyThisBuffer
:向指定组件传递buffer数据,传递buffer数据到组件的输入端口。OMX_FillThisBuffer
:向指定组件还回buffer数据,传递buffer数据到组件的输出端口。OMX_Init/OMX_Deinit
:组件的初始化/销毁。OMX_GetHandle
:获取组件的实例化对象(句柄)。OMX_SetupTunnel
:在两个组件之间建立连接(tunnel)。
1)OMX_SendCommand
宏向组件发送命令。该调用是非阻塞调用。组件应该检查参数,然后将命令排队到组件线程中执行。组件线程应该在命令结束时发送 EventHandler() 回调。这个宏将直接从应用程序到组件(通过一个核心宏)。
一般的命令有:
typedef enum OMX_COMMANDTYPE
{
OMX_CommandStateSet, /**< Change the component state */
OMX_CommandFlush, /**< Flush the data queue(s) of a component */
OMX_CommandPortDisable, /**< Disable a port on a component. */
OMX_CommandPortEnable, /**< Enable a port on a component. */
OMX_CommandMarkBuffer, /**< Mark a component/buffer for observation */
/**< Reserved region for introducing Khronos Standard Extensions */
OMX_CommandKhronosExtensions = 0x6F000000,
/**< Reserved region for introducing Vendor Extensions */
OMX_CommandVendorStartUnused = 0x7F000000,
OMX_CommandMax = 0X7FFFFFFF
} OMX_COMMANDTYPE;
#define OMX_SendCommand( \
hComponent, \
Cmd, \
nParam, \
pCmdData) \
((OMX_COMPONENTTYPE*)hComponent)->SendCommand( \
hComponent, \
Cmd, \
nParam, \
pCmdData) /* Macro End */
当该命令为“OMX_CommandStateSet”时,组件将队列状态转换到nParam中标识的新状态。
当命令为“OMX_CommandFlush”时,为了刷新端口的缓冲区队列,该命令将强制组件按照接收缓冲区的顺序返回所有当前未被处理的缓冲区给应用程序。
当命令为“OMX_CommandPortDisable”或“OMX_CommandPortEnable”时,组件的端口(由nParam的值给出)将被停止或重启。
当命令“OMX_CommandMarkBuffer”被用来标记一个缓冲区时,pCmdData将指向一个包含组件句柄的OMX_MARKTYPE结构,以检查缓冲区链中的标记。nParam1包含应用缓冲区标记的端口的索引。
例子:OMXSAFE(OMX_SendCommand(vrenderer, OMX_CommandPortEnable, 1, 0));
// 停下1对应的端口
2)OMX_GetParameter
宏将从组件获得当前参数设置之一。这个宏不能只在组件处于OMX_StateInvalid状态时被调用。nParamIndex 参数用于指示正在向组件请求哪个结构。在调用此宏之前,应用程序应分配正确的结构,并填写结构大小和版本信息。当该参数应用于端口时,调用者应填写适当的 nPortIndex 值,指示该参数应用于哪个端口。如果该组件没有更改任何设置,则应该为该组件返回一组有效的DEFAULT参数。这是一个阻塞调用。
#define OMX_GetParameter( \
hComponent, \
nParamIndex, \
pComponentParameterStructure) \
((OMX_COMPONENTTYPE*)hComponent)->GetParameter( \
hComponent, \
nParamIndex, \
pComponentParameterStructure) /* Macro End */
3)OMX_SetParameter
宏将向组件发送初始化参数结构。在对宏的单独调用中,每次发送一个结构体。这个宏只能在组件处于 OMX_StateLoaded 状态或端口被禁用(当参数应用于端口时)时被调用。nParamIndex参数用于指示传递给组件的结构。在调用这个宏之前,应用程序应该分配正确的结构,并应该填写结构大小和版本信息(以及实际数据)。在调用之后,应用程序可以自由地处理这个结构,因为组件需要复制它应保留的任何数据。这是一个阻塞调用。
#define OMX_SetParameter( \
hComponent, \
nParamIndex, \
pComponentParameterStructure) \
((OMX_COMPONENTTYPE*)hComponent)->SetParameter( \
hComponent, \
nParamIndex, \
pComponentParameterStructure) /* Macro End */
4)OMX_GetConfig
宏将从组件获得其中一个配置结构。这个宏可以在组件加载后的任何时候调用。nParamIndex调用参数用于指示正在从组件请求哪个结构。在调用此宏之前,应用程序应分配正确的结构,并填写结构大小和版本信息。如果组件之前没有发送此配置参数,则应该为该组件返回一组有效的DEFAULT值。这是一个阻塞调用。
#define OMX_GetConfig( \
hComponent, \
nConfigIndex, \
pComponentConfigStructure) \
((OMX_COMPONENTTYPE*)hComponent)->GetConfig( \
hComponent, \
nConfigIndex, \
pComponentConfigStructure) /* Macro End */
5)OMX_SetConfig
宏将把其中一个配置结构发送到组件。每个结构每次发送一个,每个都在对宏的单独调用中。这个宏可以在组件加载后的任何时候调用。在调用这个宏之前,应用程序应该分配正确的结构,并应该填写结构大小和版本信息(以及实际数据)。在调用之后,应用程序可以自由地处理这个结构,因为组件需要复制它应保留的任何数据。这是一个阻塞调用。
#define OMX_SetConfig( \
hComponent, \
nConfigIndex, \
pComponentConfigStructure) \
((OMX_COMPONENTTYPE*)hComponent)->SetConfig( \
hComponent, \
nConfigIndex, \
pComponentConfigStructure) /* Macro End */
6)OMX_GetState
宏将调用组件来获取组件的当前状态,并将状态值放置到pState所指向的位置。
#define OMX_GetState( \
hComponent, \
pState) \
((OMX_COMPONENTTYPE*)hComponent)->GetState( \
hComponent, \
pState) /* Macro End */
7)OMX_UseBuffer
宏将请求组件使用另一个组件或IL客户机已经分配的缓冲区(并分配自己的缓冲区头)。这是一个阻塞调用。
#define OMX_UseBuffer( \
hComponent, \
ppBufferHdr, \
nPortIndex, \
pAppPrivate, \
nSizeBytes, \
pBuffer) \
((OMX_COMPONENTTYPE*)hComponent)->UseBuffer( \
hComponent, \
ppBufferHdr, \
nPortIndex, \
pAppPrivate, \
nSizeBytes, \
pBuffer)
8)OMX_AllocateBuffer
宏将请求组件分配一个新的缓冲区和缓冲区头。组件将分配缓冲区和缓冲区头,并返回一个指向缓冲区头的指针。这是一个阻塞调用。
#define OMX_AllocateBuffer( \
hComponent, \
ppBuffer, \
nPortIndex, \
pAppPrivate, \
nSizeBytes) \
((OMX_COMPONENTTYPE*)hComponent)->AllocateBuffer( \
hComponent, \
ppBuffer, \
nPortIndex, \
pAppPrivate, \
nSizeBytes) /* Macro End */
9)OMX_FreeBuffer
宏将从组件中释放一个使用OMX_AllocateBuffer或OMX_UseBuffer分配的缓冲区头。如果组件分配了缓冲区(参见OMX_UseBuffer宏),则组件应该释放缓冲区和缓冲区头。这是一个阻塞调用。
#define OMX_FreeBuffer( \
hComponent, \
nPortIndex, \
pBuffer) \
((OMX_COMPONENTTYPE*)hComponent)->FreeBuffer( \
hComponent, \
nPortIndex, \
pBuffer) /* Macro End */
10)OMX_EmptyThisBuffer
宏将发送一个满是数据的缓冲区到组件的输入端口。该缓冲区将被组件清空,并通过EmptyBufferDone回调返回给应用程序。这是一个非阻塞调用,因为组件将记录缓冲区并立即返回,然后稍后在适当的时间清空缓冲区。如预期的那样,这个宏只能在组件处于OMX_StateExecuting中时被调用。如果nPortIndex没有指定输入端口,组件将返回一个错误。
#define OMX_EmptyThisBuffer( \
hComponent, \
pBuffer) \
((OMX_COMPONENTTYPE*)hComponent)->EmptyThisBuffer( \
hComponent, \
pBuffer) /* Macro End */
11)OMX_FillThisBuffer
宏将发送一个空缓冲区到组件的输出端口。该缓冲区将被组件填充,并通过FillBufferDone回调返回给应用程序。这是一个非阻塞调用,因为组件将记录缓冲区并立即返回,然后稍后在适当的时间填充缓冲区。如预期的那样,这个宏只能在组件处于OMX_ExecutingState时被调用。如果nPortIndex没有指定输出端口,组件将返回一个错误。
#define OMX_FillThisBuffer( \
hComponent, \
pBuffer) \
((OMX_COMPONENTTYPE*)hComponent)->FillThisBuffer( \
hComponent, \
pBuffer) /* Macro End */
12)OMX_UseEGLImage
宏将请求组件使用EGL提供的EGLImage(并分配自己的缓冲区头)。这是一个阻塞调用。
#define OMX_UseEGLImage( \
hComponent, \
ppBufferHdr, \
nPortIndex, \
pAppPrivate, \
eglImage) \
((OMX_COMPONENTTYPE*)hComponent)->UseEGLImage( \
hComponent, \
ppBufferHdr, \
nPortIndex, \
pAppPrivate, \
eglImage)
14)在OMX_Core.h中定义了Core的API函数,应用程序通过它可以进行初始化、处理handle等操作,具体内容如下:
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void); // 初始化OMX Core,且应该是OMX中第一个被调用的函数;
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void); // 反初始化OMX Core,且应该是OMX中最后一个被调用的函数;
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum( // 列出系统中所有可用component的名称;
OMX_OUT OMX_STRING cComponentName,
OMX_IN OMX_U32 nNameLength,
OMX_IN OMX_U32 nIndex);
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle( // 根据名称查找component,并调用component的方法来实例化component;
OMX_OUT OMX_HANDLETYPE* pHandle,
OMX_IN OMX_STRING cComponentName,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_CALLBACKTYPE* pCallBacks);
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle(
OMX_IN OMX_HANDLETYPE hComponent);
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel( // 在两个component之间建立tunnel连接
OMX_IN OMX_HANDLETYPE hOutput,
OMX_IN OMX_U32 nPortOutput,
OMX_IN OMX_HANDLETYPE hInput,
OMX_IN OMX_U32 nPortInput);
OMX_API OMX_ERRORTYPE OMX_GetContentPipe(
OMX_OUT OMX_HANDLETYPE *hPipe,
OMX_IN OMX_STRING szURI);
OMX_API OMX_ERRORTYPE OMX_GetComponentsOfRole (
OMX_IN OMX_STRING role,
OMX_INOUT OMX_U32 *pNumComps,
OMX_INOUT OMX_U8 **compNames);
OMX_API OMX_ERRORTYPE OMX_GetRolesOfComponent (
OMX_IN OMX_STRING compName,
OMX_INOUT OMX_U32 *pNumRoles,
OMX_OUT OMX_U8 **roles);
当应用程序需要使用某个component的功能时,其首先需要调用 OMX_Init()
来对OMX Core进行初始化,然后通过 OMX_GetHandle()
来实例化component,取得相应的component handle(组件句柄)。组件句柄用于访问组件的所有公共方法,还包含指向组件私有数据区域的指针。handle实际上是一个指向component对象的void类型指针,其在OMX_Type.h中定义如下:
typedef void* OMX_HANDLETYPE;
OMX_SetupTunnel()
用来在两个component之间建立tunnel连接。
6. 结语
本文主要介绍了OpenMAX官方头文件里面定义的一些数据类型,其中大部分的数据类型是需要我们自己去实现的,就是说OpenMAX只是提供了一个框架与一套API接口,而接口的具体实现则要靠我们自行编写代码去实现,并且也不是完全不可变的,而是根据实际开发的需要来适当改变一些实现方法来更好地满足开发设计需求。
可以看出来组件的设计方法与linux内核里面的media framework非常相似,都是将一个具体的功能模块抽象为一个(元件),模块之间的连接抽象为(走线),而端口就是(管脚),这个跟电路板上面的电路组成十分相似,可以看出来有时候软件设计有时候可以从硬件设计上面获取灵感,以实现更好的效果,或许除了硬件,还可以从更多的地方来获取到软件设计的方法与思想。