Analysis of ijkplayer player (3) Analysis of message mechanism

1. Introduction:
In the previous blog, I analyzed the entire process of ijkplayer. I believe that everyone’s view of the message queue is also in the cloud, so here is a separate analysis of the message mechanism of ijkplayer.

2. Code analysis:
first look at how the message mechanism is created, and the creation is initiated by native_setupa function:

static void
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    
    
    MPTRACE("%s\n", __func__);
    IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
	...
}

It should be noted that ijkmp_android_createthe input parameter is a function pointer:

IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*))
{
    
    
    IjkMediaPlayer *mp = ijkmp_create(msg_loop);
    if (!mp)
        goto fail;
	...
}

The function is passed in here message_loop, and the content in the function will be analyzed later. You can see that ijkmp_createthe input parameter is also a function pointer:

IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
{
    
    
    IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
    if (!mp)
        goto fail;
	/* 创建FFmpeg */
    mp->ffplayer = ffp_create();
    if (!mp->ffplayer)
        goto fail;
	/* 对mp->msg_loop进行赋值 */
    mp->msg_loop = msg_loop;
	...
}

We are also familiar with the process here. First, go to the bottom layer to create FFmpeg, and then assign msg_loopthe value passed down from above to IjkMediaPlayerthe function pointer variable maintained by the structure msg_loop. Enter ffp_createthe function to see the content related to the message queue:

FFPlayer *ffp_create()
{
    
    
    av_log(NULL, AV_LOG_INFO, "av_version_info: %s\n", av_version_info());
    av_log(NULL, AV_LOG_INFO, "ijk_version_info: %s\n", ijk_version_info());

	/* 1.申请FFPlayer结构体内存并初始化为0 */
    FFPlayer* ffp = (FFPlayer*) av_mallocz(sizeof(FFPlayer));
    if (!ffp)
        return NULL;
	/* 2.初始化ffp的消息队列msg_queue */
    msg_queue_init(&ffp->msg_queue);
    ffp->af_mutex = SDL_CreateMutex();
    ffp->vf_mutex = SDL_CreateMutex();
	/* 3.对FFPlayer结构体成员进行reset操作 */
    ffp_reset_internal(ffp);
    ffp->av_class = &ffp_context_class;
    ffp->meta = ijkmeta_create();

    av_opt_set_defaults(ffp);

    las_stat_init(&ffp->las_player_statistic);
    return ffp;
}

Entering the function, the first is to FFPlayerinitialize the memory. Next, it will call to msg_queue_initinitialize the message queue. Let's take a look at the function implementation:

inline static void msg_queue_init(MessageQueue *q)
{
    
    
    memset(q, 0, sizeof(MessageQueue));
    /* 创建消息队列互斥锁 */
    q->mutex = SDL_CreateMutex();
    /* 创建消息队列信号量 */
    q->cond = SDL_CreateCond();
    /* abort_request变量用于记录队列是否处理消息 */
    q->abort_request = 1;
}

It should be noted that abort_requestthe variable in the last line, a value of 1 means that the message queue does not process messages, and a value of 0 means that messages in the message queue are processed. Go back and ffp_createlook at ffp_reset_internalthe function, where there is a place for message processing:

inline static void ffp_reset_internal(FFPlayer *ffp)
{
    
    
	...
	msg_queue_flush(&ffp->msg_queue);
	...
}

Look at msg_queue_flushthe function operation:

inline static void msg_queue_flush(MessageQueue *q)
{
    
    
    AVMessage *msg, *msg1;

    SDL_LockMutex(q->mutex);
    for (msg = q->first_msg; msg != NULL; msg = msg1) {
    
    
        msg1 = msg->next;
#ifdef FFP_MERGE
        av_freep(&msg);
#else
        msg->next = q->recycle_msg;
        q->recycle_msg = msg;
#endif
    }
    q->last_msg = NULL;
    q->first_msg = NULL;
    q->nb_messages = 0;
    SDL_UnlockMutex(q->mutex);
}

The main function of this function is to clear the relevant variables in the message queue. Prepare for subsequent message processing.
Next, let's see how the message queue turns up, and follow up to _prepareAsyncthis native interface:

IjkMediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
    
    
	...
    retval = ijkmp_prepare_async(mp);
	...
}
int ijkmp_prepare_async(IjkMediaPlayer *mp)
{
    
    
	...
    int retval = ijkmp_prepare_async_l(mp);
	...
}

Focus on ijkmp_prepare_async_l:

static int ijkmp_msg_loop(void *arg)
{
    
    
    IjkMediaPlayer *mp = arg;
    int ret = mp->msg_loop(arg);
    return ret;
}

static int ijkmp_prepare_async_l(IjkMediaPlayer *mp)
{
    
    
	...
	/* 1.开启消息队列 */
    msg_queue_start(&mp->ffplayer->msg_queue);

	/* 2.创建消息队列处理线程 */
    // released in msg_loop
    ijkmp_inc_ref(mp);
    mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");
    // msg_thread is detached inside msg_loop
    // TODO: 9 release weak_thiz if pthread_create() failed;

	...

    return 0;
}

First look at msg_queue_startthe function:

inline static void msg_queue_start(MessageQueue *q)
{
    
    
    SDL_LockMutex(q->mutex);
    /* 消息队列开始处理消息 */
    q->abort_request = 0;

	/* 发送一个FFP_MSG_FLUSH消息 */
    AVMessage msg;
    msg_init_msg(&msg);
    msg.what = FFP_MSG_FLUSH;
    msg_queue_put_private(q, &msg);
    SDL_UnlockMutex(q->mutex);
}

Back to the above message processing thread, follow up to ijkmp_msg_loop:

static int ijkmp_msg_loop(void *arg)
{
    
    
    IjkMediaPlayer *mp = arg;
    /* 调用mp->msg_loop指向的函数来处理消息 */
    int ret = mp->msg_loop(arg);
    return ret;
}

mp->msg_loopThe function we have analyzed before is [email protected]:

static int message_loop(void *arg)
{
    
    
	...
    message_loop_n(env, mp);
	...
}
static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)
{
    
    
    jobject weak_thiz = (jobject) ijkmp_get_weak_thiz(mp);
    JNI_CHECK_GOTO(weak_thiz, env, NULL, "mpjni: message_loop_n: null weak_thiz", LABEL_RETURN);

    while (1) {
    
    
        AVMessage msg;
		/* 1.从ijkplayer中获取一个message */
        int retval = ijkmp_get_msg(mp, &msg, 1);
        if (retval < 0)
            break;

        // block-get should never return 0
        assert(retval > 0);
		/* 2.通过msg.what进行消息处理 */
        switch (msg.what) {
    
    
            case FFP_MSG_FLUSH:
            	MPTRACE("FFP_MSG_FLUSH:\n");
            	post_event(env, weak_thiz, MEDIA_NOP, 0, 0);
            	break;
			...
        }
        msg_free_res(&msg);
    }

LABEL_RETURN:
    ;
}

First look at ijkmp_get_msg:

/* need to call msg_free_res for freeing the resouce obtained in msg */
int ijkmp_get_msg(IjkMediaPlayer *mp, AVMessage *msg, int block)
{
    
    
    assert(mp);
    while (1) {
    
    
        int continue_wait_next_msg = 0;
        /* 调用msg_queue_get获取一个消息 */
        int retval = msg_queue_get(&mp->ffplayer->msg_queue, msg, block);
        if (retval <= 0)
            return retval;

        switch (msg->what) {
    
    
			...
        }
        if (continue_wait_next_msg) {
    
    
            msg_free_res(msg);
            continue;
        }

        return retval;
    }

    return -1;
}

See msg_queue_gethow you got the news:

/* return < 0 if aborted, 0 if no msg and > 0 if msg.  */
inline static int msg_queue_get(MessageQueue *q, AVMessage *msg, int block)
{
    
    
    AVMessage *msg1;
    int ret;

    SDL_LockMutex(q->mutex);

    for (;;) {
    
    
        if (q->abort_request) {
    
    
            ret = -1;
            break;
        }
		/* 获取队列的第一个消息*/
        msg1 = q->first_msg;
        if (msg1) {
    
    
        	/* 更新消息队列中第一个待处理消息 */
            q->first_msg = msg1->next;
            if (!q->first_msg)
                q->last_msg = NULL;
            /* 消息总数减一 */
            q->nb_messages--;
            *msg = *msg1;
            msg1->obj = NULL;
#ifdef FFP_MERGE
            av_free(msg1);
#else
			/* 循环消息处理,是为了某种场景? */
            msg1->next = q->recycle_msg;
            q->recycle_msg = msg1;
#endif
            ret = 1;
            break;
        } else if (!block) {
    
    
            ret = 0;
            break;
        } else {
    
    
            SDL_CondWait(q->cond, q->mutex);
        }
    }
    SDL_UnlockMutex(q->mutex);
    return ret;
}

The comment roughly explains the process of obtaining the message. Going back to the above message_loop_n, the first message sent by ijkplayer is FFP_MSG_FLUSH, see the processing:

case FFP_MSG_FLUSH:
    MPTRACE("FFP_MSG_FLUSH:\n");
    post_event(env, weak_thiz, MEDIA_NOP, 0, 0);
    break;

Follow up post_event:

inline static void post_event(JNIEnv *env, jobject weak_this, int what, int arg1, int arg2)
{
    
    
    // MPTRACE("post_event(%p, %p, %d, %d, %d)", (void*)env, (void*) weak_this, what, arg1, arg2);
    J4AC_IjkMediaPlayer__postEventFromNative(env, weak_this, what, arg1, arg2, NULL);
    // MPTRACE("post_event()=void");
}

J4AC_IjkMediaPlayer__postEventFromNativeis a macro definition, in ijkmedia\ijkj4a\j4a\class\tv\danmaku\ijk\media\player\IjkMediaPlayer.h:

#define J4AC_IjkMediaPlayer__postEventFromNative J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__postEventFromNative

Find IjkMediaPlayer.c:

void J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__postEventFromNative(JNIEnv *env, jobject weakThiz, jint what, jint arg1, jint arg2, jobject obj)
{
    
    
    (*env)->CallStaticVoidMethod(env, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative, weakThiz, what, arg1, arg2, obj);
}

CallStaticVoidMethodIt is a function that JNI calls back to the static method of the java layer, and the third parameter is the name of the java layer method, that is, the definition of class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNativelooking for it method_postEventFromNativeis as follows:

class_id = class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id;
    name     = "postEventFromNative";
    sign     = "(Ljava/lang/Object;IIILjava/lang/Object;)V";
    class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative = J4A_GetStaticMethodID__catchAll(env, class_id, name, sign);

From the name, you can confirm the java layer method name.
Find the corresponding function in the java layer:

postEventFromNative@android\ijkplayer\ijkplayer-java\src\main\java\tv\danmaku\ijk\media\player\IjkMediaPlayer.java:

    @CalledByNative
    private static void postEventFromNative(Object weakThiz, int what,
            int arg1, int arg2, Object obj) {
    
    
        if (weakThiz == null)
            return;

        @SuppressWarnings("rawtypes")
        IjkMediaPlayer mp = (IjkMediaPlayer) ((WeakReference) weakThiz).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);
        }
    }

You can see that the message passed from the jni layer is re-delivered into the message mechanism of the java layer. Find the handleMessageprocessing:

        public void handleMessage(Message msg) {
    
    
            IjkMediaPlayer player = mWeakPlayer.get();
            if (player == null || player.mNativeMediaPlayer == 0) {
    
    
                DebugLog.w(TAG,
                        "IjkMediaPlayer went away with unhandled events");
                return;
            }

			switch (msg.what) {
    
    
			case MEDIA_NOP: // interface test message - ignore
                break;
			...
			}
}

It can be seen that the java layer did nothing to process the first message of ijkplayer. Here is the basic analysis of the message mechanism of ijkplayer.

3. Summary:
The diagram of the message mechanism of ijkplayer is roughly as follows:
insert image description here

Guess you like

Origin blog.csdn.net/achina2011jy/article/details/115758282