一、 前言:
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
二、示例分析:
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中;