7. Android MultiMedia框架完全解析 - ALooper-AHandler-AMessage機制分析

先來看整個框架圖:

NuPlayer基於Stagefright的基礎類構建,使用了更底層的ALooper-AHandler-AMessage機制來異步處理消息。

AMessage作爲一個消息載體,保存這與這個消息有關的信息;

ALooper是一個循環,它運行着一個後臺線程,來循環處理接收到的消息(將信息轉給AHandler來處理,它相當於一箇中轉站);

AHandler作爲一個句柄,它是最終對消息進行處理的。

在實現上NuPlayer和Awesomeplayer不同,NuPlayer基於StagefrightPlayer的基礎類構建,利用了更底層的ALooper/AHandler機制來異步地處理請求,ALooper列隊消息請求,AHandler中去處理,所以有更少的Mutex/Lock在NuPlayer中。Awesomeplayer中利用了omxcodec,而NuPlayer中利用了Acodec。

首先需要分析AHandler,因爲在Amessage::setTarget函數中,需要使用AHandler中的const_cast<AHandler *>(this)和mLooper,而這兩個值需要在AHandler中初始化。

 

1. AHandler

先來看AHandler的定義:

android_m6.0.1_2.1.0/frameworks/av/include/media/stagefright/foundation/AHandler.h)

struct AHandler : public RefBase {
    AHandler()
        : mID(0),
          mVerboseStats(false),
          mMessageCounter(0) {
    }//構造函數,爲mID,mVerboseStats,mMessageCounter三個變量賦初始值。

    ALooper::handler_id id() const {
        return mID;
    }

    sp<ALooper> looper() const {
        return mLooper.promote();
    }

    wp<ALooper> getLooper() const {
        return mLooper;
    }

    wp<AHandler> getHandler() const {
        // allow getting a weak reference to a const handler
        return const_cast<AHandler *>(this);
    }

protected:
    virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
//這是個虛函數,每個繼承自AHandler的子類都需要實現這個虛函數,比如在NuPlayer中就實現了這個虛函數,最終對於Message的處理還是在這個函數中。

private:
    friend struct AMessage;      // deliverMessage()
    friend struct ALooperRoster; // setID()

    ALooper::handler_id mID;
    wp<ALooper> mLooper;

    inline void setID(ALooper::handler_id id, wp<ALooper> looper) {
        mID = id;
        mLooper = looper;
    }//這個函數供友元類ALooperRoster來使用。

    bool mVerboseStats;
    uint32_t mMessageCounter;
    KeyedVector<uint32_t, uint32_t> mMessages;
//這裏是一個KeyedVector,用來存放Messages,它是一個鍵值對類型的容器。

    void deliverMessage(const sp<AMessage> &msg);
//這個函數是AHandler.cpp需要去實現的一個函數,在裏面調用onMessageReceived(msg)函數後再對mMessageCounter進行操作。這個函數是在AMessage::deliver()函數中調用的。

    DISALLOW_EVIL_CONSTRUCTORS(AHandler);
};

從上面的接口來看,初步印象是AHandler沒有直接對外的接口(只有獲取成員變量的接口),基本上只有一個onMessageReceived用於子類的繼承,deliverMessage用於給類AMessage使用,setID用於給友元類ALooperRoster來使用。所以從這點來說,真正的代碼可能在AMessage中。

 

那這個AHandler是給誰用的呢?從名字上來看,它是一個句柄,同時也是這個ALooper-AHandler-AMessage機制的一個核心,所以如果想要在某個程序中使用這個消息機制,就要讓主體繼承自這個AHandler,然後在主體中實現onMessageReceived函數,同時也就可以使用AMessage了。

比如在Media playback中,這個主體就是NuPlayer,所以NuPlayer繼承自AHanlder。

 

 

1.2 ALooperRoster類

從AHandler中setID函數的實現上面來看,是爲AHandler中的的mID,mLooper兩個成員賦初始值,而調用這個setID函數的正是ALooperRoster::registerHandler()和ALooperRoster::unregisterHandler()這兩個函數,同時Roster的翻譯就是花名冊,所以,可以理解爲ALooperRoster類來將ALooper和AHandler類關聯起來,它是用來管理ALooper的。

 

 

2. AMessage類

.\libstagefright\foundation\AMessage.cpp
.\media\stagefright\foundation\AMessage.h

struct AMessage : public RefBase {
    AMessage();
    AMessage(uint32_t what, const sp<const AHandler> &handler);
//兩個構造函數,但是第二個是常用的構造函數,通常指定id和需要哪個AHanlder來處理。

    static sp<AMessage> FromParcel(const Parcel &parcel);
    void writeToParcel(Parcel *parcel) const;

    void setWhat(uint32_t what);
    uint32_t what() const;
//如果不通過構造函數傳入id的話,也可以通過這兩個設置。

    void setTarget(const sp<const AHandler> &handler);
//這個函數還是蠻重要的,通過這個函數,可以爲這個類中的mTarget,mHandler,mLooper賦值,這個函數通常會被這樣調用:msg->setTarget(this);通過這一步,就可以爲msg設置Handler和Looper。注意這裏本身傳入的就是一個AHandler。

    void clear();

    void setInt32(const char *name, int32_t value);
    void setInt64(const char *name, int64_t value);
    void setSize(const char *name, size_t value);
    void setFloat(const char *name, float value);
    void setDouble(const char *name, double value);
    void setPointer(const char *name, void *value);
    void setString(const char *name, const char *s, ssize_t len = -1);
    void setString(const char *name, const AString &s);
    void setObject(const char *name, const sp<RefBase> &obj);
    void setBuffer(const char *name, const sp<ABuffer> &buffer);
    void setMessage(const char *name, const sp<AMessage> &obj);

    void setRect(
            const char *name,
            int32_t left, int32_t top, int32_t right, int32_t bottom);

    bool contains(const char *name) const;

    bool findInt32(const char *name, int32_t *value) const;
    bool findInt64(const char *name, int64_t *value) const;
    bool findSize(const char *name, size_t *value) const;
    bool findFloat(const char *name, float *value) const;
    bool findDouble(const char *name, double *value) const;
    bool findPointer(const char *name, void **value) const;
    bool findString(const char *name, AString *value) const;
    bool findObject(const char *name, sp<RefBase> *obj) const;
    bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
    bool findMessage(const char *name, sp<AMessage> *obj) const;

    bool findRect(
            const char *name,
            int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;

    status_t post(int64_t delayUs = 0);
//投遞消息,新建一個消息後,設置屬性,就可以調用post投遞了。

    status_t postAndAwaitResponse(sp<AMessage> *response);

    bool senderAwaitsResponse(sp<AReplyToken> *replyID);

    status_t postReply(const sp<AReplyToken> &replyID);

    sp<AMessage> dup() const;

    size_t countEntries() const;
    const char *getEntryNameAt(size_t index, Type *type) const;

protected:
    virtual ~AMessage();

private:
    friend struct ALooper; // deliver()

    uint32_t mWhat;

    // used only for debugging
    ALooper::handler_id mTarget;

    wp<AHandler> mHandler;
    wp<ALooper> mLooper;

    Item *allocateItem(const char *name);
    void freeItemValue(Item *item);
    const Item *findItem(const char *name, Type type) const;

    void setObjectInternal(
            const char *name, const sp<RefBase> &obj, Type type);

    size_t findItemIndex(const char *name, size_t len) const;

    void deliver();//這個接口給ALooper調用,發送消息的接口。

    DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};

從上面的接口可以看出,在使用AMessage時只需要指定消息的id和要處理該消息的AHandler即可,可以通過AMessage的構造函數中指定,也可以單獨調用setWhat和setTarget接口。AMessage構造完成之後,可以調用setxxx設置對應的參數,通過findxxx獲取傳遞的參數。最後通過post即可將消息投遞到AHandler的消息隊列中。

 

下面就是一個消息從創建到發送的過程:

    sp<AMessage> notify = dupNotify();
    notify->setInt32("what", kWhatPrepared);
    notify->setInt32("err", err);
    notify->post();

3. ALooper

(這裏插入ALooper類)

ALooper的對外接口比較簡單,通常就是NuPlayerDriver構造函數中的調用邏輯,先創建一個ALooper對象,然後調用setName和start接口,之後調用registerHandler設置一個Handler,這樣就完成了初始化。

創建過程

mLooper(new ALooper),
mLooper->setName("NuPlayerDriver Looper");

mLooper->start(
            false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO);

mLooper->registerHandler(mPlayer);

在ALooper::start函數中會啓動一個線程,並調用ALooper::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());
    }

    event.mMessage->deliver();

    // 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;
}

說一下這裏的邏輯:首先ALooper中維護着一個Event鏈表,List<Event> mEventQueue;這個Event結構體中包含一個AMessage消息和一個時間值,在ALooper::loop()函數中會一直監聽着這個鏈表,如果有一個Event的話,就從鏈表中取出Event,調用event.mMessage->deliver()函數。

在AMessage::deliver()函數中,就會去獲取這個AMessage的Ahandler,然後調用對應AHandler的deliverMessage函數:handler->deliverMessage(this)。

在AHandler::deliverMessage(const sp<AMessage> &msg)函數中,就會調用AHandler中的onMessageReceived(msg)函數,如果這時候傳入的是AHandler子類的話,就會去調用AHandler子類的onMessageReceived(msg)函數。

4. 實例

void NuPlayer::setVideoSurfaceTextureAsync(
        const sp<IGraphicBufferProducer> &bufferProducer) {
    sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);

    if (bufferProducer == NULL) {
        msg->setObject("surface", NULL);
    } else {
        msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));
    }

    msg->post();
}

5. 上面這些消息都是異步實現的,如果想要同步消息,在這個消息機制中的設計很巧妙,它是新建一個AMessage,然後通過這個新建的AMessage來傳輸回覆的消息,實例如下:

status_t NuPlayer::getPlaybackSettings(AudioPlaybackRate *rate /* nonnull */) {
    sp<AMessage> msg = new AMessage(kWhatGetPlaybackSettings, this);
    sp<AMessage> response;
    status_t err = msg->postAndAwaitResponse(&response);
    if (err == OK && response != NULL) {
        CHECK(response->findInt32("err", &err));
        if (err == OK) {
            readFromAMessage(response, rate);
        }
    }
    return err;
}

6.

sp<AReplyToken> replyID;

CHECK(msg->senderAwaitsResponse(&replyID));

 

7.

sp<AMessage> response = new AMessage;

response->setInt32("err", err);

 

response->postReply(replyID);

 

 

8. msg中findObject和setObject的用法:

void NuPlayer::setVideoSurfaceTextureAsync(

const sp<IGraphicBufferProducer> &bufferProducer) {

sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, this);

 

if (bufferProducer == NULL) {

msg->setObject("surface", NULL);

} else {

msg->setObject("surface", new Surface(bufferProducer, true /* controlledByApp */));

}

 

msg->post();

}

之後在OnMessageReceived函數中:

case kWhatSetVideoSurface:

{

 

sp<RefBase> obj;

CHECK(msg->findObject("surface", &obj));

sp<Surface> surface = static_cast<Surface *>(obj.get());

這裏通過一個static_cast轉換,就能夠把這個sp<Surface>結構體通過msg傳遞過來。

 

 

 

 

最終總結一句:很多同學到這裏就不理解了,爲什麼這裏要使用這麼複雜的機制,直接在函數中把這些操作直接順序寫下來不就行了?因爲在Media相關的地方,很多操作都是耗時很長的操作,但是用戶對於畫面的流程度的容忍度很低,舉一個很簡單的例子,在視頻的播放過程中,如果我想直接把進度條拖到中間的位置開始播放,當我點擊完畢後,NuPlayer內部就開始執行了seek操作,但是這時候耗時比較長,畫面就卡在這裏了,用戶體驗度很差。但是如果我使用類似的機制,畫面繼續播放之前緩衝的數據,而NuPlayer抓緊去執行seek操作,當seek操作執行完畢後,直接把畫面切換到這個位置開始播放,這樣的話,畫面不會卡住,而用戶體驗度就會好很多。

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