1. OpenMAX系列 —— ACodec和OMX的联系

1. OpenMAX系列 —— ACodec和OMX的联系

本文作为openmax系列的开篇博文(注:N版本),记录了个人对openmax的一些理解。可能会出现部分误解,望理解并欢迎指正。

MediaCodec调用ACodec的initiateAllocateComponent接口进编解码组件的创建(这里以创建解码组件来分析),ACodec就给自己发送了个msg: kWhatAllocateComponent。结合下面的代码来看,这个msg的处理就是单纯的调用执行onAllocateComponent。

bool ACodec::UninitializedState::onMessageReceived(const sp &msg) {
    bool handled = false;
    switch (msg->what()) {
        ... 
        case ACodec::kWhatAllocateComponent:
        {
            onAllocateComponent(msg);
            handled = true;
            break;
        }
        ...             
    }
}       

onAllocateComponent接口实现内容(截取部分):

bool ACodec::UninitializedState::onAllocateComponent(const sp &msg) {
    1. omx client连接omx
    OMXClient client;
    if (client.connect() != OK) {
        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
        return false;
    }

    2. 获取omx bp代理对象
    sp omx = client.interface();

    sp observer = new CodecObserver;
    IOMX::node_id node = 0;

    3. allocate node
    err = omx->allocateNode(componentName.c_str(), observer, &mCodec->mNodeBinder, &node);

    notify = new AMessage(kWhatOMXMessageList, mCodec);
    observer->setNotificationMessage(notify);
    mCodec->mOMX = omx;
    mCodec->mNode = node;
}

很明显可以看到ACodec将omx bp代理对象和node id保存在其私有变量中,方面后面和omx进行联系,omx bp代理对象和omx bn通信,omx bn再根据node id找到存放在mNodeIDToInstance中OMXNodeInstance对象,而该对象中存放有编解码组件的操作句柄。
在onAllocateComponent接口实现内容中,omxclient连接omx,通过interface接口将连接过程中获取到omx bp对象返回给ACodec保存并使用。连接omx的过程是在media.player和media.codec(N版本mediaserver将不同service独立拆分了)进程获取omx bp代理对象。

1. OpenMAX系列 —— ACodec和OMX的联系_第1张图片


step1. omx client连接omx

从media.player和media.codec服务获取iomx的bp代理对象,并且封装在MuxOMX中,返回给ACodec的就是mOMX变量。

status_t OMXClient::connect() {
    sp sm = defaultServiceManager();
    sp playerbinder = sm->getService(String16("media.player"));
    sp mediaservice = interface_cast(playerbinder);
    sp mediaServerOMX = mediaservice->getOMX();

    sp codecbinder = sm->getService(String16("media.codec"));
    sp codecservice = interface_cast(codecbinder);
    sp mediaCodecOMX = codecservice->getOMX();

    mOMX = new MuxOMX(mediaServerOMX, mediaCodecOMX);
    return OK;
}

以media.player服务为例,media.player服务交互的过程省略不说,主要看getOMX接口的实现内容。mOMX变量使用了mutable关键字来修饰,表面该变量外部不可变,只能内部接口中可更改值。单纯来看,mOMX只是一个OMX对象。不过在OMX的构造函数中,实例化了OMXMaster对象,就是OMXMaster加载了omx plugin。

sp MediaPlayerService::getOMX() {
    ALOGI("MediaPlayerService::getOMX");
    Mutex::Autolock autoLock(mLock);

    if (mOMX.get() == NULL) {
        mOMX = new OMX;
    }

    return mOMX;
}

OMX::OMX()
    : mMaster(new OMXMaster),
      mNodeCounter(0) {
}

OMXMaster::OMXMaster()
    : mVendorLibHandle(NULL) {
    ......  
    addVendorPlugin();
    addPlugin(new SoftOMXPlugin);
}

.看了omx client连接omx的整个过程,ACodec最后拿到的是iomx的bp代理对象。通过这个bp代理对象和omx通信,进一步告知omxmaster去操作omx plugin。


step2. 获取omx bp代理对象

这部分的内容可以和step1结合起来,interface接口调用返回step1中构建出来的mOMX对象。接口实现如下:

sp interface() {
    return mOMX;
}

step3. allocate node

onAllocateComponent接口的局部变量omx实质上是一个MuxOMX对象,所以被调用函数allocateNode在OMXClient.cpp文件。getPreferredCodecLocation接口返回值决定着后面采用的iomx bp代理对象是处于media.player服务,还是media.codec进程。毕竟还是需要通过iomx bp代理对象和omx进行通信的,从而获取node节点。这个node节点只是一个无符号的32位整型数字,但是通过类似键值对的方式 对应着一个解码组件的操作句柄。

status_t MuxOMX::allocateNode(
        const char *name, const sp &observer,
        sp *nodeBinder,
        node_id *node) {
    sp omx;

    node_location loc = getPreferredCodecLocation(name);
    if (loc == CODECPROCESS) {
        omx = mMediaCodecOMX;
    } else if (loc == MEDIAPROCESS) {
        omx = mMediaServerOMX;
    } else {
        if (mLocalOMX == NULL) {
            mLocalOMX = new OMX;
        }
        omx = mLocalOMX;
    }

    status_t err = omx->allocateNode(name, observer, nodeBinder, node);

    mNodeLocation.add(*node, loc);
    return OK;
}

从下面接口中的注释来看,非安全解码器、google软解等正常是运行在media.codec进程下,而硬解是运行在mediaserver进程中 更加安全。

MuxOMX::node_location MuxOMX::getPreferredCodecLocation(const char *name) {
    if (sCodecProcessEnabled) {
        // all codecs go to codec process unless excluded using system property, in which case
        // all non-secure decoders, OMX.google.* codecs and encoders can go in the codec process
        // (non-OMX.google.* encoders can be excluded using system property.)
        if ((strcasestr(name, "decoder")
                        && strcasestr(name, ".secure") != name + strlen(name) - 7)
                || (strcasestr(name, "encoder")
                        && !property_get_bool("media.stagefright.legacyencoder", false))
                || !property_get_bool("media.stagefright.less-secure", false)
                || !strncasecmp(name, "OMX.google.", 11)) {
            return CODECPROCESS;
        }
        // everything else runs in the media server
        return MEDIAPROCESS;
    } else {
        ......  
    }
}

iomx bp代理对象和omx进行通信,发送“ALLOCATE_NODE”指令过去,omx调用allocateNode接口,并且将node节点打包返回给iomx bp代理对象,方便后面两者交互。
omx allocateNode接口分析:大致分3步。

status_t OMX::allocateNode(
    const char *name, const sp &observer,
    sp *nodeBinder, node_id *node) {
   Mutex::Autolock autoLock(mLock);

   *node = 0;
   if (nodeBinder != NULL) {
       *nodeBinder = NULL;
   }

   if (mNodeIDToInstance.size() == kMaxNodeInstances) {
       // all possible node IDs are in use
       return NO_MEMORY;
   }
   step3.1 实例化OMXNodeInstance对象,存放node id、解码组件handle、ACodec传递下来的observer等
   OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name);

   OMX_COMPONENTTYPE *handle;
   step3.2 master通知plugin创建对应的解码组件、并返回其操作句柄 
   OMX_ERRORTYPE err = mMaster->makeComponentInstance(
           name, &OMXNodeInstance::kCallbacks,
           instance, &handle);

   if (err != OMX_ErrorNone) {
       ALOGE("FAILED to allocate omx component '%s' err=%s(%#x)", name, asString(err), err);

       instance->onGetHandleFailed();

       return StatusFromOMXError(err);
   }
   step3.3 生成node id,将该id和instance对应保存起来    
   *node = makeNodeID_l(instance);
   mDispatchers.add(*node, new CallbackDispatcher(instance));

   instance->setHandle(*node, handle);

   mLiveNodes.add(IInterface::asBinder(observer), instance);
   IInterface::asBinder(observer)->linkToDeath(this);

   return OK;
}

step3.1 实例化OMXNodeInstance对象,存放一些关键信息

OMXNodeInstance对象持有一些关键信息,比如生成的节点id(mNodeID)、ACodec传递下来的observer、plugin里创建的解码组件handle和构造OMXNodeInstance时传入的omx对象等。
mNodeID:最后返回给ACodec,node id主要使用在omx。omx的私有变量mNodeIDToInstance和mDispatcher是将node id和OMXNodeInstance、CallbackDispatcher对应保存起来。后面函数调用再通过ACodec传下来的node id查找对应的OMXNodeInstance或者CallbackDispatcher,最后找到需要的解码组件的操作句柄等。
observer: ACodec初始化解码组件时创建的omxobserver bn对象,用于OMXNodeInstance内FBD、EBD、Event事件回掉通知给ACodec处理。
handle:编解码组建的操作句柄,实际操作的对象。上面的存粹是兜兜转转,真正干活的就是这个handle,用这个handle操作plugin。
omx:在OMXNodeInstance内保存一份而已,看着有用到,但后面操作又回到OMXNodeInstance中。

OMXNodeInstance::OMXNodeInstance(
    OMX *owner, const sp &observer, const char *name)
: mOwner(owner),
  mNodeID(0),
  mHandle(NULL),
  mObserver(observer),
  mDying(false),
  mSailed(false),
  mQueriedProhibitedExtensions(false),
  mBufferIDCount(0)
{
    ......
}

step3.2 master通知plugin创建对应的解码组件、并返回其操作句柄

makeComponentInstance接口实现关键的地方在于先找到指定的plugin、再通知plugin去创建对应的解码组件。这部分的内容后面再细看。

OMX_ERRORTYPE OMXMaster::makeComponentInstance(
        const char *name,
        const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData,
        OMX_COMPONENTTYPE **component) {
        ALOGI("makeComponentInstance(%s) in %s process", name, mProcessName);
        Mutex::Autolock autoLock(mLock);

        *component = NULL;

        ssize_t index = mPluginByComponentName.indexOfKey(String8(name));

        if (index < 0) {
            return OMX_ErrorInvalidComponentName;
        }

        OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
        OMX_ERRORTYPE err =
            plugin->makeComponentInstance(name, callbacks, appData, component);

        if (err != OMX_ErrorNone) {
            return err;
        }

        mPluginByInstance.add(*component, plugin);

        return err;
    }

step3.3 生成node id,将该id和instance对应保存起来

生成的node id是node_id 类型,实质是一个32位的无符号int型。高16位为进程号,低16位为mNodeCounter计数值。关键是将这个node id和instance变量关联起来,以键值对形式存放到mNodeIDToInstance中。后面只要根据node id,就可以取到对应的instance,再拿到存放在instance中的解码器操作句柄。

OMX::node_id OMX::makeNodeID_l(OMXNodeInstance *instance) {
    // mLock is already held.

    node_id prefix = node_id(getpid() << 16);
    node_id node = 0;
    do  {
        if (++mNodeCounter >= kMaxNodeInstances) {
            mNodeCounter = 0; // OK to use because we're combining with the pid
        }
        node = node_id(prefix | mNodeCounter);
    } while (mNodeIDToInstance.indexOfKey(node) >= 0);
    mNodeIDToInstance.add(node, instance);

    return node;
}

总结

1. OpenMAX系列 —— ACodec和OMX的联系_第2张图片

acodec在创建编解码组件的过程中,通过omxclient拿到omx bp代理对象,根据这个代理对象和omx进行通信。omx接收到ALLOCATE_NODE的请求后,调用allocateNode()接口进一步通知plugin创建对应的编解码组件,并返回其操作句柄,后面就是操作这个句柄来工作。这个操作句柄保存在instance(OmxNodeInstance类型)对象中,与之对应的是一个node id,是一个32位的无符号int值,两者以键值对形式存放在mNodeIDToInstance变量中。acodec可以拿到的就是这个node id和omx bp代理对象。

在O版本上由于HIDL化,omx bp和bn通信部分发生了较大的变化,但是最后调用的还是instance对象,操作的还是保存在instance对象中的操作句柄。

你可能感兴趣的:(Android)