• ②NuPlayer播放框架之ALooper-AHandler-AMessage底层机制分析


    [时间:2016-09] [状态:Open]
    [关键词:android,NuPlayer,开源播放器,播放框架,ALooper,AHandler,AMessage]

    前文中提到过NuPlayer基于StagefrightPlayer的基础类构建,利用了更底层的ALooper/AHandler机制来异步地处理请求,ALooper保存消息请求,然后调用AHandler接口去处理。
    实际上在代码中NuPlayer本身继承自AHandler类,而ALooper对象保存在NuPlayerDriver中。
    ALooper/AHandler机制是模拟的消息循环处理方式,通常有三个主要部分:消息(message,通常包含Handler)、消息队列(queue)、消息处理线程(looper thread)。
    对于handler消息机制,构成就必须包括一个Loop,message。那么对应的AHandler,也应该有对应的ALooper、AMessage。
    因此本文主要涉及到三个类ALooper、AHandler、AMessage。

    1 AHandler接口分析(消息处理类)

    下面代码是AHandler接口:

    // code frome "./frameworks/av/include/media/stagefright/AHandler.h"
    struct AHandler : public RefBase {
        AHandler();
    
        ALooper::handler_id id() const;
        sp<ALooper> looper() const;
        wp<ALooper> getLooper() const;
        wp<AHandler> getHandler() const;
    
    protected:
        virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
    
    private:
        friend struct AMessage;      // deliverMessage()
        friend struct ALooperRoster; // setID()
    
        uint32_t mMessageCounter;
        KeyedVector<uint32_t, uint32_t> mMessages;
    
    	void setID(ALooper::handler_id id, wp<ALooper> looper);
    	void deliverMessage(const sp<AMessage> &msg);
    };
    

    看上面接口,初步印象是AHandler没有直接对外的接口(只有获取成员变量的接口),基本上只有一个onMessageReceived用于子类继承,deliverMessage用于给类AMessage使用,setID用于给友元类ALooperRoster使用。从这点来说,真正代码应该在AMessage里边。

    2 AMessage接口分析(消息载体)

    下面代码是AMessage的声明:

    // code from "./frameworks/av/include/media/stagefright/AMessage.h"
    struct AMessage : public RefBase {
        AMessage();
        AMessage(uint32_t what, const sp<const AHandler> &handler); // 代码中常用的构造函数
        static sp<AMessage> FromParcel(const Parcel &parcel, size_t maxNestingLevel = 255);
    
        // Write this AMessage to a parcel.
        // All items in the AMessage must have types that are recognized by
        // FromParcel(); otherwise, TRESPASS error will occur.
        void writeToParcel(Parcel *parcel) const;
    
        void setWhat(uint32_t what);
        uint32_t what() const;
    
    	// 注意这是一个AHandler,通过这个可以获得ALooper对象引用
        void setTarget(const sp<const AHandler> &handler);
    
    	// 清除所有设置的消息属性参数
        void clear();
    	
    	// 一系列设置/获取 Message 属性的函数。。。
        void setInt32/setInt64/setSize/setFloat/setDouble/setPointer/setPointer/setString/setRect/setObject/setBuffer/setMessage(...);
        bool findInt32/findInt64/findSize/findFloat/findDouble/findPointer/findString/findObject/findBuffer/findMessage/findRect(...) const;
    
    	// 通过这个函数检索下指定名称的消息属性是否存在
        bool contains(const char *name) const;
    
    	// 投递消息的接口,顾名思义直接投递给构造函数的ALooper,注意支持延时消息,但不支持提前消息,delayUS > 0
        status_t post(int64_t delayUs = 0);
    
        // 投递消息并等待执行结束后发送response消息
        status_t postAndAwaitResponse(sp<AMessage> *response);
    
        // If this returns true, the sender of this message is synchronously
        // awaiting a response and the reply token is consumed from the message
        // and stored into replyID. The reply token must be used to send the response
        // using "postReply" below.
        bool senderAwaitsResponse(sp<AReplyToken> *replyID);
    
        // Posts the message as a response to a reply token.  A reply token can
        // only be used once. Returns OK if the response could be posted; otherwise,
        // an error.
        status_t postReply(const sp<AReplyToken> &replyID);
    
        // 深拷贝
        sp<AMessage> dup() const;
    
        // 比较两个消息,并返回差异
        sp<AMessage> changesFrom(const sp<const AMessage> &other, bool deep = false) 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;
    
        wp<AHandler> mHandler;
        wp<ALooper> mLooper;
    	
    	// 用于ALooper调用的,发送消息的接口
        void deliver();
    };
    

    从上面的接口可以看出在使用AMessage是只需要指定消息的id和要处理该消息的AHandler即可,可以通过构造函数,也可以单独调用setWhat和setTarget接口。AMessage构造完成之后,可以调用setXXX设置对应的参数,通过findXXX获取传递的参数。最后通过post即可将消息投递到AHandler的消息队列中。

    3 ALooper接口分析(消息处理循环及后台线程)

    其简化的声明如下:

    // code from "./frameworks/av/include/media/stagefright/ALooper.h"
    struct ALooper : public RefBase {
        ALooper();
    
        // Takes effect in a subsequent call to start().
        void setName(const char *name);
    	const char *getName() const;
    
        handler_id registerHandler(const sp<AHandler> &handler);
        void unregisterHandler(handler_id handlerID);
    
        status_t start(bool runOnCallingThread = false,
                bool canCallJava = false, int32_t priority = PRIORITY_DEFAULT);
    
        status_t stop();
    
        static int64_t GetNowUs();    
    
    protected:
        virtual ~ALooper();
    
    private:
        friend struct AMessage;       // post()
    
    	AString mName;
    
        struct Event {
            int64_t mWhenUs;
            sp<AMessage> mMessage;
        };
        List<Event> mEventQueue;
    
        struct LooperThread;
        sp<LooperThread> mThread;
        bool mRunningLocally;
    
        // START --- methods used only by AMessage
    
        // posts a message on this looper with the given timeout
        void post(const sp<AMessage> &msg, int64_t delayUs);
    
        // creates a reply token to be used with this looper
        sp<AReplyToken> createReplyToken();
        // waits for a response for the reply token.  If status is OK, the response
        // is stored into the supplied variable.  Otherwise, it is unchanged.
        status_t awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response);
        // posts a reply for a reply token.  If the reply could be successfully posted,
        // it returns OK. Otherwise, it returns an error value.
        status_t postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &msg);
    
        // END --- methods used only by AMessage
    
        bool loop();
    };
    

    ALooper对外接口比较简单,通常就是NuPlayerDriver构造函数中的调用逻辑。先创建一个ALooper对象,然后调用setName和start接口,之后调用registerHandler设置一个AHandler,这样就完成了初始化。在析构之前需要调用stop接口。
    这里需要说明下,ALooper::start接口会启动一个线程,并调用ALooper::loop函数,该函数主要实现消息的实际执行。代码如下:

    bool ALooper::loop() {
        Event event;
    
        {
            Mutex::Autolock autoLock(mLock);
            if (mThread == NULL && !mRunningLocally) {
                return false;
            }
    
    		// 从mEventQueue取出消息,判断是否需要执行,不需要的话就等待
    		// 需要的话就调用handler执行,并删除对应消息
            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();
    
        return true;
    }
    

    那么消息是通过那个函数添加进来的呢? 这就是友元类AMessage的作用,通过调用ALooper::post接口,将AMessage添加到mEventQueue中。

    4 一个调用实例

    以NuPlayer::setVideoSurfaceTextureAsync为示例分析下ALooper/AHandler机制。
    这里不解释ALooper的初始化过程,有兴趣的可以参考资料Android Native层异步消息处理框架的内容。
    下面是setVideoSurfaceTextureAsync的代码。

    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();
    }
    

    这段代码功能很简单,创建一个AMessage对象,并设置下参数,参数类型为Object,名称是"surface",然后通过AMessage::post接口,间接调用ALooper::post接口,将消息发送给ALooper-NuPlayerDriver::mLooper;ALooper的消息循环线程检测到这个消息,在ALooper::loop函数中通过AMessage的deliver接口,调用AHandler::deliverMessage接口,这个函数会调动NuPlayer::onMessageReceived(通过继承机制实现)接口。这样绕了一圈。我们就可以通过ALooper/AHandler机制处理消息了。
    具体处理代码如下

    void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
        switch (msg->what()) {
    		case kWhatSetVideoSurface:
            {
    
                sp<RefBase> obj;
                CHECK(msg->findObject("surface", &obj));
                sp<Surface> surface = static_cast<Surface *>(obj.get());
    
                ALOGD("onSetVideoSurface(%p video decoder)", surface.get());
    
                // Need to check mStarted before calling mSource->getFormat because NuPlayer might
                // be in preparing state and it could take long time.
                // When mStarted is true, mSource must have been set.
                if (mSource == NULL || !mStarted || mSource->getFormat(false /* audio */) == NULL
                        // NOTE: mVideoDecoder's mSurface is always non-null
                        || (mVideoDecoder != NULL && mVideoDecoder->setVideoSurface(surface) == OK)) {
                    performSetSurface(surface);
                    break;
                }
    
    		}
    		
    		// ... 省略其他部分代码
    	}
    }
    

    总结

    本文主要介绍了NuPlayer中ALooper、AHandler、AMessage三个类直接的关系及代码结构,并以NuPlayer::setVideoSurfaceTextureAsync接口的实现为例说明。
    主要是作为后续深入分析NuPlayer内部机制做一个基础知识介绍。

    参考资料

    1. AOSP 7.0
    2. Android异步消息框架
  • 相关阅读:
    [算法说明]SAP HANA PAL 指数平滑算法说明 沧海
    [Step By Step]SAP HANA PAL演绎推理算法Lite Aprior实现LITEAPRIORIRULE 沧海
    [PAL规范]SAP HANA PAL K分值硬聚类算法KMeans编程规范KMEANS 沧海
    [Step By Step]SAP HANA PAL K分值硬聚类算法KMeans Validated实现案例VALIDATEKMEANS 沧海
    SAP HANA AFL插件库升级后之前生成的存储过程升级方法(PAL升级方法) 沧海
    [PAL规范]SAP HANA PAL单指数平滑编程规范 沧海
    [PAL规范]SAP HANA PAL演绎推理算法Lite Apriori编程规范LITEAPRIORIRULE 沧海
    [Step By Step]SAP HANA PAL K分值硬聚类算法KMeans实现KMEANS 沧海
    [Step By Step]SAP HANA PAL Time Series单指数平滑算法SINGLESMOOTH(Single Exponential Smoothing) 沧海
    [PAL规范]SAP HANA PAL K分值硬聚类算法KMeans Validated编程规范VALIDATEKMEANS 沧海
  • 原文地址:https://www.cnblogs.com/tocy/p/2-nuplayer-alooper-ahandler-amessage-intro.html
Copyright © 2020-2023  润新知