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接收到相关数据后就可以开始观看视频了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值