在上一篇文章中,讲了如何去使用一个OMX组件,我个人有几个问题:
1)在状态转换函数中,只是发送了一个OMX_CommandStateSet命令,状态转换就完成了,里面是如何操作的?
2)设置了回调函数后,比如向组件发送OMX_EmptyThisBuffer后,OMX组件是如何向外面返回Empty_Done的?同理还有Fill_Done和OMX_CmdComplete?
对于一个OMX组件,它有几个端口,同时还需要进行状态转换,那么如何去设计这样的一个组件呢?
以我浅薄的理解,将OMX的常用操作抽象出来一个父类,真正的插件类作为它的子类,去实现自己差异化的东西。同时,抽象出来了一个Port类去形容端口。对于状态机的实现,我倒是不会设计了,参考FSL的设计,抽象出几个状态类,其中每个状态包含他自己可以执行的一些权限操作等,同时这几个状态类都作为组件父类的友元,确实设计的比较聪明。
(一)组件解析
先分析一下FSL是如何实现的,组件父类是class ComponentBase,代码位于:
external/fsl_imx_omx/OpenMAXIL/src/component/common/ComponentBase.cpp & ComponentBase.h
class ComponentBase {
public:
friend class State;
friend class LoadedState;
friend class IdleState;
friend class ExecutingState;
friend class PauseState;
friend void *DoThread(void *arg);
State *CurState; /**< stores the instance of current state */
ComponentBase();
virtual ~ComponentBase() {};
OMX_ERRORTYPE ConstructComponent(OMX_HANDLETYPE pHandle);
OMX_ERRORTYPE DestructComponent();
OMX_ERRORTYPE SetCallbacks(OMX_CALLBACKTYPE* pCbs, OMX_PTR pAppData);
OMX_ERRORTYPE ComponentRoleEnum(OMX_U8 *cRole, OMX_U32 nIndex);
OMX_HANDLETYPE GetComponentHandle();
OMX_ERRORTYPE GetState(OMX_STATETYPE* pState);
OMX_ERRORTYPE SetState(OMX_STATETYPE eState);
OMX_ERRORTYPE SendEvent(OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData);
OMX_ERRORTYPE EmptyDone(OMX_BUFFERHEADERTYPE* pBufferHdr);
OMX_ERRORTYPE FillDone(OMX_BUFFERHEADERTYPE* pBufferHdr);
OMX_ERRORTYPE PortNotify(OMX_U32 nPortIndex, PORT_NOTIFY_TYPE ePortNotify);
OMX_ERRORTYPE MarkOtherPortsBuffer(OMX_MARKTYPE *pMarkData);
OMX_BOOL bHasCmdToProcess();
virtual OMX_ERRORTYPE DoAllocateBuffer(OMX_PTR *buffer, OMX_U32 nSize,OMX_U32 nPortIndex);
virtual OMX_ERRORTYPE DoFreeBuffer(OMX_PTR buffer,OMX_U32 nPortIndex);
virtual OMX_ERRORTYPE FlushComponent(OMX_U32 nPortIndex);
virtual OMX_ERRORTYPE ComponentReturnBuffer(OMX_U32 nPortIndex);
virtual OMX_ERRORTYPE UseEGLImage(OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_U32 nPortIndex, OMX_PTR pAppPrivate, void* eglImage);
OMX_U8 name[MAX_NAME_LEN];
protected:
OMX_VERSIONTYPE ComponentVersion;
OMX_U32 role_cnt; /**< how many roles this component supported */
OMX_STRING role[MAX_COMPONENT_ROLE]; /**< store all the roles this component supported */
OMX_BOOL bInContext;
OMX_U32 nPorts;
Port *ports[MAX_PORT_NUM]; /**< store all the port instance this component have */
OMX_PORT_PARAM_TYPE PortParam[TOTAL_PORT_PARM];
private:
OMX_VERSIONTYPE SpecVersion;
OMX_COMPONENTTYPE *hComponent;
OMX_CALLBACKTYPE *pCallbacks;
fsl_osal_ptr pThreadId;
fsl_osal_sem outContextSem;
fsl_osal_mutex SemLock;
OMX_S32 SemCnt;
OMX_BOOL bStopThread;
fsl_osal_mutex inContextMutex;
fsl_osal_u32 InContextThreadId;
List<MSG_TYPE> DelayedInContextMsgList;
OMX_STATETYPE eCurState;
State *states[TOTAL_STATE]; /**< stores all the instance of each state */
Queue *pCmdQueue;
PENDING_CMD pendingCmd; /**< stores the current processing command */
virtual OMX_ERRORTYPE InitComponent() = 0;
virtual OMX_ERRORTYPE DeInitComponent() = 0;
virtual OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE nParamIndex, OMX_PTR pComponentParameterStructure);
virtual OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nParamIndex, OMX_PTR pComponentParameterStructure);
virtual OMX_ERRORTYPE GetConfig(OMX_INDEXTYPE nParamIndex, OMX_PTR pComponentParameterStructure);
virtual OMX_ERRORTYPE SetConfig(OMX_INDEXTYPE nParamIndex, OMX_PTR pComponentParameterStructure);
virtual OMX_ERRORTYPE GetExtensionIndex(OMX_STRING cParameterName, OMX_INDEXTYPE* pIndexType);
virtual OMX_ERRORTYPE PortFormatChanged(OMX_U32 nPortIndex);
virtual OMX_ERRORTYPE InstanceInit();
virtual OMX_ERRORTYPE InstanceDeInit();
virtual OMX_ERRORTYPE DoLoaded2Idle();
virtual OMX_ERRORTYPE DoIdle2Loaded();
virtual OMX_ERRORTYPE DoExec2Pause();
virtual OMX_ERRORTYPE DoPause2Exec();
OMX_ERRORTYPE ProcessInContext(MSG_TYPE msg);
OMX_ERRORTYPE ProcessOutContext(MSG_TYPE msg);
OMX_ERRORTYPE Up();
OMX_ERRORTYPE Down();
virtual OMX_ERRORTYPE ProcessClkBuffer();
virtual OMX_ERRORTYPE ProcessDataBuffer() = 0;
OMX_BOOL CheckAllPortsState(PORT_NOTIFY_TYPE ePortNotify);
};
这个类可以理解为所有Component的父类,它通过ConstructComponent函数来构建组件,通过GetParameter,SetParameter等等设置参数,同时还有DoAllocateBuffer,DoFreeBuffer等等对buffer操作的函数。需要注意的是这个类中的私有成员,包括端口格式转变函数:PortFormatChanged,状态切换函数:DoLoaded2Idle,DoIdle2Loaded,DoExec2Pause等等,这些虚函数是需要各个组件自己去实现的了。
另外注意我标红的几个成员状态变量,它们就是组件状态机实现的关键,作为ComponentBase类的友元存在。
(二)状态机实现机制
下面来看看里面的函数实现,设计的非常巧妙(本人真的没有看过设计模式等类型的书,只能从代码上分析他们的实现),以BaseGetComponentVersion函数为例:
static OMX_ERRORTYPE BaseGetComponentVersion(
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)
{
ComponentBase *base = NULL;
OMX_COMPONENTTYPE *hComp = NULL;
if(hComponent == NULL)
return OMX_ErrorInvalidComponent;
hComp = (OMX_COMPONENTTYPE*)hComponent;
base = (ComponentBase*)(hComp->pComponentPrivate);
OMX_COMP_API_LOG(base->name,"GetComponentVersion: ComponentName: %s \r\n",pComponentName);
return base->CurState->GetVersion(pComponentName, pComponentVersion, pSpecVersion, pComponentUUID);
}
根据hComp来获取到组件的Handle,通过这个Handle找到它的父类base,最后调用父类中的状态友元变量,根据不同的状态来调用不同状态对应的函数。
那么一个组件,如何知道它的当前状态的?或者说,它的状态是如何切换的,先贴出来之前讲过的状态切换图:
想要搞懂这些,先来看ComponentBase::ConstructComponent()函数。为什么先来看这个函数呢?在分析OMXFSLPlugin_new.cpp时,我们说过,当一个组件加载时,会去/etc/media_codec.xml目录下读取组件的入口函数,而在组件的入口函数中,就会去调用base->ConstructComponent函数来构建这个组件。以VpuDecComponent这个插件为例(这篇文章就会用这个组件为例)(external/fsl_imx_omx/OpenMAXIL/src/component/vpu_dec_v2/VpuDecComponent.cpp):
/**< C style functions to expose entry point for the shared library */
extern "C"
{
OMX_ERRORTYPE VpuDecoderInit(OMX_IN OMX_HANDLETYPE pHandle)
{
OMX_ERRORTYPE ret = OMX_ErrorNone;
VpuDecoder *obj = NULL;
ComponentBase *base = NULL;
VPU_COMP_API_LOG("%s: \r\n",__FUNCTION__);
obj = FSL_NEW(VpuDecoder, ());
if(obj == NULL)
{
return OMX_ErrorInsufficientResources;
}
base = (ComponentBase*)obj;
ret = base->ConstructComponent(pHandle);
if(ret != OMX_ErrorNone)
{
return ret;
}
return ret;
}
}
VpuDecoder继承自VideoFilter,而VideoFilter继承自ComponentBase。子类中都没有去实现ConstructComponent函数,所以就会调用到ComponentBase::ConstructComponent()函数。
下面就来看这个函数:
OMX_ERRORTYPE ComponentBase::ConstructComponent(
OMX_HANDLETYPE pHandle)
{
OMX_ERRORTYPE ret = OMX_ErrorNone;
OMX_PARAM_PORTDEFINITIONTYPE sPortDef;
OMX_U32 i;
if(pHandle == NULL)
return OMX_ErrorBadParameter;
SpecVersion.s.nVersionMajor = 0x1;
SpecVersion.s.nVersionMinor = 0x1;
SpecVersion.s.nRevision = 0x2;
SpecVersion.s.nStep = 0x0;
//设置组件的版本号
hComponent = (OMX_COMPONENTTYPE*)pHandle;
OMX_CHECK_STRUCT(hComponent, OMX_COMPONENTTYPE, ret);
if(ret != OMX_ErrorNone)
return ret;
hComponent->pComponentPrivate = this;
hComponent->GetComponentVersion = BaseGetComponentVersion;
hComponent->SendCommand = BaseSendCommand;
hComponent->GetParameter = BaseGetParameter;
hComponent->SetParameter = BaseSetParameter;
hComponent->GetConfig = BaseGetConfig;
hComponent->SetConfig = BaseSetConfig;
hComponent->GetExtensionIndex = BaseGetExtensionIndex;
hComponent->GetState = BaseGetState;
hComponent->ComponentTunnelRequest = BaseComponentTunnelRequest;
hComponent->UseBuffer = BaseUseBuffer;
hComponent->AllocateBuffer = BaseAllocateBuffer;
hComponent->FreeBuffer = BaseFreeBuffer;
hComponent->EmptyThisBuffer = BaseEmptyThisBuffer;
hComponent->FillThisBuffer = BaseFillThisBuffer;
hComponent->SetCallbacks = BaseSetCallbacks;
hComponent->ComponentDeInit = BaseComponentDeInit;
hComponent->UseEGLImage = BaseUseEGLImage;
hComponent->ComponentRoleEnum = BaseComponentRoleEnum;
//设置组件的函数指针
pendingCmd.cmd = OMX_CommandMax;
/**< setup cmd queue */
pCmdQueue = FSL_NEW(Queue, ());
if(pCmdQueue == NULL) {
LOG_ERROR("New cmd queue failed.\n");
ret = OMX_ErrorInsufficientResources;
goto err;
}
if(pCmdQueue->Create(32, sizeof(CMD_MSG), E_FSL_OSAL_TRUE) != QUEUE_SUCCESS) {
LOG_ERROR("Init cmd queue failed.\n");
ret = OMX_ErrorUndefined;
goto err;
}
//创建Cmd Queue,其实就是开一个新线程
/**< setup ports */
if(nPorts > MAX_PORT_NUM) {
LOG_ERROR("Requried port number: %d more than defined: %d\n", nPorts, MAX_PORT_NUM);
ret = OMX_ErrorBadParameter;
goto err;
}
for(i=0; i<nPorts; i++) {
ports[i] = FSL_NEW(Port, (this, i));
if(ports[i] == NULL) {
LOG_ERROR("Create ports[%d] failed.\n", i);
ret = OMX_ErrorInsufficientResources;
goto err;
}
ret = ports[i]->Init();
if(ret != OMX_ErrorNone) {
LOG_ERROR("Init port [%d] failed\n", i);
goto err;
}
}
//一个组件可能有几个端口,每个端口都用一个Port类来描述,并初始化Port
/**< setup states */
states[InvalidStateIdx] = FSL_NEW(InvalidState, (this));
states[LoadedStateIdx] = FSL_NEW(LoadedState, (this));
states[IdleStateIdx] = FSL_NEW(IdleState, (this));
states[ExecutingStateIdx] = FSL_NEW(ExecutingState, (this));
states[PauseStateIdx] = FSL_NEW(PauseState, (this));
states[WaitForResourcesStateIdx] = FSL_NEW(WaitForResourcesState, (this));
if(states[InvalidStateIdx] == NULL
|| states[LoadedStateIdx] == NULL
|| states[IdleStateIdx] == NULL
|| states[ExecutingStateIdx] == NULL
|| states[PauseStateIdx] == NULL
|| states[WaitForResourcesStateIdx] == NULL) {
LOG_ERROR("Create states failed.\n");
ret = OMX_ErrorInsufficientResources;
goto err;
}
//这里就是设置一个组件的状态变量,每个不同的状态用一个不用的类来描述,这里也可以看到一个组件有哪些状态:Invalid,Loaded,Idle,Executing,Pause,WaitForResources等几个状态。
/**< setup mutex and thread for message processing */
//这里设置一些互斥量等,就省略了
ret = InitComponent(); //这个函数,是ComponentBase的一个虚函数,它是需要子类去实现的,我们自己去写一个OMX组件的话,这个函数也是一个首要实现的函数。
if(ret != OMX_ErrorNone)
goto err;
for(i=0; i<TOTAL_PORT_PARM; i++) {
OMX_INIT_STRUCT(&PortParam[i], OMX_PORT_PARAM_TYPE);
PortParam[i].nStartPortNumber = OMX_ALL;
}
OMX_INIT_STRUCT(&sPortDef, OMX_PARAM_PORTDEFINITIONTYPE);
for(i=0; i<nPorts; i++) {
sPortDef.nPortIndex = i;
ports[i]->GetPortDefinition(&sPortDef);
switch(sPortDef.eDomain) {
case OMX_PortDomainAudio:
PortParam[AudioPortParamIdx].nPorts ++;
if(PortParam[AudioPortParamIdx].nStartPortNumber == OMX_ALL)
PortParam[AudioPortParamIdx].nStartPortNumber = i;
break;
case OMX_PortDomainVideo:
PortParam[VideoPortParamIdx].nPorts ++;
if(PortParam[VideoPortParamIdx].nStartPortNumber == OMX_ALL)
PortParam[VideoPortParamIdx].nStartPortNumber = i;
break;
case OMX_PortDomainImage:
PortParam[ImagePortParamIdx].nPorts ++;
if(PortParam[ImagePortParamIdx].nStartPortNumber == OMX_ALL)
PortParam[ImagePortParamIdx].nStartPortNumber = i;
break;
case OMX_PortDomainOther:
PortParam[OtherPortParamIdx].nPorts ++;
if(PortParam[OtherPortParamIdx].nStartPortNumber == OMX_ALL)
PortParam[OtherPortParamIdx].nStartPortNumber = i;
break;
default:
break;
}
}
//设置端口的一些信息,端口有四种:Audio,Video,Image,Other,这里就需要区分设置。
/**< set state to loaded */
eCurState = OMX_StateLoaded; //初始化当前的状态为Loaded,用eCurState这个变量来记录
CurState = states[LoadedStateIdx];
LOG_DEBUG("Component %s is loaded.\n", name);
return ret;
err:
DestructComponent();
return ret;
}
参照上一篇文章中讲的OMX的使用例子,首先通过OMX_Init()函数来初始化OMX的环境,在这个函数中会去注册组件,注册content pipe,之后就会通过OMX_GetHandle()函数来构建组件,并获取这个组件的Handler,在这个函数中会通过上述的ConstructComponent()函数来构建组件,并通过SetCallbacks来设置组件的回调函数,此时就完成了组件的加载,状态为Loaded。
组件的下一个状态就是Idle了,应用程序是如何让组件状态切换成Idle的呢?
SendCommand(hTest, OMX_CommandStateSet,eState,NULL, OMX_FALSE);
下面具体来看看这个函数的实现,如果能够理解的话,就会对OMX的理解深入一层:
OMX_ERRORTYPE SendCommand(
HTEST *hTest,
OMX_COMMANDTYPE Cmd,
OMX_U32 nParam1,
OMX_PTR pCmdData,
OMX_BOOL bSync)
{
OMX_ERRORTYPE ret = OMX_ErrorNone;
OMX_HANDLETYPE hComponent = hTest->hComponent;
hTest->sCmdDone.eEvent = OMX_EventMax;
hTest->bError = OMX_FALSE;
ret = OMX_SendCommand(hComponent, Cmd, nParam1, pCmdData);
if(ret != OMX_ErrorNone)
return ret;
if(bSync == OMX_TRUE)
WaitCommand(hTest, Cmd, nParam1, pCmdData);
return OMX_ErrorNone;
}
以前只是追踪到这里后就再也没有继续追踪下去,可以在OMX_Core.h中,这是一个宏定义:
#define OMX_SendCommand( \
hComponent, \
Cmd, \
nParam, \
pCmdData) \
((OMX_COMPONENTTYPE*)hComponent)->SendCommand( \
hComponent, \
Cmd, \
nParam, \
pCmdData) /* Macro End */
具体是如何实现的呢?先来看OMX_COMPONENTTYPE,这个结构体从名字上面来看就是一个OMX的元件,这个结构体里面包含了一个元件需要的所有操作函数,比如:
SendCommand/GetParameter/GetConfig/SetConfig/GetState一些常规操作,以及UseBuffer/AllocateBuffer/FreeBuffer/EmptyThisBuffer/FillThisBuffer等等对buffer的操作。
SendCommand函数的实现,由于在宏定义中是通过(OMX_COMPONENTTYPE*)hComponent)->SendCommand这种形式调用的,所以需要找到hComponent是如何得到的,这个hComponent是OMX_HANDLETYPE类型的,就是VpuDecoder类,而这个成员在前面通过OMX_GetHandle来实例化,并取得对应的handler,而VpuDecoder类又继承自ComponentBase类,在ComponentBase::ConstructComponent()函数中指定了
hComponent->SendCommand = BaseSendCommand;
所以这里就会调用到BaseSendCommand函数:
static OMX_ERRORTYPE BaseSendCommand(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_COMMANDTYPE Cmd,
OMX_IN OMX_U32 nParam1,
OMX_IN OMX_PTR pCmdData)
{
ComponentBase *base = NULL;
OMX_COMPONENTTYPE *hComp = NULL;
if(hComponent == NULL)
return OMX_ErrorInvalidComponent;
hComp = (OMX_COMPONENTTYPE*)hComponent;
base = (ComponentBase*)(hComp->pComponentPrivate);
OMX_COMP_API_LOG(base->name,"SendCommand: Cmd: 0x%X(%s), Param: 0x%X, CmdData: 0x%X \r\n",Cmd,DBG_MAP_STR((OMX_U32)Cmd,&omx_map_cmd_str[0]),nParam1,pCmdData);
return base->CurState->SendCommand(Cmd, nParam1, pCmdData);
}
看最后一行,调用base当前状态的下的SendCommand函数,就像前面介绍的,这些状态作为友元存在于ComponentBase类中:
class ComponentBase {
public:
friend class State;
friend class LoadedState;
friend class IdleState;
friend class ExecutingState;
friend class PauseState;
当前组件的状态为Loaded,所以以LoadedState为例(LoadedState.cpp文件中):
OMX_ERRORTYPE LoadedState::SendCommand(
OMX_COMMANDTYPE Cmd,
OMX_U32 nParam,
OMX_PTR pCmdData)
{
return State::DoSendCommand(Cmd, nParam, pCmdData);
}
然后在State::DoSendCommand中(它在State.cpp文件中实现),继续通过
ret = (base->*Process)(COMMAND);
来将这个任务转给ComponentBase来处理,看看这个Process函数的定义(State.h中):
OMX_ERRORTYPE (ComponentBase::*Process)(MSG_TYPE msg);
Process函数的实现在State.cpp文件中:
State::State(ComponentBase *pBase) : base(pBase)
{
if(base->bInContext == OMX_TRUE)
Process = &ComponentBase::ProcessInContext;
else
Process = &ComponentBase::ProcessOutContext;
}
ComponentBase::ProcessInContext()函数如下(ComponentBase.cpp文件中):
OMX_ERRORTYPE ComponentBase::ProcessInContext(
MSG_TYPE msg)
{
OMX_ERRORTYPE ret1, ret2;
if(E_FSL_OSAL_SUCCESS != fsl_osal_mutex_trylock(inContextMutex)) {
fsl_osal_u32 id = 0;
fsl_osal_thread_self(&id);
if(id == InContextThreadId) {
DelayedInContextMsgList.Add(&msg);
return OMX_ErrorNone;
}
else
fsl_osal_mutex_lock(inContextMutex);
}
fsl_osal_thread_self(&InContextThreadId);
while(1) {
if(msg == COMMAND)
CurState->ProcessCmd();
else if(msg == BUFFER)
CurState->ProcessBuffer();
if(DelayedInContextMsgList.GetNodeCnt() == 0)
break;
MSG_TYPE *pMsg = NULL;
pMsg = DelayedInContextMsgList.GetNode(0);
msg = *pMsg;
DelayedInContextMsgList.Remove(pMsg);
};
fsl_osal_mutex_unlock(inContextMutex);
return OMX_ErrorNone;
}
然后通过CurState->ProcessCmd()又跳到State.cp中了:
OMX_ERRORTYPE State::ProcessCmd()
{
OMX_ERRORTYPE ret = OMX_ErrorNone;
CMD_MSG sMsg;
OMX_U32 i;
if(base->pCmdQueue->Size() == 0)
return OMX_ErrorNoMore;
base->pCmdQueue->Get(&sMsg);
LOG_DEBUG("Processing Command[cmd:%d, param: %d, data: %d] in Component[%s].\n",
sMsg.Cmd, sMsg.nParam, sMsg.pCmdData, base->name);
switch (sMsg.Cmd)
{
case OMX_CommandStateSet:
switch (sMsg.nParam)
{
case OMX_StateInvalid:
ret = ToInvalid();
break;
case OMX_StateLoaded:
ret = ToLoaded();
break;
case OMX_StateIdle:
ret = ToIdle();
break;
case OMX_StateExecuting:
ret = ToExecuting();
break;
case OMX_StatePause:
ret = ToPause();
break;
case OMX_StateWaitForResources:
ret = ToWaitForResources();
break;
default:
LOG_ERROR("Invalid state transation.\n");
ret = OMX_ErrorBadParameter;
break;
}
break;
因为我们使用的SendCommand命令是:
SendCommand(hTest, OMX_CommandStateSet,eState,NULL, OMX_FALSE);
所以下面就会走OMX_CommandStateSet 这个case,而当前的状态为Loaded,下一个要设置的状态为Idle,所以就会调用ret = ToIdle();函数,而这个函数,每个状态都实现了这个函数,但是当前的状态为Loaded,所以就会调用到: LoadedState::ToIdle()函数(LoadedState.cpp文件)
OMX_ERRORTYPE LoadedState::ToIdle()
{
OMX_ERRORTYPE ret = OMX_ErrorNone;
OMX_U32 i;
ret = base->DoLoaded2Idle();
if(ret != OMX_ErrorNone) {
base->DoIdle2Loaded();
return ret;
}
for(i=0; i<base->nPorts; i++) {
if(base->ports[i]->IsEnabled() != OMX_TRUE)
continue;
if(base->ports[i]->IsTunneled() == OMX_TRUE
&& base->ports[i]->IsSupplier() == OMX_TRUE) {
base->ports[i]->SupplierAllocateBuffers();
}
ret = OMX_ErrorNotComplete;
}
return ret;
}
这时调用的是base->DoLoaded2Idle()函数,ComponentBase中并没有实现这个虚函数,所以会调用到子类的DoLoaded2Idle()函数。
OMX_ERRORTYPE VideoFilter::DoLoaded2Idle()
{
hTsHandle = tsmCreate();
if(hTsHandle == NULL) {
LOG_ERROR("Create Ts manager failed.\n");
return OMX_ErrorUndefined;
}
return OMX_ErrorNone;
}
就是做了一些子类认为它从Loaded状态转换成Idle状态所需的步骤。
看完上面的分析后,再来看看它的状态机机制。
一个组件有Loaded,Idle,Executing,Pause,WaitForResource,Invilid几种状态,所以也抽象出来一个State类,这些类都继承自State类。State类中会做一些常规的操作,而不同类中处理不同的状态类型转换,这句话是什么意思呢?举个例子,比如从Loaded状态转换成Idle状态和从Executing状态转换成Idle状态,在State中都是通过:ToIdle()函数来实现。而在ComponentBase::ProcessInContext()函数中,是通过CurState->ProcessCmd();来调用的,所以如果从Loaded状态转换成Idle状态,就会去调用LoadedState::ToIdle()函数,而从Executing状态转换成Idle状态,就需要去调用ExecutingState::ToIdle()函数。
(三)回调函数机制(以上一篇文章中的应用为例)
首先明确回调函数的定义,应用通过指针,把回调函数的指针设置给组件,组件在执行应用的某些指令后,调用执行对应的函数指针。
应用向组件设置回调函数是通过OMX_GetHandle函数:
OMX_GetHandle(&hComponent, hTest->name, hTest, &gCallBacks);
在组件的构造函数中,就设置了:
hComponent->SetCallbacks = BaseSetCallbacks;
所以通过OMXCore最终就会调用到这个函数:
static OMX_ERRORTYPE BaseSetCallbacks(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_CALLBACKTYPE* pCbs,
OMX_IN OMX_PTR pAppData)
{
ComponentBase *base = NULL;
OMX_COMPONENTTYPE *hComp = NULL;
if(hComponent == NULL)
return OMX_ErrorInvalidComponent;
hComp = (OMX_COMPONENTTYPE*)hComponent;
base = (ComponentBase*)(hComp->pComponentPrivate);
OMX_COMP_API_LOG(base->name,"SetCallbacks: \r\n");
return base->SetCallbacks(pCbs, pAppData);
}
然后就是:
OMX_ERRORTYPE ComponentBase::SetCallbacks(
OMX_CALLBACKTYPE* pCbs,
OMX_PTR pAppData)
{
pCallbacks = pCbs;
hComponent->pApplicationPrivate = pAppData;
return OMX_ErrorNone;
}
这里的pCallbacks就是应用的gCallBacks了。
下面看看应用场景:
当数据开始传输时,就开始调用Template::ProcessDataBuffer()函数处理数据(具体为什么会调用到这个函数,后面再分析),组件就开始从输入端口接收数据,从输出端口输出数据,肯定就会使用Port的SendBuffer函数:
ports[OUT_PORT]->SendBuffer(pOutBufferHdr);
在这个函数中:
OMX_ERRORTYPE Port::SendBuffer(
OMX_BUFFERHEADERTYPE* pBufferHdr)
{
if(sPortDef.eDir == OMX_DirInput) {
if(bTunneled == OMX_TRUE)
OMX_FillThisBuffer(sTunnelInfo.hTunneledComp, pBufferHdr);
else
base->EmptyDone(pBufferHdr);
}
}
然后看看这个函数:
OMX_ERRORTYPE ComponentBase::EmptyDone(
OMX_BUFFERHEADERTYPE* pBufferHdr)
{
pCallbacks->EmptyBufferDone(hComponent, hComponent->pApplicationPrivate, pBufferHdr);
LOG_DEBUG("EmptyDone to client, buffer[%p]\n", pBufferHdr);
OMX_COMP_CALLBACK_LOG(((ComponentBase*)(hComponent->pComponentPrivate))->name,"EmptyDone to buffer: 0x%X \r\n", pBufferHdr->pBuffer);
return OMX_ErrorNone;
}
看看struct OMX_CALLBACKTYPE的定义,它只有三个回调函数,所以对应的EmptyBufferDone回调函数指针,就会调用到应用的gm_EmptyBufferDone函数,通过这个过程,就实现了调用应用的回调函数。