Message mechanism analysis of MediaPlayer

Posted by alexweb on Fri, 08 Oct 2021 20:56:12 +0200

1, Introduction:
First post a 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

Friends who often deal with media problems must have encountered the printing in the above log. In fact, this is the message callback mechanism of Android native MediaPlayer. Call back the underlying error / Info / warning to the application layer. When problems often occur, we want to confirm which message has the problem, but we have no way to start. This requires a real understanding of the message mechanism. In fact, we only need to focus on three points: listener, post event and notify. The functions of the three are: register the listener to the lower layer, the lower layer calls the listener to send a callback event, and the upper layer processes the message. With the binder mechanism, the three do not exist in pairs in the framework layer code. The following will analyze the three in turn.

2, Java (framework) - > JNI:
Let's first look at how the Java layer receives messages: the JNI layer uses the Java layer reflection mechanism to call to the Java layer.

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());
            /* Pass the underlying msg to the java layer through the reflection mechanism */
            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
                    msg, ext1, ext2, jParcel);
            env->DeleteLocalRef(jParcel);
        }
    } else {
    	/* Pass the underlying msg to the java layer through the reflection mechanism */
        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();
    }
}

Take a look at fields. Post_ What is event:

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

The corresponding is the postEventFromNative method in MediaPlayer.java:

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);
       /* Send message to handler for processing */
       mp.mEventHandler.sendMessage(m);
   }
}

Follow up:

public void handleMessage(Message msg) {
	...
	switch(msg.what) {
            case MEDIA_PREPARED:
				...
			    OnPreparedListener onPreparedListener = mOnPreparedListener;
                if (onPreparedListener != null)
                	/* Send the onPrepared message to apk */
                    onPreparedListener.onPrepared(mMediaPlayer);
                return;
	}
}

You can see that many listeners can be registered in mediaplayer.java. Mediaplayer.java will call the callback functions of these listeners to apk to complete the operations required by the application.

3, JNI - > mediaplayer.cpp:
First look at the register of listener:

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

	/* 2.Create the listener of JNI layer and set it */
    // 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);
}

You can see that the JNI layer creates a listener and sets it to the lower layer. Let's take a look at the processing of 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");
        /* Callback messages to jni layer through listener */
        listener->notify(msg, ext1, ext2, obj);
        ALOGV("back from callback");
    }
}

4, Mediaplayer.cpp (BP) - > mediaplayer service (BN):
Here we are told when the above notify is called. The answer is directly called back from the notify on the Bn side:

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

void MediaPlayerService::Client::notify(
        int msg, int ext1, int ext2, const Parcel *obj)
{
	...
	/* This is where you call mediaplayer.cpp */
	if (c != NULL) {
        ALOGV("[%d] notify (%d, %d, %d)", mConnId, msg, ext1, ext2);
        c->notify(msg, ext1, ext2, obj);
    }
}

5, Media player service (BN) - > underlying player:
First, the Bn side needs to have a listener. In the MediaPlayerService::Client::Client constructor:

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

A listener will be built here. The listener is registered in the underlying player. When was it registered? Look
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) {
    	/* Pass in the constructed listener */
        p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
    }

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

    return p;
}

Follow up createPlayer:

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

sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
        player_type playerType,
        const sp<MediaPlayerBase::Listener> &listener,
        pid_t pid) {
		...
		/* 1.Create underlying player */
		p = factory->createPlayer(pid);
		...
		init_result = p->initCheck();
    	if (init_result == NO_ERROR) {
    		/* 2.Register the listener in the callback function of the player */
        	p->setNotifyCallback(listener);
    	} else {
        	ALOGE("Failed to create player object of type %d, initCheck failed"
              " (res = %d)", playerType, init_result);
        p.clear();
        ...
    }
}

Continue to follow up. MediaPlayerBase in MediaPlayerInterface.h:

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

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

Note the inheritance relationship here. mstplayer: MediaPlayerInterface: MediaPlayerBase, so the listener on the Bn side will be registered in the underlying player all the way. After that, the underlying player will call sendent to return the message:

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) {
    	/* The underlying player will call this interface to notify the upper layer */
        listener->notify(msg, ext1, ext2, obj);
    }
}

6, Summary:

Topics: Java Android MediaPlayer