20. Android MultiMedia框架完全解析 - OMX组件解析与状态机简介

在上一篇文章中,讲了如何去使用一个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函数,通过这个过程,就实现了调用应用的回调函数。

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值