ijkplayer 入门之 初始化
我们先将创建播放器、设置播放url、prepareAsync归为初始化部份
首先是创建播放器
java层代码
//创建播放器
public IjkMediaPlayer() {
this(sLocalLibLoader);
}
public IjkMediaPlayer(IjkLibLoader libLoader) {
initPlayer(libLoader);
}
private void initPlayer(IjkLibLoader libLoader) {
loadLibrariesOnce(libLoader);
//就是调用了C的IjkMediaPlayer_native_init的方法,但没有什么操作
initNativeOnce();
……
native_setup(new WeakReference<IjkMediaPlayer>(this));
}
loadLibrariesOnce加载本地的so库,在加载JNI时,C代码会对应的调用了JNI_OnLoad,而卸载时会自动调用JNI_OnUnload
该函数在ijkplayer_jni.c
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
……
pthread_mutex_init(&g_clazz.mutex, NULL );
// FindClass returns LocalReference
IJK_FIND_JAVA_CLASS(env, g_clazz.clazz, JNI_CLASS_IJKPLAYER);
(*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods) );
ijkmp_global_init(); //主要是ffmpeg的初始化工作
ijkmp_global_set_inject_callback(inject_callback);
FFmpegApi_global_init(env);//初始化FFmpegApi
return JNI_VERSION_1_4;
}
这里通过RegisterNatives注册相关native方法,在java层调用的C的函数均可以在g_methods里找得到,接着是一些ffmpeg的初始化工作
回到java层,接着调用了native_setup方法,我们在g_methods里找到了对应的函数IjkMediaPlayer_native_setup
static void
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
MPTRACE("%s\n", __func__);
IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
JNI_CHECK_GOTO(mp, env, "java/lang/OutOfMemoryError", "mpjni: native_setup: ijkmp_create() failed", LABEL_RETURN);
jni_set_media_player(env, thiz, mp);
ijkmp_set_weak_thiz(mp, (*env)->NewGlobalRef(env, weak_this));
ijkmp_set_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
ijkmp_set_ijkio_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
LABEL_RETURN:
ijkmp_dec_ref_p(&mp);
}
ijkmp_android_create(message_loop),创建player对象,参数为函数指针,函数体为循环获取消息,这里最终与java层的handler消息机制对应着。
创建播放器的大致流程就到这里结束了,接着我们来看下seturl,最终调用的是setDataSourceFd方法,对应C中的
{ "_setDataSourceFd", "(I)V", (void *) IjkMediaPlayer_setDataSourceFd }
这里以设置网络文件为例(其中最终播放本地文件和网络文件的下层代码是一致的,只是前期逻辑有些区别)
static void
IjkMediaPlayer_setDataSourceFd(JNIEnv *env, jobject thiz, jint fd)
{
MPTRACE("%s\n", "wsMediaPlayer_setDataSourceFd");
int retval = 0;
int dupFd = 0;
char uri[128];
IjkMediaPlayer *mp = jni_get_media_player(env, thiz);
JNI_CHECK_GOTO(fd > 0, env, "java/lang/IllegalArgumentException", "mpjni: setDataSourceFd: null fd", LABEL_RETURN);
JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: setDataSourceFd: null mp", LABEL_RETURN);
dupFd = dup(fd);
ALOGV("setDataSourceFd: dup(%d)=%d\n", fd, dupFd);
snprintf(uri, sizeof(uri), "pipe:%d", dupFd);
retval = ijkmp_set_data_source(mp, uri);
IJK_CHECK_MPRET_GOTO(retval, env, LABEL_RETURN);
LABEL_RETURN:
ijkmp_dec_ref_p(&mp);
}
这个函数做的事不算多,最终所url设置进mp数据源,发送了一个消息MP_STATE_INITIALIZED,主要代码都可以通过ijkmp_set_data_source跟踪到
static int ijkmp_set_data_source_l(IjkMediaPlayer *mp, const char *url)
{
……
freep((void**)&mp->data_source);
mp->data_source = strdup(url);
if (!mp->data_source)
return EIJK_OUT_OF_MEMORY;
ijkmp_change_state_l(mp, MP_STATE_INITIALIZED);
return 0;
}
最后是prepareAsync,一样在C的注册方法中我可以找到
{ "_prepareAsync", "()V", (void *) IjkMediaPlayer_prepareAsync },
最终调用的ijkmp_prepare_async_l函数
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");
// msg_thread is detached inside msg_loop
// TODO: 9 release weak_thiz if pthread_create() failed;
int retval = ffp_prepare_async_l(mp->ffplayer, mp->data_source);
if (retval < 0) {
ijkmp_change_state_l(mp, MP_STATE_ERROR);
return retval;
}
return 0;
}
SDL_CreateThreadEx,创建了msg_loop消息队列的线程,
而在ffp_prepare_async_l里有个很重要的函数stream_open
VideoState *is = stream_open(ffp, file_name, NULL);
为什么说这个重要的,因为stream_open函数里创建了两个很重要的线程
is->video_refresh_tid = SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");
if (!is->video_refresh_tid) {
av_freep(&ffp->is);
return NULL;
}
is->read_tid = SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read");
if (!is->read_tid) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
一个是视频渲染刷新线程video_refresh_thread,一个是数据读取线程read_thread,而在read_thread中获取到第一帧相关数据后就会向上层发送FFP_MSG_PREPARED信息,最终我们在java层OnPreparedListener接收到相关数据后就可以开始观看视频了
上一篇: 文章标题