先來看整個框架圖:
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操作執行完畢後,直接把畫面切換到這個位置開始播放,這樣的話,畫面不會卡住,而用戶體驗度就會好很多。