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();
return;
case 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*在哪里被调用则需要具体例子具体分析了。