MediaCodec, ACodec消息机制

1,312 阅读6分钟

核心三元素

  • AHandler
  • ALooper
  • AMessage

类图

Mediacodec, ACodec 消息机制-2.jpg

三者如何建立联系?(以 MediaCodec 初始化为例)

Handler 和 Looper 绑定

我们目前所知的是 AHandler 是处理消息,AMessage 代表消息本身,而 ALooper 负责管理消息队
列,接下来介绍三者之间是如何建立联系,让消息能被对应的的 handler 接收到并处理。
从 java 层开始,用户使用的 MediaCodec 的时候,会使用 MediaCodec.createByCodecName 来创建
实例,这个函数在 JNI 中的代码有

mLooper = new ALooper;  
mLooper->setName("MediaCodec_looper");//创建以 MediaCodec_looper 为名的  
ALooper 实例  
mLooper->start(
        false, // runOnCallingThread
        true,  // canCallJava |
        ANDROID_PRIORITY_VIDEO);//因为runOnCallingThread 是 false,也就是这个 start 动作会创建一个新的 Looper 线程  
if (nameIsType) {  
    mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);  
} else {  
    mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);

以上我们已经创建并启动了一个 looper 的循环,但是此处 looper 还是孤独的,并没有和另外两个组件
建立联系。
MediaCodec::CreateByComponentName 为例,这个函数中主要做两件事情。

  1. sp<MediaCodec> codec = new MediaCodec(looper, pid, uid)
  2. const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);

通过实例化一个 MediaCodec 对象,将 looper 赋值给 codec 的 mLooper 变量。
这里我们已经将一个 looper 和一个 handler (MediaCodec) 联系起来了,但是要真正让这两者工作起
来,这还是不够的, 在 MediaCodec::init 函数中

if (mIsVideo) {  
    // video codec needs dedicated looper  
    if (mCodecLooper == NULL) {  
    mCodecLooper = new ALooper;  
    mCodecLooper->setName("CodecLooper");  
    mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);  
} 
    mCodecLooper->registerHandler(mCodec);  
} else {  
    // 注册 handler(ACodec)  
    mLooper->registerHandler(mCodec);  
} 
// 注册 Handler(MediaCodec)  
mLooper->registerHandler(this);  
// 给 ACodec 设置一个以 kWhatCodecNotify 为 msg 的 AMessage 变量,这个message 的 handler 是 MediaCodec  
mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, this));  
sp<AMessage> msg = new AMessage(kWhatInit, this);  
msg->setString("name", name);  
msg->setInt32("nameIsType", nameIsType);  

现在看 ALooper::registerHandler 中是如何做的

ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {  
    return gLooperRoster.registerHandler(this, handler);  
}

gLooperRoster 的类型是 ALooperRoster ,实际上是调用

ALooper::handler_id ALooperRoster::registerHandler(const sp<ALooper> looper, const sp<AHandler> &handler) {  
    Mutex::Autolock autoLock(mLock);  
    if (handler->id() != 0) {  
        CHECK(!"A handler must only be registered once.");  
        return INVALID_OPERATION;  
    } 
    HandlerInfo info;  
    info.mLooper = looper;  
    info.mHandler = handler;  
    ALooper::handler_id handlerID = mNextHandlerID++;  
    //放入KeyedVector中  
    mHandlers.add(handlerID, info);  
    //setID 会给 AHandler 的成员变量 mLooper 赋值,记录关联起来的 looper,不过这里的 mLooper 和Mediacodec 中的 mlooper 有点不同,这里的是 wp 类型的,而且 Mediacodec 中的是 sp 类型的。  
    handler->setID(handlerID, looper);  
    return handlerID;  
}

AMessage 消息发送

kWhatInit 的发送和处理为例,在 Mediacodec::init 中,会 new 一个 kWhatInit 的消息,之后
会发送出去,现在看这一段具体是怎么做的。

status_t MediaCodec::PostAndAwaitResponse(const sp<AMessage> &msg, sp<AMessage> *response) {  
    status_t err = msg->postAndAwaitResponse(response);  
    if (err != OK) {  
        return err;  
    }
    if (!(*response)->findInt32("err", &err)) {  
        err = OK;  
    } 
    return err;  
}

AMessage 有三种 post 消息的方式,分别是

  • post: 把消息发送出去,除此之外什么都不做
  • postAndAwaitResponse: 不仅 post 消息,还会等待一个 reply
  • postReply:和上一项组合使用,用于发送 reply
status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {  
    sp<ALooper> looper = mLooper.promote();//因为AMessage中持有的 mLooper 是 wp,如果要操作实际对象的话,需要提升到 sp。  
    if (looper == NULL) {  
        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);  
        return -ENOENT;  
    } 
    //创建 token  
    sp<AReplyToken> token = looper->createReplyToken();  
    if (token == NULL) {  
        ALOGE("failed to create reply token");  
        return -ENOMEM;  
    } 
    //作为 ID 设置到当前的 msg (kWhatInit)中  
    setObject("replyID", token);  
    // post 到 looper中  
    looper->post(this, 0 /* delayUs */);  
    return looper->awaitResponse(token, response);//等待回复  
    }
}

post 消息之后,looper 进入 awaitResponse 开始等待回复。

looper 中的处理

在 looper->post 中,主要干了这些事

Event event;  
event.mWhenUs = whenUs;  
event.mMessage = msg;

mEventQueue.insert(it, event);  

其实就只是将 msg 封装成 event 之后,塞进队列里面。
ALooper::start 的时候,会创建一个 LooperThread 来处理 Event 队列,最终是在
ALooper::loop() 中工作的

event = *mEventQueue.begin();  
event.mMessage->deliver();

AMessage::deliver()

sp<AHandler> handler = mHandler.promote();//提升到 SP,用于操作对象  
handler->deliverMessage(this);

handler 的处理

之后就是 AHandler::deliverMessage()

onMessageReceived(msg);

这时候就到了 MediaCodec 中的函数实现了

case kWhatInit:  
{  
    sp<AReplyToken> replyID;  
    CHECK(msg->senderAwaitsResponse(&replyID));  
    if (mState != UNINITIALIZED) {  
        // post err  
        PostReplyWithError(replyID, INVALID_OPERATION);  
        break;  
    } 
    mReplyID = replyID;  
    //设置当前状态  
    setState(INITIALIZING);  
    AString name;  
    CHECK(msg->findString("name", &name));  
    int32_t nameIsType;  
    int32_t encoder = false;  
    CHECK(msg->findInt32("nameIsType", &nameIsType));  
    if (nameIsType) {  
        CHECK(msg->findInt32("encoder", &encoder));  
    } 
    sp<AMessage> format = new AMessage;  
    if (nameIsType) {  
        format->setString("mime", name.c_str());  
        format->setInt32("encoder", encoder);  
    } else {  
        format->setString("componentName", name.c_str());  
    } 
    //调用 ACodec  
    mCodec->initiateAllocateComponent(format);  
    break;  
} 

处理 reply

上面已经进入 MediaCodec 的处理 msg 了,这时需要返回 reply 了,不然那边就还在那里等着。
上面的代码看起来很多,但是并没有真正的发送 reply,而只是把这个消息的 token 通过 mReplyID = replyID; 记录了下来。这个消息的 reply 是在 ACodec 处理完 initiateAllocateComponent 的流程的时
候,通知 MediaCodec 来做的。

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg)  
{ ...  
    // 这个 notify 就是在之前 mediacodec init 过程中,设置给 ACodec 的,用于  
    ACodec post 消息给 Mediacodec  
    sp<AMessage> notify = mCodec->mNotify->dup();  
    notify->setInt32("what", CodecBase::kWhatComponentAllocated);  
    notify->setString("componentName", mCodec->mComponentName.c_str());  
    notify->post();  
...  
}

然后在

MediaCodec::onMessageReceived(const sp<AMessage> &msg) {  
    switch (msg->what()) {  
        case kWhatCodecNotify:  
        {  
        //...  
            switch (what) {  
                case CodecBase::kWhatComponentAllocated:  
                {  
                    // 切换状态至 INITIALIZED  
                    setState(INITIALIZED);  
                    mFlags |= kFlagIsComponentAllocated;  
                    CHECK(msg->findString("componentName", &mComponentName));  
                    if (mComponentName.startsWith("OMX.google.")) {  
                        mFlags |= kFlagUsesSoftwareRenderer;  
                    } else {  
                        mFlags &= ~kFlagUsesSoftwareRenderer;  
                    }  
                    ...  
                    // 发送回复了,把之前记录的 mReplayID 作为参数传了进去  
                    (new AMessage)->postReply(mReplyID);  
                    break;  
                } 
                //....其它case
            }  
        }  
    }  
}  

AMessage::postReply 最终也是调用 ALooper::postReply

status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {  
    Mutex::Autolock autoLock(mRepliesLock);  
    status_t err = replyToken->setReply(reply);  
    if (err == OK) {  
        mRepliesCondition.broadcast();  
    } 
    return err;  
}  
status_t AReplyToken::setReply(const sp<AMessage> &reply) {  
    if (mReplied) {  
        ALOGE("trying to post a duplicate reply");  
        return -EBUSY;  
    } 
    CHECK(mReply == NULL);  
    mReply = reply;  
    mReplied = true;  
    return OK;  
}  

在把 response 设置给 token 之后,会广播 Condition,唤醒等待中的线程,前文中的
ALooper::awaitResponse 返回

status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {  
    // return status in case we want to handle an interrupted wait  
    Mutex::Autolock autoLock(mRepliesLock);  
    CHECK(replyToken != NULL);  
    while (!replyToken->retrieveReply(response)) {  
        mRepliesCondition.wait(mRepliesLock);  
    } 
    return OK;  
}  
bool retrieveReply(sp<AMessage> *reply) {  
    if (mReplied) {  
        *reply = mReply;  
        mReply.clear();  
    } 
    return mReplied;  
}  

这样等待就结束了,MediaCodec 也拿到了相应的返回信息,整个 init 流程也结束了。如果是不带 response 的消息 post ,就更加简单了,只在对应的 handler 里面处理完消息就可以了。

两种不同的 notify

在 Mediacodec-ACodec 中有两种用于 notify 的 AMessage,一种是前文中有提到的,用户 ACodec
post msg to Mediacodec 的,msg name 是 kWhatCodecNotify ,还有一种就是用于 omx post msg
to ACodec 的。
首先在 init 阶段, ACodec::UninitializedState::onAllocateComponent 中会有

sp<CodecObserver> observer = new CodecObserver;  
...  
notify = new AMessage(kWhatOMXMessageList, mCodec);  
observer->setNotificationMessage(notify);

当 OMX 有 EMPTY_BUFFER_DONEFILL_BUFFER_DONE 等消息通知上层的时候,首先 call
CodecObserver::onMessages 中,在这个函数中,会把上报的消息全部都放进列表中

notify->setObject("messages", msgList);  
notify->post(); 

这里的 notify 就是初始化过程中的 kWhatOMXMessageList ,之后就是 ACodec 的
onMessageReceived 函数去处理了。

ACodec 中的 onMessageReceived

如果看代码的话,会注意到在 ACodec.cpp 中其实是没有 ACodec::onMessageReceived 函数对于各
个不同的消息的处理的,反而是在 BaseState, UninitializedState 等类中会有 onMessageReceived
但这些 State类并不是 AHandler 的子类,所以仍然需要 ACodec::onMessageReceived
在 ACodec.h 中,实现了这个函数,内容只有一句

handleMessage(msg);

从 ACodec 的继承关系中可以看到,这个 handleMessage 是 ACodec 的父类 AHierarchicalStateMachine 的函数

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());  
}

总结

梳理了 Mediacodec 和 ACodec 中使用的消息机制,发消息和收消息以及处理消息是如何运作
的,涉及的不同 Class。
需要追踪哪个消息是在哪里处理,看这个消息 new 的时候,设置的 handler 是哪个就行了。
一个线程对应一个 looper,一个 handler 对应一个 looper,而 Handler 和 looper 能处理多个
message。
ACodec 在特定的状态能够处理一些特定的消息,也有一些通用的消息是在所有状态下都能处理
的。