一、 前言:
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
二、示例分析:
- 消息機制的建立:
消息機制的建立首先是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中;