MediaCodec中的AHandler、ALooper和AMessage機制簡單分析

一、 前言:
Android媒體通路中,大量充斥着AHandler、ALooper和AMessage的消息機制,之前簡單分析了一下java層的消息機制,而native層的消息機制同java層原理類似,但又有一些區別,所以單獨拿來分析一下,在nuplayer和mediacodec中隨處可見這種異步機制。
三者的簡單概括:
AMessage:我們要發送的消息,類似於一個“包裹”,“郵件”;
AHandler:消息處理者,與java層消息機制不同的是,這裏的hanlder不會有發送message的功能,每個handler都有一個唯一的標識符ID;
ALooper:消息分發的控制者,實際上通過一個“小弟”ALooperRoster來協調控制message與handler;

下面我們將以mediacodec中一條消息的發送爲例,來看下一條message是如何被打包、發送再到處理的,進而結合源碼,對整個native層的AHandler、ALooper和AMessage有一個全面的瞭解。
相關代碼路徑(Android5.1):

AHandler、ALooper和AMessage相關源碼路徑:
frameworks\av\media\libstagefright\foundation
mediacodec代碼路徑:
frameworks\av\media\libstagefright

二、示例分析:

  1. 消息機制的建立:
    消息機制的建立首先是ALooper的實例化:
    mediacodec的java接口通過JNI會調入到android_media_MediaCodec.cpp文件中,在native層,會先去實例化一個JMediaCodec:
JMediaCodec::JMediaCodec(
        JNIEnv *env, jobject thiz,
        const char *name, bool nameIsType, bool encoder)
    : mClass(NULL),
      mObject(NULL) {
      ...
    /* 1.實例化一個Alooper */
    mLooper = new ALooper;
    mLooper->setName("MediaCodec_looper");

	/* 2.開啓looper的start */
    mLooper->start(
            false,      // runOnCallingThread
            true,       // canCallJava
            PRIORITY_FOREGROUND);
        if (nameIsType) {
        if (mDebug) {
            ALOGI("CreateByType(%s, encoder:%s)", name, encoder?"true":"false");
            mIsVideo = !strncasecmp(name, "video/", 6);
        }
        /* 3.實例化mediacodec */
        mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
    } else {
        if (mDebug) {
            ALOGI("CreateByComponentName(%s)", name);
            AString nameString = AString(name);
            nameString.trim();
            if (nameString.find("video", 0) >= 0) {
                mIsVideo = true;
            }
        }
        mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
    }
    ...
}

截取了該函數中關鍵部分代碼,首先是實例化ALooper,然後是設置當前線程中looper的名字,注意,native層代碼中將不再限制一個線程只有一個looper:


ALooper::ALooper()
    : mRunningLocally(false) {
	/* 每個looper會維護一個全局變量ALooperRoster,也就是前文說的“小弟” */
    gLooperRoster.unregisterStaleHandlers();
}

void ALooper::setName(const char *name) {
    mName = name;
}

從構造函數可以看出來,ALooper是單例設計模式,每個ALooper只有一個ALooperRoster,ALooper很多重要的工作都是交由ALooperRoster來完成的。
回到JMediaCodec看第二步,start函數:

status_t ALooper::start(
        bool runOnCallingThread, bool canCallJava, int32_t priority) {
    if (runOnCallingThread) {
        {
            Mutex::Autolock autoLock(mLock);

            if (mThread != NULL || mRunningLocally) {
                return INVALID_OPERATION;
            }

            mRunningLocally = true;
        }

        do {
        } while (loop());

        return OK;
    }

    Mutex::Autolock autoLock(mLock);

    if (mThread != NULL || mRunningLocally) {
        return INVALID_OPERATION;
    }
	/* 創建了一個thread給looper處理 */
    mThread = new LooperThread(this, canCallJava);
	/* 調用run方法讓thread轉起來 */
    status_t err = mThread->run(
            mName.empty() ? "ALooper" : mName.c_str(), priority);
    if (err != OK) {
        mThread.clear();
    }

    return err;
}

實際上,我們可以把looper看成是一個thread來操作;
JMediaCodec的第三步就是去實例化mediacodec了:

sp<MediaCodec> MediaCodec::CreateByType(
        const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err) {
    /* 實例化mediacodec */
    sp<MediaCodec> codec = new MediaCodec(looper);
	
	/* 執行init操作 */
    const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
    if (err != NULL) {
        *err = ret;
    }
    return ret == OK ? codec : NULL; // NULL deallocates codec.
}
MediaCodec::MediaCodec(const sp<ALooper> &looper)
    : mState(UNINITIALIZED),
      mLooper(looper),
      mCodec(NULL),
      mReplyID(0),
      mFlags(0),
      mStickyError(OK),
      mSoftRenderer(NULL),
      mBatteryStatNotified(false),
      mIsVideo(false),
      mDequeueInputTimeoutGeneration(0),
      mDequeueInputReplyID(0),
      mDequeueOutputTimeoutGeneration(0),
      mDequeueOutputReplyID(0),
      mHaveInputSurface(false) {
    mStats = false;
    mBufferCounter = 0;
    char value[PROPERTY_VALUE_MAX];
    if (property_get("service.media.codec.stats", value, NULL)
        && (!strcasecmp("true", value))) {
        mStats = true;
    }
}

mediacodec構造中關鍵的一點就是把JMediaCodec實例化的looper給傳過來了;
再看一下init函數,有點長,我們也只截取部分:

status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
	...
	
	/* 1.實例化ACodec */
	mCodec = new ACodec;
    bool needDedicatedLooper = false;
    if (nameIsType && !strncasecmp(name.c_str(), "video/", 6)) {
        needDedicatedLooper = true;
    } else {
        AString tmp = name;
        if (tmp.endsWith(".secure")) {
            tmp.erase(tmp.size() - 7, 7);
        }
        const sp<IMediaCodecList> mcl = MediaCodecList::getInstance();
        ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
        if (codecIdx >= 0) {
            const sp<MediaCodecInfo> info = mcl->getCodecInfo(codecIdx);
            Vector<AString> mimes;
            info->getSupportedMimes(&mimes);
            for (size_t i = 0; i < mimes.size(); i++) {
                if (mimes[i].startsWith("video/")) {
                    needDedicatedLooper = true;
                    break;
                }
            }
        }
    }

    if (needDedicatedLooper) {
        if (mCodecLooper == NULL) {
        /* 2.mediacodec再創建一個looper */
            mCodecLooper = new ALooper;
            mCodecLooper->setName("CodecLooper");
            mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
        }
		/* 3.將ACodec註冊到mediacodec的looper中 */
        mCodecLooper->registerHandler(mCodec);
    } else {
        mLooper->registerHandler(mCodec);
    }
	
	/* 4.將mediacodec註冊到JMediaCodec的looper中 */
    mLooper->registerHandler(this);
    ...
}

init函數會去實例化ACodec,它與OMX進行交互,並且,在mediacodec中又創建了一個looper,然後就是調用registerHandler來將handler註冊到looper中,這裏需要點一下,mediacodec和acodec都是繼承自handler,每個looper可以有多個handler,但是,每個handler只能被註冊到一個looper中,看一下注冊函數:

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

轉交ALooperRoster來處理:

ALooper::handler_id ALooperRoster::registerHandler(
        const sp<ALooper> looper, const sp<AHandler> &handler) {
    Mutex::Autolock autoLock(mLock);

	/* handler構造時默認id爲0,如果不爲0,說明該handler已經被註冊 */
    if (handler->id() != 0) {
        CHECK(!"A handler must only be registered once.");
        return INVALID_OPERATION;
    }

    HandlerInfo info;			//打包handler
    info.mLooper = looper;		//記錄當前looper
    info.mHandler = handler;	//記錄註冊handler
    /* 分配一個id */
    ALooper::handler_id handlerID = mNextHandlerID++;
    /* 這是一個KeyedVector,記錄每個handlerinfo與id */
    mHandlers.add(handlerID, info);
	
	/* 將分配的id設置到handler中 */
    handler->setID(handlerID);

    return handlerID;
}

registerHandler完成的事就是將該handler註冊到對應的looper中.
到目前爲止,我們必須明白以下幾點:
①JMediaCodec和mediacodec各自創建了一個looper;
②JMediaCodec的looper中註冊了兩個handler,分別是JMediaCodec和mediacodec;
③mediacodec的looper中目前只註冊了ACodec這一個handler;

2. start指令中的消息傳遞:
這裏選用mediacodec的start指令來看下消息收發,經過java層,jni,最終調用到mediacodec的start函數中:

status_t MediaCodec::start() {
	/* 實例化一個Amessage */
    sp<AMessage> msg = new AMessage(kWhatStart, id());

	/* 投遞出去並獲取響應 */
    sp<AMessage> response;
    return PostAndAwaitResponse(msg, &response);
}

AMessage的實例化很簡單:

AMessage::AMessage(uint32_t what, ALooper::handler_id target)
    : mWhat(what),
      mTarget(target),
      mNumItems(0) {
}

看一下下面的PostAndAwaitResponse函數:

status_t MediaCodec::PostAndAwaitResponse(
        const sp<AMessage> &msg, sp<AMessage> *response) {
     /* 調用AMessage的postAndAwaitResponse函數 */
    status_t err = msg->postAndAwaitResponse(response);

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

    if (!(*response)->findInt32("err", &err)) {
        err = OK;
    }

    return err;
}

還是得去看AMessage中的函數實現:

status_t AMessage::postAndAwaitResponse(sp<AMessage> *response) {
    return gLooperRoster.postAndAwaitResponse(this, response);
}

這個gLooperRoster很眼熟,沒錯,就是ALooper中的ALooperRoster,可以看下AMessage.h:

AMessage.cpp:
extern ALooperRoster gLooperRoster;

AMessage是通過外部引用的ALooper中的“小弟”ALooperRoster,所以,AMessage的操作最終還是讓ALooperRoster來接管了:

status_t ALooperRoster::postAndAwaitResponse(
        const sp<AMessage> &msg, sp<AMessage> *response) {
    /* 1.通過handler id 來找到註冊的looper */
    sp<ALooper> looper = findLooper(msg->target());

    if (looper == NULL) {
        ALOGW("failed to post message. "
                "Target handler %d still registered, but object gone.",
                msg->target());
        response->clear();
        return -ENOENT;
    }

    Mutex::Autolock autoLock(mLock);

    uint32_t replyID = mNextReplyID++;

    msg->setInt32("replyID", replyID);
	
	/* 2.調用Alooper將消息投遞出去 */
    looper->post(msg, 0 /* delayUs */);

    ssize_t index;
    while ((index = mReplies.indexOfKey(replyID)) < 0) {
        mRepliesCondition.wait(mLock);
    }

    *response = mReplies.valueAt(index);
    mReplies.removeItemsAt(index);

    return OK;
}

這個函數着重分析兩點,首先是通過handler找到對應的looper,因爲前面我們說過了,每個handler只能被註冊到一個looper中,所以,在前面的KeyedVector中通過搜尋鍵值對的方式可以找到looper,接下來,就是調用looper的post函數將消息投遞到對應的looper中了,注意區別,java層中的消息投遞,是由hanlder.sendMessage來完成的。
我們具體看下post函數:

void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
    Mutex::Autolock autoLock(mLock);
	/* 1.計算投遞時間 */
    int64_t whenUs;
    if (delayUs > 0) {
        whenUs = GetNowUs() + delayUs;
    } else {
        whenUs = GetNowUs();
    }

	/* 2.遍歷鏈表,找到一個系統時間大於該事件的時間 */
    List<Event>::iterator it = mEventQueue.begin();
    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
        ++it;
    }

	/* 3.將消息打包成event */
    Event event;
    event.mWhenUs = whenUs;
    event.mMessage = msg;

    if (it == mEventQueue.begin()) {
        mQueueChangedCondition.signal();
    }

	/* 4.插入到事件隊列中 */
    mEventQueue.insert(it, event);
}

post實現的邏輯很簡單,就是將事件打包成event之後找一個合適的位置插入到事件鏈表中;
消息發送端的操作就分析完了,通過創建message所在的hanlder id,找到對應的looper,調用looper的post將消息投遞出去
來看下接收端是何時建立並且如何處理消息的,還記得looper實例化之後調用的start函數嗎?裏面會去實例化一個thread:

...
    mThread = new LooperThread(this, canCallJava);

    status_t err = mThread->run(
            mName.empty() ? "ALooper" : mName.c_str(), priority);
    if (err != OK) {
        mThread.clear();
    }
...

LooperThread繼承的是Android的thread基類,其構造函數中傳入了looper對象:

    LooperThread(ALooper *looper, bool canCallJava)
        : Thread(canCallJava),
          mLooper(looper),
          mThreadId(NULL) {
    }

既然是繼承自thread基類,那麼必然覆寫threadLoop方法,因爲thread類中,threadLoop是一個純虛函數,這裏需要注意,threadLoop如果返回值爲true,則會一直循環,具體的代碼就不去分析了,扯了這麼多,我們看一下LooperThread中threadLoop乾的啥:

    virtual bool threadLoop() {
        return mLooper->loop();
    }

很簡單,就去調用了ALooper中的loop函數,也就是說,如果mLooper->loop()返回值爲true,那麼該函數一直循環,因此,消息的取出就是在這裏面做的了,看一下loop函數:

bool ALooper::loop() {
    Event event;

    {
        Mutex::Autolock autoLock(mLock);
        if (mThread == NULL && !mRunningLocally) {
            return false;
        }
        if (mEventQueue.empty()) {
            mQueueChangedCondition.wait(mLock);
            return true;
        }
        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
        int64_t nowUs = GetNowUs();

        if (whenUs > nowUs) {
            int64_t delayUs = whenUs - nowUs;
            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);

            return true;
        }

        event = *mEventQueue.begin();
        mEventQueue.erase(mEventQueue.begin());
    }
	/* 事件隊列不爲空,分發事件 */
    gLooperRoster.deliverMessage(event.mMessage);

    // NOTE: It's important to note that at this point our "ALooper" object
    // may no longer exist (its final reference may have gone away while
    // delivering the message). We have made sure, however, that loop()
    // won't be called again.

    return true;
}

果然代碼中充斥着true,還是需要用到熟悉的ALooper“小弟”ALooperRoster,直接看關鍵函數deliverMessage:

void ALooperRoster::deliverMessage(const sp<AMessage> &msg) {
    sp<AHandler> handler;

    {
        Mutex::Autolock autoLock(mLock);

        ssize_t index = mHandlers.indexOfKey(msg->target());

        if (index < 0) {
            ALOGW("failed to deliver message. Target handler not registered.");
            return;
        }

        const HandlerInfo &info = mHandlers.valueAt(index);
        handler = info.mHandler.promote();

        if (handler == NULL) {
            ALOGW("failed to deliver message. "
                 "Target handler %d registered, but object gone.",
                 msg->target());

            mHandlers.removeItemsAt(index);
            return;
        }
    }
	/* 調用對應的handle處理消息 */
    handler->onMessageReceived(msg);
}

終於看到關鍵點了,onMessageReceived,這下你知道爲什麼每一個繼承自AHandler的類中都要有onMessageReceived函數了吧,總算把消息投遞到了“收件人”那裏,至於start的消息具體幹了啥,我們就不分析了,主要分析消息收發的過程。

三、總結:
給一張圖總結一下JMediaCodec中三者間的關係:

在這裏插入圖片描述
①AMessage被封裝成Event進行收發;
②每個AHandler只能被註冊到一個ALooper中;
③Event投遞最終是通過ALooper來的;
④ALooper實際是看成了一個線程來運行的,AHandler與Event的協調工作是通過ALooperRoster來完成的;

⑤JMediaCodec和MediaCodec這兩個AHandler是註冊在JMediaCodec實例化的ALooper中,而ACodec這個AHandler是註冊在Mediacodec實例化多的ALooper中;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章