一、引入:
在前面分享MediaCodec和ACodec状态机的时候(MediaCodec(native)状态机分析 和ACodec状态机分析),有讲过各个状态机都是根据底层OMX状态机来的,而OMX的状态想要被上层ACodec和MediaCodec掌握,需要搞懂OMX的消息回调机制是怎么实现的,这篇博客将详细分析下回调函数的注册及消息上报。
二、回调函数的注册:
1.ACodec在申请omx组件时,会获取OMX服务申请组件:
bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
...
err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
...
}
跟进至omx.cpp:
Return<void> Omx::allocateNode(
const hidl_string& name,
const sp<IOmxObserver>& observer,
allocateNode_cb _hidl_cb) {
...
/* 实例化Node */
instance = new OMXNodeInstance(
this, new LWOmxObserver(observer), name.c_str());
OMX_COMPONENTTYPE *handle;
/* 将Node中的回调函数注册到omxmstar中 */
OMX_ERRORTYPE err = mMaster->makeComponentInstance(
name.c_str(), &OMXNodeInstance::kCallbacks,
instance.get(), &handle);
}
首先会申请Node,并且将Node中的回调函数通过OMXMaster
注册到底层的OMX组件中。OMXNodeInstance::kCallbacks
是一个实例化的结构体对象,其类型为OMX_CALLBACKTYPE
,里面包含了三个函数指针,分别是:
typedef struct OMX_CALLBACKTYPE
{
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);
OMX_ERRORTYPE (*EmptyBufferDone)(
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
OMX_ERRORTYPE (*FillBufferDone)(
OMX_OUT OMX_HANDLETYPE hComponent,
OMX_OUT OMX_PTR pAppData,
OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer);
} OMX_CALLBACKTYPE;
EventHandler
用于处理消息事件的回调,EmptyBufferDone
和FillBufferDone
用于底层OMX组件对于数据的处理,本博客只分析EventHandler,剩下的会在后面的博客进行分析。再看看OMXNodeInstance
中这三个函数指针的指向:
OMX_CALLBACKTYPE OMXNodeInstance::kCallbacks = {
&OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
};
也就是说,OMX底层回调上来的信息是在OnEvent
函数中去处理的。
继续跟进看一下回调函数是如何注册到OMX底层组件中的:
OMX_ERRORTYPE OMXMaster::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
...
/* plugin即芯片平台的OMX硬解库 */
OMXPluginBase *plugin = mPluginByComponentName.valueAt(index);
OMX_ERRORTYPE err =
plugin->makeComponentInstance(name, callbacks, appData, component);
...
}
callbacks即为Node传下来的回调函数。看一下芯片平台对这个函数的实现:
OMX_ERRORTYPE MSOMXPlugin::makeComponentInstance(
const char *name,
const OMX_CALLBACKTYPE *callbacks,
OMX_PTR appData,
OMX_COMPONENTTYPE **component) {
ALOGV("MSOMXPlugin::makeComponentInstance");
if (mLibHandle == NULL) {
return OMX_ErrorUndefined;
}
return (*mGetHandle)(
reinterpret_cast<OMX_HANDLETYPE *>(component),
const_cast<char *>(name),
appData, const_cast<OMX_CALLBACKTYPE *>(callbacks));
}
直接返回mGetHandle指针指向的函数:
OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle(
OMX_OUT OMX_HANDLETYPE *pHandle,
OMX_IN OMX_STRING cComponentName,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_CALLBACKTYPE *pCallBacks)
{
...
ret = loadComponent->pOMXComponent->SetCallbacks(loadComponent->pOMXComponent, pCallBacks, pAppData);
....
}
这里会直接将pCallBacks注册到具体的component中:
OMX_ERRORTYPE MS_OMX_SetCallbacks (
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_CALLBACKTYPE* pCallbacks,
OMX_IN OMX_PTR pAppData)
{
...
pMSComponent = (MS_OMX_BAMSOMPONENT *)pOMXComponent->pComponentPrivate;
...
pMSComponent->callbacks = *pCallbacks;
pMSComponent->callbackData = pAppData;
...
}
也就是说,后续OMX组件有消息要通知到上层的时候,直接调用pMSComponent->callbacks.EventHandle
就可以了。
三、回调过程分析:
注册的过程比较简单,而回调的过程相对复杂一些。这里以ACodec设置OMX组件为OMX_StateLoaded
为例,看下状态设置完成后,是如何通知到ACodec的。当OMX组件状态设置完成后,会调用回调函数通知上去:
if (pMSComponent->callbacks.EventHandler != NULL) {
if (ret == OMX_ErrorNone) {
pMSComponent->callbacks.EventHandler((OMX_HANDLETYPE)pOMXComponent,
pMSComponent->callbackData,
OMX_EventCmdComplete, OMX_CommandStateSet,
destState, NULL);
} else {
pMSComponent->callbacks.EventHandler((OMX_HANDLETYPE)pOMXComponent,
pMSComponent->callbackData,
OMX_EventError, ret, 0, NULL);
}
}
OMX_CommandStateSet
为ACodec传下来的指令,OMX_EventCmdComplete
代表OMX命令执行完成。
直接定位到[email protected]
:
OMX_ERRORTYPE OMXNodeInstance::OnEvent(
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) {
...
OMXNodeInstance *instance = static_cast<OMXNodeInstance *>(pAppData);
...
omx_message msg;
msg.type = omx_message::EVENT;
msg.fenceFd = -1;
msg.u.event_data.event = eEvent;
msg.u.event_data.data1 = nData1;
msg.u.event_data.data2 = nData2;
instance->mDispatcher->post(msg, true /* realTime */);
return OMX_ErrorNone;
}
注意,回调上来的参数会被封装到omx_message
中,然后投掷出去。onMessages将会对消息进行处理:
void OMXNodeInstance::onMessages(std::list<omx_message> &messages) {
for (std::list<omx_message>::iterator it = messages.begin(); it != messages.end(); ) {
/* node先处理msg */
if (handleMessage(*it)) {
messages.erase(it++);
} else {
++it;
}
}
/* 再次投递至ACodec */
if (!messages.empty()) {
mObserver->onMessages(messages);
}
}
OMXNodeInstance
会首先对消息进行处理,由于不是buffer的操作,所以这里不会做什么,重点是通过mObserver
将消息再次发送出去。
那么这里的mObserver是什么时候赋值的?ACodec在申请OMX组件的时候,会调用onAllocateComponent
去实例化一个observer,并将其注册到node中:
bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
...
/* 1.创建observer */
sp<CodecObserver> observer = new CodecObserver;
...
/* 2.申请omx组件 */
err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
...
/* 3.创建notify并注册到observer中 */
notify = new AMessage(kWhatOMXMessageList, mCodec);
notify->setInt32("generation", ++mCodec->mNodeGeneration);
observer->setNotificationMessage(notify);
}
所以,mObserver->onMessages
对应的就是ACodec中的observer:
virtual void onMessages(const std::list<omx_message> &messages) {
...
switch (omx_msg.type) {
sp<AMessage> msg = new AMessage;
msg->setInt32("type", omx_msg.type);
switch (omx_msg.type) {
case omx_message::EVENT:
{
msg->setInt32("event", omx_msg.u.event_data.event);
msg->setInt32("data1", omx_msg.u.event_data.data1);
msg->setInt32("data2", omx_msg.u.event_data.data2);
break;
}
...
}
...
msgList->getList().push_back(msg);
notify->setObject("messages", msgList);
notify->post();
}
这个函数包含了两个重要信息,首先,是将omx_message
中的重要信息填入到AMessage
中,便于调用ACodec的AHandler进行处理,其次,这个AMessage不是直接就post出去了,而是推入到msgList
中,再“套娃”到notify投递出去的,那么,这个notify对应的是什么消息呢?notify来自于CodecObserver
的成员变量mNotify,这个msg也是在创建observer的时候完成的,onAllocateComponent
中的注释三便是,也就是说,我们需要看ACodec中kWhatOMXMessageList
消息是怎么处理的:
case ACodec::kWhatOMXMessageList:
{
return checkOMXMessage(msg) ? onOMXMessageList(msg) : true;
}
因为返回的是true,所以看onOMXMessageList:
bool ACodec::BaseState::onOMXMessageList(const sp<AMessage> &msg) {
sp<RefBase> obj;
/* 1.获取msgList */
CHECK(msg->findObject("messages", &obj));
sp<MessageList> msgList = static_cast<MessageList *>(obj.get());
bool receivedRenderedEvents = false;
for (std::list<sp<AMessage>>::const_iterator it = msgList->getList().cbegin();
it != msgList->getList().cend(); ++it) {
/* 2.设置kWhatOMXMessageItem消息类型 */
(*it)->setWhat(ACodec::kWhatOMXMessageItem);
/* 3.处理msg */
mCodec->handleMessage(*it);
int32_t type;
CHECK((*it)->findInt32("type", &type));
if (type == omx_message::FRAME_RENDERED) {
receivedRenderedEvents = true;
}
}
if (receivedRenderedEvents) {
// NOTE: all buffers are rendered in this case
mCodec->notifyOfRenderedFrames();
}
return true;
}
函数很好理解,首先是从msg中取出msgList所在的地址,然后遍历套娃在里面的msg并设置what参数,之后统一调用mCodec->handleMessage
进行处理,这里的mCodec即ACodec自己,由于ACodec继承自AHierarchicalStateMachine
类且没有被复写,所以直接看AHierarchicalStateMachine
中的handleMessage
实现:
void AHierarchicalStateMachine::handleMessage(const sp<AMessage> &msg) {
sp<AState> save = mState;
sp<AState> cur = mState;
/* 调用当前状态的onMessageReceived */
while (cur != NULL && !cur->onMessageReceived(msg)) {
// If you claim not to have handled the message you shouldn't
// have called setState...
CHECK(save == mState);
cur = cur->parentState();
}
if (cur != NULL) {
return;
}
ALOGW("Warning message %s unhandled in root state.",
msg->debugString().c_str());
}
核心就是调用当前状态的onMessageReceived
,由于ACodec中的各个状态都没有去单独实现ACodec::kWhatOMXMessageItem
这个msg,所以,我们直接看BaseState::onMessageReceived
就可以了:
case ACodec::kWhatOMXMessageItem:
{
// no need to check as we already did it for kWhatOMXMessageList
return onOMXMessage(msg);
}
跟进到onOMXMessage:
bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
int32_t type;
CHECK(msg->findInt32("type", &type));
switch (type) {
case omx_message::EVENT:
{
int32_t event, data1, data2;
CHECK(msg->findInt32("event", &event));
CHECK(msg->findInt32("data1", &data1));
CHECK(msg->findInt32("data2", &data2));
if (event == OMX_EventCmdComplete
&& data1 == OMX_CommandFlush
&& data2 == (int32_t)OMX_ALL) {
// Use of this notification is not consistent across
// implementations. We'll drop this notification and rely
// on flush-complete notifications on the individual port
// indices instead.
return true;
}
return onOMXEvent(
static_cast<OMX_EVENTTYPE>(event),
static_cast<OMX_U32>(data1),
static_cast<OMX_U32>(data2));
}
...
}
由于此时的状态为LoadedToIdleState
,所以这里要去ACodec::LoadedToIdleState::onOMXEvent
:
bool ACodec::LoadedToIdleState::onOMXEvent(
OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
switch (event) {
case OMX_EventCmdComplete:
{
status_t err = OK;
if (data1 != (OMX_U32)OMX_CommandStateSet
|| data2 != (OMX_U32)OMX_StateIdle) {
ALOGE("Unexpected command completion in LoadedToIdleState: %s(%u) %s(%u)",
asString((OMX_COMMANDTYPE)data1), data1,
asString((OMX_STATETYPE)data2), data2);
err = FAILED_TRANSACTION;
}
/* 向OMX底层发送OMX_StateExecuting的CMD */
if (err == OK) {
err = mCodec->mOMXNode->sendCommand(
OMX_CommandStateSet, OMX_StateExecuting);
}
if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
} else {
mCodec->changeState(mCodec->mIdleToExecutingState);
}
return true;
}
default:
return BaseState::onOMXEvent(event, data1, data2);
}
}
可以看到,当ACodec确认OMX在设置OMX_StateIdle成功后,会继续向OMX发送OMX_StateExecuting
的状态设置,使OMX底层的buffer运转起来。
四、消息机制详细图解: