MediaPlayer的消息机制分析

一、引子:

先贴一段log:

08-18 21:57:20.110 11775 11812 V MediaPlayer: resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
08-18 21:57:20.110 11775 11775 E MediaPlayerNative: error (-38, 0)
08-18 21:57:20.110  3206  3206 D MTK_KL  : 4,60541,9379664038,-;[MI_AUDIO_Stop:8901][3079] u32ErrCode:0x0
08-18 21:57:20.110  3206  3206 D MTK_KL  : 4,60542,9379664153,-;[_MI_AOUT_NotifyDisconnectInput:7948][3079] hAoutImpl:0x17000002, hInputImpl:0x19000000
08-18 21:57:20.110  3206  3206 D MTK_KL  : 4,60543,9379664175,-;[_MI_AOUT_SetMultiMute:2794][3079] ePath:2, pszMuteName:_MI_AOUT_NotifyDisconnectInput, bMute:1, u32AutoUnmuteTimer:246, u32AoutMuteFlag:0x3
08-18 21:57:20.110 11775 11812 V MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null
08-18 21:57:20.110 11775 11775 E MediaPlayer: Error (-38,0)
08-18 21:57:20.110 11775 11812 V MediaPlayer: resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
08-18 21:57:20.110 11775 11812 V MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null

经常处理媒体问题的朋友肯定遇到过上述log中的打印,实际上,这是Android原生MediaPlayer的消息回调机制。将底层的error/info/waring回调至应用层,而往往出现问题的时候,我们想要确认是哪个消息出现的问题却无从下手,这就需要真正理解这个消息机制才行,实际上,我们只需要关注三个点:listener、post event 和 notify。这三者的作用是:注册监听器给下层,下层调用监听器发送回调事件,上层处理该消息,而伴随着binder机制,这三者并不是在framework层代码中成对存在的,下面就对这三者依次进行分析。

Error (-38,0)对应的错误查找:
frameworks/base/services/core/jni/BroadcastRadio/types.h 

二、Java(framework)->JNI:

先看看Java层是如何接收到消息的:JNI层利用Java层反射机制调用至Java层。

notify@ frameworks\base\media\jni\android_media_MediaPlayer.cpp

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());
            /* 通过反射机制将底层msg传至java层 */
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
    	/* 通过反射机制将底层msg传至java层 */
        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                msg, ext1, ext2, NULL);
    }
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        LOGW_EX(env);
        env->ExceptionClear();
    }
}

看一下fields.post_event是什么:

    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

对应的是MediaPlayer.java中的postEventFromNative方法:

postEventFromNative@ frameworks\base\media\java\android\media\MediaPlayer.java

private static void postEventFromNative(Object mediaplayer_ref,
                                        int what, int arg1, int arg2, Object obj)
{
	...
    if (mp.mEventHandler != null) {
       Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
       /* 发送消息给handler处理 */
       mp.mEventHandler.sendMessage(m);
   }
}

继续跟进:

public void handleMessage(Message msg) {
	...
	switch(msg.what) {
            case MEDIA_PREPARED:
				...
			    OnPreparedListener onPreparedListener = mOnPreparedListener;
                if (onPreparedListener != null)
                	/* 将onPrepared消息发送至apk中 */
                    onPreparedListener.onPrepared(mMediaPlayer);
                return;
	}
}

可以看到,mediaplayer.java中可以注册非常多的listener,mediaplayer.java会调用这些listener的回调函数至apk,完成应用需要的操作。

三、JNI->mediaplayer.cpp:

先看listener的注册:

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    ALOGV("native_setup");
    /* 1.实例化MediaPlayer */
    sp<MediaPlayer> mp = new MediaPlayer();
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

	/* 2.创建JNI层的listener并设置下去 */
    // create new listener and give it to MediaPlayer
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    setMediaPlayer(env, thiz, mp);
}

可以看到,JNI层自己创建了一个listener并设置到下层中。下面看一下mediaplayer.cpp的处理:

void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
	...
	
	sp<MediaPlayerListener> listener = mListener;
    if (locked) mLock.unlock();

    // this prevents re-entrant calls into client code
    if ((listener != 0) && send) {
        Mutex::Autolock _l(mNotifyLock);
        ALOGV("callback application");
        /* 通过listener将消息回调至jni层 */
        listener->notify(msg, ext1, ext2, obj);
        ALOGV("back from callback");
    }
}

四、mediaplayer.cpp(Bp)->mediaplayerservice(Bn):

这里就告诉了我们上面的notify是何时调用的,答案而是直接从Bn端的notify中回调上来的:

client::notify@ frameworks\av\media\libmediaplayerservice\MediaPlayerService.cpp

void MediaPlayerService::Client::notify(
        int msg, int ext1, int ext2, const Parcel *obj)
{
	...
	/* 这里就是调用到mediaplayer.cpp中去的地方 */
	if (c != NULL) {
        ALOGV("[%d] notify (%d, %d, %d)", mConnId, msg, ext1, ext2);
        c->notify(msg, ext1, ext2, obj);
    }
}

五、mediaplayerservice(Bn)->底层播放器:

首先Bn端需要有一个listener,在MediaPlayerService::Client::Client构造函数中: 

MediaPlayerService::Client::Client(
        const sp<MediaPlayerService>& service, pid_t pid,
        int32_t connId, const sp<IMediaPlayerClient>& client,
        audio_session_t audioSessionId, uid_t uid)
{
	...
	mListener = new Listener(this);
	...
}

这里会构建一个listener,这个listener拿来是注册到底层播放器中的,什么时候注册的?来看
MediaPlayerService::Client::createPlayer:

sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    // determine if we have the right player type
    sp<MediaPlayerBase> p = getPlayer();
    if ((p != NULL) && (p->playerType() != playerType)) {
        ALOGV("delete player");
        p.clear();
    }
    if (p == NULL) {
    	/* 将已构造的listener传进去 */
        p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
    }

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

    return p;
}

跟进下createPlayer:

createPlayer@ frameworks\av\media\libmediaplayerservice\MediaPlayerFactory.cpp:

sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
        player_type playerType,
        const sp<MediaPlayerBase::Listener> &listener,
        pid_t pid) {
		...
		/* 1.创建底层播放器 */
		p = factory->createPlayer(pid);
		...
		init_result = p->initCheck();
    	if (init_result == NO_ERROR) {
    		/* 2.将listener注册到播放器的回调函数中 */
        	p->setNotifyCallback(listener);
    	} else {
        	ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
        ...
    }
}

继续往下跟进,MediaPlayerBase在MediaPlayerInterface.h中:

setNotifyCallback@frameworks\av\media\libmediaplayerservice\include\MediaPlayerInterface.h:

void        setNotifyCallback(
        const sp<Listener> &listener) {
    Mutex::Autolock autoLock(mNotifyLock);
    mListener = listener;
}

这里需要注意一下继承关系,mstplayer: MediaPlayerInterface: MediaPlayerBase,所以Bn端的这个listener会一路注册到底层的播放器中,之后,底层播放器就会调用sendevent将消息返回上来了:

void        sendEvent(int msg, int ext1=0, int ext2=0,
                      const Parcel *obj=NULL) {
    sp<Listener> listener;
    {
        Mutex::Autolock autoLock(mNotifyLock);
        listener = mListener;
    }

    if (listener != NULL) {
    	/* 底层的播放器都会调用这个接口通知至上层 */
        listener->notify(msg, ext1, ext2, obj);
    }
}

六、总结: 

 如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/127302648