本文作为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代理对象。
从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。
这部分的内容可以和step1结合起来,interface接口调用返回step1中构建出来的mOMX对象。接口实现如下:
sp interface() {
return mOMX;
}
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;
}
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)
{
......
}
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;
}
生成的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;
}
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对象中的操作句柄。