本文共 9462 字,大约阅读时间需要 31 分钟。
一、引言:
上一篇博客中分析了ijkplayer的整个流程,相信大家对其中的消息队列看的也是云里雾里的,所以这里单独会ijkplayer的消息机制做一个分析。二、代码分析:
先看下消息机制是怎么创建起来的。创建的发起是native_setup
函数: static voidIjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this){ MPTRACE("%s\n", __func__); IjkMediaPlayer *mp = ijkmp_android_create(message_loop); ...}
需要注意的是ijkmp_android_create
的入参是一个函数指针:
IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*)){ IjkMediaPlayer *mp = ijkmp_create(msg_loop); if (!mp) goto fail; ...}
这里传入了函数message_loop
,函数里面的内容后面分析,可以看到,ijkmp_create
的入参也是一个函数指针:
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; ...}
这里面的流程我们也比较熟悉了,首先是去底层创建FFmpeg
,之后会将上面传下来的msg_loop
赋值给IjkMediaPlayer
结构体维护的函数指针变量msg_loop
。进入ffp_create
函数看一下跟消息队列相关的内容:
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;}
进入函数,首先是对FFPlayer
内存的初始化,接下来,会去调用msg_queue_init
对消息队列进行一个初始化,看一下函数实现:
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;}
需要注意的是最后一行的abort_request
变量,值为1表示消息队列不处理消息,值为0表示处理消息队列中的消息。再回到ffp_create
看下ffp_reset_internal
函数,其中有对消息处理的地方:
inline static void ffp_reset_internal(FFPlayer *ffp){ ... msg_queue_flush(&ffp->msg_queue); ...}
看下msg_queue_flush
函数操作:
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);}
这个函数的主要作用是将消息队列中的相关变量清零。为后续的消息处理做好准备。
接下来看下消息队列是怎么转起来的,跟进到_prepareAsync
这个native接口: IjkMediaPlayer_prepareAsync(JNIEnv *env, jobject thiz){ ... retval = ijkmp_prepare_async(mp); ...}
int ijkmp_prepare_async(IjkMediaPlayer *mp){ ... int retval = ijkmp_prepare_async_l(mp); ...}
重点看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;}
首先看msg_queue_start
函数:
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);}
回到上面消息处理线程,跟进到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_loop
指向的函数是msg_loop@ijkplayer.c
:
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: ;}
先看下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;}
看下msg_queue_get
是怎么拿到消息的:
/* 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;}
注释大致解释了消息的获取过程,回到上面的message_loop_n
,ijkplayer发送的第一个消息是FFP_MSG_FLUSH
,看下处理:
case FFP_MSG_FLUSH: MPTRACE("FFP_MSG_FLUSH:\n"); post_event(env, weak_thiz, MEDIA_NOP, 0, 0); break;
跟进下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__postEventFromNative
是一个宏定义,在ijkmedia\ijkj4a\j4a\class\tv\danmaku\ijk\media\player\IjkMediaPlayer.h
中:
#define J4AC_IjkMediaPlayer__postEventFromNative J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__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);}
CallStaticVoidMethod
是一个JNI回调到java层static方法的函数,第三个参数则是java层方法名,也就是class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative
,找一下method_postEventFromNative
的定义如下:
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);
从name可以确认这个java层方法名。
找到java层中对应的函数: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); } }
可以看到从jni层传上来的消息被重新投递进java层的消息机制中,找一下handleMessage
的处理:
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; ... }}
可以看到,java层对ijkplayer的第一个消息处理也是什么都没做。这里就基本分析 完了ijkplayer的消息机制。
三、总结:
ijkplayer的消息机制图解大致如下:转载地址:http://idwwz.baihongyu.com/