ijkplayer 入门之Error事件源码简单跟踪

MediaPlayer对于Error事件的接收都是通过Handler来处理的(其实还包括了Info事件),而这个Handler就是EventHandler,而消息的发送则在postEventFromNative,看名字就可以很容易知道,这个代码是给C调用的。

//不完整代码
 private static class EventHandler extends Handler {
        private final WeakReference mWeakPlayer;
        private long mPrevCachePercent;

        public EventHandler(IjkMediaPlayer mp, Looper looper) {
            super(looper);
            mWeakPlayer = new WeakReference(mp);
        }

        @Override
        public void handleMessage(Message msg) {
            IjkMediaPlayer player = mWeakPlayer.get();
            ……
            switch (msg.what) {
            case MEDIA_PREPARED:
                player.notifyOnPrepared();
                returncase MEDIA_SEEK_COMPLETE:
                player.notifyOnSeekComplete();
                return;

            case MEDIA_SET_VIDEO_SIZE:
               ……
                return;

            case MEDIA_ERROR:
                DebugLog.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
                if (!player.notifyOnError(msg.arg1, msg.arg2)) {
                    player.notifyOnCompletion();
                }
                player.stayAwake(false);
                return;

            case MEDIA_INFO:
               ……
            default:
                DebugLog.e(TAG, "Unknown message type " + msg.what);
            }
        }
    }

通过player.notifyOn*来触发我们注册的监听器

接着到C代码中

全局搜索postEventFromNative,可以在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);
}

从命名上可以判断C代码中是在这里调用java层的postEventFromNative
而该函数在IjkMediaplayer.h文件中通过宏定义:

#define J4AC_IjkMediaPlayer__postEventFromNative J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__postEventFromNative

被调用的
查看该宏定义被引用的地方,跟踪到ijkplayer_jni.c

inline static void post_event(JNIEnv *env, jobject weak_this, int what, int arg1, int arg2)
{
    J4AC_IjkMediaPlayer__postEventFromNative(env, weak_this, what, arg1, arg2, NULL);
}

该内联函数都在message_loop_n中被调用

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;

        int retval = ijkmp_get_msg(mp, &msg, 1);
        if (retval < 0)
            break;

        // block-get should never return 0
        assert(retval > 0);

        switch (msg.what) {
        case FFP_MSG_FLUSH:
            MPTRACE("FFP_MSG_FLUSH:\n");
            post_event(env, weak_thiz, MEDIA_NOP, 0, 0);
            break;
        case FFP_MSG_ERROR:
            MPTRACE("FFP_MSG_ERROR: %d\n", msg.arg1);
            post_event(env, weak_thiz, MEDIA_ERROR, MEDIA_ERROR_IJK_PLAYER, msg.arg1);
            break;
            ……

通过代码我们可以看到:循环获取msg,(这很明显是跑在某一个线程里的)通ijkmp_get_msg函数中获取到了msg,再封装后能通过post_event发送到java层,跟踪到这里,我们可以发现,这边是C代码中信息的接收方,因为ijkmp_get_msg是在消息队列中拿取消息体的,先接着跟踪 message_loop_n,该函数在message_loop中被调用了
接着跟踪就会发现循环体是被mediaplyer所持有的

static void
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
    MPTRACE("%s\n", __func__);
    IjkMediaPlayer *mp = ijkmp_android_create(message_loop);//创建player对象,参数为函数指针,函数体为循环获取消息
   ……
}

这边在创建ijkMediaplayer时传入进去
ijkplayer_android.c

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

fail:
    ijkmp_dec_ref_p(&mp);
    return NULL;
}

ijkplayer.c

IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
{
    IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
    if (!mp)
        goto fail;

    mp->ffplayer = ffp_create();
    if (!mp->ffplayer)
        goto fail;

    mp->msg_loop = msg_loop;

   ……
}

此外,在ijkmp_prepare_async_l函数中我们可以找到msg_thread创建代码

static int ijkmp_prepare_async_l(IjkMediaPlayer *mp)
{
   ……
    ijkmp_change_state_l(mp, MP_STATE_ASYNC_PREPARING);

    msg_queue_start(&mp->ffplayer->msg_queue);

    // released in msg_loop 创建消息线程
    ijkmp_inc_ref(mp);
    mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");
   ……
}

对于在C代码中消息的接收到此结束了,那C代码中的发送方在哪里呢?
上面我们提到,IjkMediaplayer通过该消息机制处理的不仅有error事件,还有各种Info事件,包括了状态机,刚好我们在IjkPlayer.c中找到了这样的一个函数

void ijkmp_change_state_l(IjkMediaPlayer *mp, int new_state)
{
    mp->mp_state = new_state;
    ffp_notify_msg1(mp->ffplayer, FFP_MSG_PLAYBACK_STATE_CHANGED);
}

状态机改变通知,而这个函数内部调用的是
ff_ffplay_df.c

inline static void ffp_notify_msg1(FFPlayer *ffp, int what) {
    msg_queue_put_simple3(&ffp->msg_queue, what, 0, 0);
}

传入参参:msg_queue,可以肯定的是C代码中的发送方在这里了
接着在ff_ffmsg_queue中


inline static void msg_queue_put_simple3(MessageQueue *q, int what, int arg1, int arg2)
{
    AVMessage msg;
    msg_init_msg(&msg);
    msg.what = what;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    msg_queue_put(q, &msg);
}

msg_queue_put负责将msg发送出去
C代码中消息的发送方到这就结束了,至于ffp_notify_msg*在哪里被调用则需要具体例子具体分析了。

你可能感兴趣的:(ijkplayer)