MediaPlayer代码分析(2)-处理返回机制Notify

来源:互联网 发布:java周末班 编辑:程序博客网 时间:2024/05/16 11:21

在各层处理消息时都是使用notify将处理的信息返回的。各层都对下一层注册了notify函数。

Java层是处理返回给应用层的消息,postEventFromNative

    private static void postEventFromNative(Object mediaplayer_ref,                                            int what, int arg1, int arg2, Object obj)    {        MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();        if (mp == null) {            return;        }        if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {            // this acquires the wakelock if needed, and sets the client side state            mp.start();        }        if (mp.mEventHandler != null) {            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);            mp.mEventHandler.sendMessage(m);        }    }
之后将一些消息扔给EventHandler处理。有些是调用app注册的回调函数,如onPrepared,onCompletion,onBufferingUpdate,onSeekComplete等。

JNI层首先定义了各种Java层的本地变量和回调函数,MediaPlayer的Java层回调函数使用post_event:

struct fields_t {    jfieldID    context;    jfieldID    surface_texture;    jmethodID   post_event;    jmethodID   proxyConfigGetHost;    jmethodID   proxyConfigGetPort;    jmethodID   proxyConfigGetExclusionList;};static fields_t fields;
获取post_event的代码:

    jclass clazz;    clazz = env->FindClass("android/media/MediaPlayer");    if (clazz == NULL) {        return;    }    。。。。。。    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");    if (fields.post_event == NULL) {        return;    }
那么在什么地方调用它呢?当然是在jni层提供给下层的notify中了

void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj){    JNIEnv *env = AndroidRuntime::getJNIEnv();    if (obj && obj->dataSize() > 0) {        jobject jParcel = createJavaParcelObject(env);        if (jParcel != NULL) {            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);            nativeParcel->setData(obj->data(), obj->dataSize());            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,                    msg, ext1, ext2, jParcel);            env->DeleteLocalRef(jParcel);        }    } else {        env->CallStaticVoidMethod(mClass, <strong>fields.post_event</strong>, mObject,                msg, ext1, ext2, NULL);    }    if (env->ExceptionCheck()) {        ALOGW("An exception occurred while notifying an event.");        LOGW_EX(env);        env->ExceptionClear();    }}
下层注册回调的地方是

static voidandroid_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){    ALOGV("native_setup");    sp<MediaPlayer> mp = new MediaPlayer();    if (mp == NULL) {        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");        return;    }    // create new listener and give it to MediaPlayer    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);    <strong>mp->setListener(listener)</strong>;    // Stow our new C++ MediaPlayer in an opaque field in the Java object.    setMediaPlayer(env, thiz, mp);}
继续看MediaPlayer层的setListener

status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener){    ALOGV("setListener");    Mutex::Autolock _l(mLock);    mListener = listener;    return NO_ERROR;}
这层就是将JNI层的JNIMediaPlayerListener赋值给mListener。那么何时调用notify呢?

在MediaPlayer给下层的notify中找到了它,同时我们也找到了MediaPlayer给Stagefright层注册的notify回调

void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj){    ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);    bool send = true;    bool locked = false;    // TODO: In the future, we might be on the same thread if the app is    // running in the same process as the media server. In that case,    // this will deadlock.    //    // The threadId hack below works around this for the care of prepare    // and seekTo within the same process.    // FIXME: Remember, this is a hack, it's not even a hack that is applied    // consistently for all use-cases, this needs to be revisited.    if (mLockThreadId != getThreadId()) {        mLock.lock();        locked = true;    }    // Allows calls from JNI in idle state to notify errors    if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {        ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);        if (locked) mLock.unlock();   // release the lock when done.        return;    }    switch (msg) {    case MEDIA_NOP: // interface test message        break;    case MEDIA_PREPARED:        ALOGV("prepared");        mCurrentState = MEDIA_PLAYER_PREPARED;        if (mPrepareSync) {            ALOGV("signal application thread");            mPrepareSync = false;            mPrepareStatus = NO_ERROR;            mSignal.signal();        }        break;    case MEDIA_PLAYBACK_COMPLETE:        ALOGV("playback complete");        if (mCurrentState == MEDIA_PLAYER_IDLE) {            ALOGE("playback complete in idle state");        }        if (!mLoop) {            mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;        }        break;    case MEDIA_ERROR:        // Always log errors.        // ext1: Media framework error code.        // ext2: Implementation dependant error code.        ALOGE("error (%d, %d)", ext1, ext2);        mCurrentState = MEDIA_PLAYER_STATE_ERROR;        if (mPrepareSync)        {            ALOGV("signal application thread");            mPrepareSync = false;            mPrepareStatus = ext1;            mSignal.signal();            send = false;        }        break;    case MEDIA_INFO:        // ext1: Media framework error code.        // ext2: Implementation dependant error code.        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {            ALOGW("info/warning (%d, %d)", ext1, ext2);        }        break;    case MEDIA_SEEK_COMPLETE:        ALOGV("Received seek complete");        if (mSeekPosition != mCurrentPosition) {            ALOGV("Executing queued seekTo(%d)", mSeekPosition);            mSeekPosition = -1;            seekTo_l(mCurrentPosition);        }        else {            ALOGV("All seeks complete - return to regularly scheduled program");            mCurrentPosition = mSeekPosition = -1;        }        break;    case MEDIA_BUFFERING_UPDATE:        ALOGV("buffering %d", ext1);        break;    case MEDIA_SET_VIDEO_SIZE:        ALOGV("New video size %d x %d", ext1, ext2);        mVideoWidth = ext1;        mVideoHeight = ext2;        break;    case MEDIA_TIMED_TEXT:        ALOGV("Received timed text message");        break;    case MEDIA_SUBTITLE_DATA:        ALOGV("Received subtitle data message");        break;    default:        ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);        break;    }    sp<MediaPlayerListener> <strong>listener = mListene</strong>r;    if (locked) mLock.unlock();    // this prevents re-entrant calls into client code    if ((listener != 0) && send) {        Mutex::Autolock _l(mNotifyLock);        ALOGV("callback application");        <strong>listener->notify</strong>(msg, ext1, ext2, obj);        ALOGV("back from callback");    }}
根据前一篇的分析,MediaPlayer是一个BpMediaPlayer,而调用它的notify的函数肯定在BnMediaPlayer里。BnMediaPlayer是MediaPlayer::Client

那么BnMediaPlayer的notify函数里肯定有BpMediaPlayer的notify,继续寻找:

void MediaPlayerService::Client::notify(        void* cookie, int msg, int ext1, int ext2, const Parcel *obj){    Client* client = static_cast<Client*>(cookie);    if (client == NULL) {        return;    }    sp<IMediaPlayerClient> c;    {        Mutex::Autolock l(client->mLock);        <strong>c = client->mClient</strong>;        if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {            if (client->mAudioOutput != NULL)                client->mAudioOutput->switchToNextOutput();            client->mNextClient->start();            client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj);        }    }    if (MEDIA_INFO == msg &&        MEDIA_INFO_METADATA_UPDATE == ext1) {        const media::Metadata::Type metadata_type = ext2;        if(client->shouldDropMetadata(metadata_type)) {            return;        }        // Update the list of metadata that have changed. getMetadata        // also access mMetadataUpdated and clears it.        client->addNewMetadataUpdate(metadata_type);    }    if (c != NULL) {        ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);        <strong>c->notify</strong>(msg, ext1, ext2, obj);    }}

上面的c代表BpMediaPlayer对象。

这样,MediaPlayerService::Client::notify就是BnMediaPlayer的通知函数了。接着找调用它的位置。

再往下找根据前一篇的分析,肯定是要到AwesomePlayer里找,但是我们只在AwesomePlayer里找到了AwesomePlayer::notifyListener_l。

那么究竟是从什么地方注册了MediaPlayerService::Client::notify,又是从什么地方调用它的呢?继续找

我们按照前一篇的思路,从create函数开始找。

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 = MediaPlayerFactory::createPlayer(playerType, this, <strong><span style="color:#ff0000;">notify</span></strong>);    }    if (p != NULL) {        p->setUID(mUID);    }    return p;}
一下就找到了MediaPlayerService::Client::notify,继续往下找

sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(        player_type playerType,        void* cookie,        notify_callback_f <strong>notifyFunc</strong>) {    sp<MediaPlayerBase> p;    IFactory* factory;    status_t init_result;    Mutex::Autolock lock_(&sLock);    if (sFactoryMap.indexOfKey(playerType) < 0) {        ALOGE("Failed to create player object of type %d, no registered"              " factory", playerType);        return p;    }    factory = sFactoryMap.valueFor(playerType);    CHECK(NULL != factory);    p = factory->createPlayer();    if (p == NULL) {        ALOGE("Failed to create player object of type %d, create failed",               playerType);        return p;    }    init_result = p->initCheck();    if (init_result == NO_ERROR) {        <strong><span style="color:#ff0000;">p->setNotifyCallback(cookie, notifyFunc)</span></strong>;    } else {        ALOGE("Failed to create player object of type %d, initCheck failed"              " (res = %d)", playerType, init_result);        p.clear();    }    return p;}
变量p是MediaPlayerBase类的对象,那么setNotifyCallback就是将MediaPlayerService::Client::notify注册给了它。

我们来看看setNotifyCallback函数,很简单,就是赋值

    void        setNotifyCallback(            void* cookie, notify_callback_f notifyFunc) {        Mutex::Autolock autoLock(mNotifyLock);        mCookie = cookie; mNotify = notifyFunc;    }
终于在MediaPlayerBase类里找到了notify赋值的地方,那么对应的就应该有调用的地方。

没错,就在下面的sendEvent

    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);    }
而这个sendEvent就是在AwesomePlayer::notifyListener_l里调用的:

void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {    if ((mListener != NULL) && !mAudioTearDown) {        sp<MediaPlayerBase> listener = mListener.promote();        if (listener != NULL) {            listener->sendEvent(msg, ext1, ext2);        }    }}
看来帅哥为了通知上面的app,也是费尽周折啊。
AwesomePlayer.cpp:            notifyListener_l(MEDIA_PAUSED);AwesomePlayer.cpp:    notifyListener_l(MEDIA_PAUSED);AwesomePlayer.cpp:        notifyListener_l(MEDIA_PAUSED);AwesomePlayer.cpp:        notifyListener_l(MEDIA_SEEK_COMPLETE);AwesomePlayer.cpp:        notifyListener_l(MEDIA_SKIPPED);AwesomePlayer.cpp:        notifyListener_l(MEDIA_SEEK_COMPLETE);AwesomePlayer.cpp:            notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);AwesomePlayer.cpp:            notifyListener_l(MEDIA_SEEK_COMPLETE);AwesomePlayer.cpp:        notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);AwesomePlayer.cpp:            notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);AwesomePlayer.cpp:        notifyListener_l(MEDIA_PREPARED);AwesomePlayer.cpp:                notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
这里仅列出一部分通知的调用。
0 0