android 多媒体 notifyListener_l 函数分析

今天看了看多媒体的框架,在跟踪代码时跟踪到notifyListener_l()这个函数里面的Listener->sendEvent(msg, ext1, ext2);函数时,就跟踪不下去了,不知道这个函数最后调用的函数在那。通过查找代码最后确定,Listener->sendEvent(msg, ext1, ext2);就是一个回调函数,最终调用到的函数在MediaPlayer.cpp文件里的notify()函数,找到后感觉这个回调函数设计的非常巧妙,于是整理了一下思路,把它记录下来。可以为以后的开发做一个参考。

先从 notifyListener_l ()这个函数开始,首先看一下这个函数的定义:(函数所在的文件frameworks/av/meida/libstagefright/AwesomaPlayer.cpp)

void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
    if (mListener != NULL) {
        sp<MediaPlayerBase> listener = mListener.promote();

        if (listener != NULL) {
            listener->sendEvent(msg, ext1, ext2);
        }
    }
}
函数首先是定义一个MediaPlayerBase的指针 listener 并有MListener.promote();进行初始化。那么先看一下mListener.promote()这个函数,跟踪这个函数会看到这个函数其实就是一个函数模版。函数定义如下:(函数所在的文件frameowrks/native/include/utils/RefBase.h)

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}
函数就是返回一个T类型的指针,那么T类型是什么类型呢,那就要看mListener这个变量,这个变量在frameworks/av/meida/libstagefright/include/AwesomaPlayer.h里定义

wp<MediaPlayerBase> mListener;
可以看到这个变量是MediaPlayerBase类类型的对象。这个对象的赋值是在setListener函数里,看一下这个函数的定义(函数在frameworks/av/meida/libstagefright/AwesomaPlayer.cpp)

void AwesomePlayer::setListener(const wp<MediaPlayerBase> &listener) {
    Mutex::Autolock autoLock(mLock);
    mListener = listener;
}
setListener()这个函数只是对mListener这个变量赋值,没有什么实际价值,无法确定mListener这个变量真正的对象是那个。既然这个函数无法得到更多的信息,那来看一下这个函数的调用者,看看能不能从这个函数的调用这里得到需要的信息。直接看这个函数的直接调用者,函数原型(函数在frameworks/av/media/libmediaplayerservice/StagefrightPlayer.cpp)

StagefrightPlayer::StagefrightPlayer()
    : mPlayer(new AwesomePlayer) {
    ALOGV("StagefrightPlayer");

    mPlayer->setListener(this);
}
从这个函数可以看到setListener这个函数是在StagefrightPlayer类的构造函数里调用的,而且setListener(this)函数的参数就this,this在这里的类型肯定就是StagefrightPlayer类类型了,那么我们就能确定mListener这个对象最终的类型了,但是mListener的类型是MediaPlayerBase类类型对象,而this是StagefrightPlayer类类型对象,二者好像联系不到一起,接下来我们看一下StagefrightPlayer的继承关系就能知道了


是不是很清楚了,mListener是MediaPlayerBase类类型对象,this是StagefrightPlayer类类型的对象,MediaPlayerBase是StagefrightPlayer的基类。

确定了mListener的类型就能确定promote这个模版函数T的类型了,那么listener->sendEvent(msg, ext1, ext2);的函数调用就是调用MediaPlayerBase类MediaPlayerInterface类和StagefrightPlayer类这个三个类中的一个sendEvent()函数了,通过察看这三个类声明我们可以知道只有MediaPlayerBase这个类里有sendEvent()这个函数,那么

listener->sendEvent()这个函数调用肯定就是MediaPlayerBase类中的sendEvnet()函数了,来看一下这个函数的函数定义

(函数在frameworks/av/include/media/MediaPlayerInterface.h)注意mCookie变量后面会用到

 void  sendEvent(int msg, int ext1=0, int ext2=0,
                          const Parcel *obj=NULL) {
        Mutex::Autolock autoLock(mNotifyLock);
        if (mNotify) mNotify(mCookie, msg, ext1, ext2, obj);
    }

通过这个函数的定义我们可以知道最终会调用mNotify,看上去mNotify像是一个对象而不像是函数,那么就看一下mNotify的定义。

notify_callback_f   mNotify;
而 notify_callback_f  是一个有typedef声明的一个函数指针。到此我们可以确定notifyListener_l()函数是通过sendEvnet()这个函数通过一个函数指针来完成一个函数回调。

既然mNotify是一个函数指针,那么接下来我们就要确定mNotify这个函数指针是怎么被赋值的。

跟踪代码我们可以看到在mNotify是在setNotifyCallback()这个函数里被赋值的。看一下setNotifyCallback()这个函数的定义;注意这个函数里的cookie变量后面会用到

(函数在frameworks/av/include/media/MediaPlayerInterface.h)

    void  setNotifyCallback(
            void* cookie, notify_callback_f notifyFunc) {
        Mutex::Autolock autoLock(mNotifyLock);
        mCookie = cookie; mNotify = notifyFunc;
    }

既然是有setNotifyCallback()这个函数赋值的,那么我们就要跟踪这个函数,看看是谁调用的它。同样跟踪代码setNotifyCallback()是在createPlayer()函数里调用的。

还是先看一下这个函数的原型(函数在frameworks/av/media/libmediaplayerserver/MediaPlayerService.cpp),只把函数的关键部分列了出来。

注意这个函数里的cookie变量后面会用到

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
        notify_callback_f notifyFunc)
{
    sp<MediaPlayerBase> p;
    switch (playerType) {
        ...
        case STAGEFRIGHT_PLAYER:
            ALOGV(" create StagefrightPlayer");
         (1)p = new StagefrightPlayer;
            break;
        case NU_PLAYER:
            ALOGV(" create NuPlayer");
            p = new NuPlayerDriver;
            break;
         ...
    }
    if (p != NULL) {
        if (p->initCheck() == NO_ERROR) {
          (2) p->setNotifyCallback(cookie, notifyFunc);
        } else {
            p.clear();
        }
    }
    if (p == NULL) {
        ALOGE("Failed to create player object");
    }
    return p;
}

看到标有(2)的部分setNotifyCallback()函数调用。指针p看标有(1)的部分,通过前面我们分析的那几个类的继承关系,知道是怎么调用到MediaPlayerInterface类里的setNotifyCallback()这个函数的了吧。notifyFunc就是要赋给mNotifu这个函数指针变量的值,但notifyFunc也只是createPlayer()这个函数的参数而已,我们也看不是什么门头来,那我们就继续看谁调用了createPlayer()这个函数,跟踪代码我们同样看到是另一个createPlayer()函数调用的,同样我们来看一下这个createPlayer()的函数原型(函数在frameworks/av/media/libmediaplayerserver/MediaPlayerService.cpp) 注意这个函数里的this变量

sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    // determine if we have the right player type
    sp<MediaPlayerBase> p = mPlayer;
    if ((p != NULL) && (p->playerType() != playerType)) {
        ALOGV("delete player");
        p.clear();
    }
    if (p == NULL) {
        p = android::createPlayer(playerType, this, notify);
    }

    if (p != NULL) {
        p->setUID(mUID);
    }

    return p;
}

 notify就是我们最终要找的,mNotify这个函数指针最终指向的就是notify这个函数。这里直接用函数名notify用作函数的参数来用了,notify这个函数就是class Client 这个类里的notify,接下来看一下这个函数的函数定义(函数在frameworks/av/media/libmediaplayerserver/MediaPlayerService.cpp)  只把函数的关键部分列了出来

void MediaPlayerService::Client::notify(
        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
    Client* client = static_cast<Client*>(cookie);

    ...
    ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
  (1)  client->mClient->notify(msg, ext1, ext2, obj);
}

首先看变量cookie,根据前面的注明能知道cookie是怎么来的吧,看标有(1)的部分 client指向mClient,看mClient的定义

sp<IMediaPlayerClient>      mClient;
知道mClient定义可以知道红色字体最终要调用的是那里的函数,mClinet是IMediaPlaerClient类型的指针,看到IXXX就可以知道这个类是一个接口类,先看一下下面的图吧:

图中只画出了关键的继承部分

图1:


图2:


很熟悉把,android中的binder机制,实现进程间的通信,那么client->mClient->notify(msg, ext1, ext2, obj);就将会调用到IMediaPlayerClient.cpp文件中的notify()函数,然后通过binder的机制,最终就会掉用到MediaPlayer.cpp文件中的notify()这个函数了。到此分析结束。这个分析过程都是逆向分析过来的,从后向前一点点的跟踪函数调用。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值