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中;

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值