ijkplayer解码器的准备过程(二)

简要

 本文介绍,从prepareAsync后,到onPrepared回调的过程

先看Log

V/IJKMEDIA: setDataSource: path http://weblive.hebtv.com/live/hbws_bq/index.m3u8
D/IJKMEDIA: IjkMediaPlayer_prepareAsync
D/IJKMEDIA: ijkmp_prepare_async()
I/IJKMEDIA: ===== versions =====
I/IJKMEDIA: FFmpeg       : ff3.4--ijk0.8.7--20180103--001
I/IJKMEDIA: SDL_RunThread: [15969] ff_msg_loop
D/IJKMEDIA: message_loop
I/IJKMEDIA: ===== options =====
I/IJKMEDIA: player-opts : packet-buffering             = 0
I/IJKMEDIA: player-opts : framedrop                    = 1
I/IJKMEDIA: player-opts : find_stream_info             = 0
I/IJKMEDIA: player-opts : render-wait-start            = 1
I/IJKMEDIA: player-opts : subtitle                     = 1
I/IJKMEDIA: player-opts : fps                          = 1
I/IJKMEDIA: player-opts : mediacodec                   = 1
I/IJKMEDIA: format-opts : analyzemaxduration           = 100
I/IJKMEDIA: format-opts : probesize                    = 5120
I/IJKMEDIA: format-opts : flush_packets                = 1
I/IJKMEDIA: format-opts : analyzeduration              = 1
I/IJKMEDIA: codec-opts  : skip_loop_filter             = 48
I/IJKMEDIA: ===================
I/IJKMEDIA: SDL_RunThread: [15970] ff_vout
D/IJKMEDIA: ijkmp_prepare_async()=0
I/IJKMEDIA: SDL_RunThread: [15971] ff_read
D/IJKMEDIA: func_open_video_decoder
D/IJKMEDIA: ffpipenode_create_video_decoder_from_android_mediacodec()
D/IJKMEDIA: FFP_MSG_PREPARED:

通过LOG可以看到,主要做了prepareAsync->ijkmp_prepare_async->ijkmp_prepare_async_l->SDL_RunThread->stream_open->func_open_video_decoder->ffpipenode_create_video_decoder_from_android_mediacodec->IjkMediaPlayer_start

流程图

ijkplayer解码器的准备过程(二)_第1张图片

JAVA部分

            ijkMediaPlayer.setDataSource("http://192.168.199.8/emo.mp4");
            ts = System.currentTimeMillis();
            ijkMediaPlayer.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(IMediaPlayer iMediaPlayer) {
                    iMediaPlayer.start();

                }
            });
            ijkMediaPlayer.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
                @Override
                public boolean onInfo(IMediaPlayer mp, int what, int extra) {
                    if(what == 3){
                        Log.d(TAG,"Tango  delay time = "+(System.currentTimeMillis()-ts));
                    }

                    return false;
                }
            });
            ijkMediaPlayer.prepareAsync();

设置好DataSource后,prepareAsync()是ijkplayer播放前的准备函数,具体工作流程

IjkMediaPlayer.java
@Override
    public void prepareAsync() throws IllegalStateException {
        _prepareAsync();
    }

通过JNI调用

ijkplayer_jni.c

static JNINativeMethod g_methods[] = {
.....
{ "_setVideoSurface",       "(Landroid/view/Surface;)V", (void *) IjkMediaPlayer_setVideoSurface },
    { "_prepareAsync",          "()V",      (void *) IjkMediaPlayer_prepareAsync },
    { "_start",                 "()V",      (void *) IjkMediaPlayer_start },
    { "_stop",                  "()V",      (void *) IjkMediaPlayer_stop },
    { "seekTo",                 "(J)V",     (void *) IjkMediaPlayer_seekTo },
    { "_pause",                 "()V",      (void *) IjkMediaPlayer_pause },
    { "isPlaying",              "()Z",      (void *) IjkMediaPlayer_isPlaying },
    { "getCurrentPosition",     "()J",      (void *) IjkMediaPlayer_getCurrentPosition },
    { "getDuration",            "()J",      (void *) IjkMediaPlayer_getDuration },
    { "_release",               "()V",      (void *) IjkMediaPlayer_release },
...
}

播放器相关的控制函数,均在这个文件里实现

IjkMediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
{
    MPTRACE("%s\n", __func__);
    int retval = 0;
    IjkMediaPlayer *mp = jni_get_media_player(env, thiz);//获取player
    JNI_CHECK_GOTO(mp, env, "java/lang/IllegalStateException", "mpjni: prepareAsync: null mp", LABEL_RETURN);

    retval = ijkmp_prepare_async(mp);
    IJK_CHECK_MPRET_GOTO(retval, env, LABEL_RETURN);

LABEL_RETURN:
    ijkmp_dec_ref_p(&mp);
}

通过jni_get_media_player获取到player,并传入ijkmp_prepare_async

ijkplayer.c

int ijkmp_prepare_async(IjkMediaPlayer *mp)
{
    assert(mp);
    MPTRACE("ijkmp_prepare_async()\n");
    pthread_mutex_lock(&mp->mutex);
    int retval = ijkmp_prepare_async_l(mp);
    pthread_mutex_unlock(&mp->mutex);
    MPTRACE("ijkmp_prepare_async()=%d\n", retval);
    return retval;
}

 这里会创建消息线程ijkmp_msg_loop,接着进入ffp_prepare_async_l

static int ijkmp_prepare_async_l(IjkMediaPlayer *mp)
{
    assert(mp);
    //------判断是否是下面这些状态,如果是返回错误??不知道是否有误
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_IDLE);
    // MPST_RET_IF_EQ(mp->mp_state, MP_STATE_INITIALIZED);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ASYNC_PREPARING);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PREPARED);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STARTED);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PAUSED);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_COMPLETED);
    // MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STOPPED);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ERROR);
    MPST_RET_IF_EQ(mp->mp_state, MP_STATE_END);
    //---------
    assert(mp->data_source);
    //准备状态
    ijkmp_change_state_l(mp, MP_STATE_ASYNC_PREPARING);

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

    // 释放消息线程
    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;
}

ffp_prepare_async_l函数,打印了ffmepg和ijk的版本信息,主要调用 ffpipeline_open_audio_output函数和stream_open函数。 

int ffp_prepare_async_l(FFPlayer *ffp, const char *file_name)
{
    assert(ffp);
    assert(!ffp->is);
    assert(file_name);

    if (av_stristart(file_name, "rtmp", NULL) ||
        av_stristart(file_name, "rtsp", NULL)) {//如果不是rtmp或者rtsp,则延时设为0
        // There is total different meaning for 'timeout' option in rtmp
        av_log(ffp, AV_LOG_WARNING, "remove 'timeout' option for rtmp.\n");
        av_dict_set(&ffp->format_opts, "timeout", NULL, 0);
    }

    /* there is a length limit in avformat */
    if (strlen(file_name) + 1 > 1024) {//如果文件长度长于1024的处理
        av_log(ffp, AV_LOG_ERROR, "%s too long url\n", __func__);
        if (avio_find_protocol_name("ijklongurl:")) {
            av_dict_set(&ffp->format_opts, "ijklongurl-url", file_name, 0);
            file_name = "ijklongurl:";
        }
    }
    //打印解码器的相关信息,包括Option的设置信息
    av_log(NULL, AV_LOG_INFO, "===== versions =====\n");
    ffp_show_version_str(ffp, "ijkplayer",      ijk_version_info());
    ffp_show_version_str(ffp, "FFmpeg",         av_version_info());
    ffp_show_version_int(ffp, "libavutil",      avutil_version());
    ffp_show_version_int(ffp, "libavcodec",     avcodec_version());
    ffp_show_version_int(ffp, "libavformat",    avformat_version());
    ffp_show_version_int(ffp, "libswscale",     swscale_version());
    ffp_show_version_int(ffp, "libswresample",  swresample_version());
    av_log(NULL, AV_LOG_INFO, "===== options =====\n");
    ffp_show_dict(ffp, "player-opts", ffp->player_opts);
    ffp_show_dict(ffp, "format-opts", ffp->format_opts);
    ffp_show_dict(ffp, "codec-opts ", ffp->codec_opts);
    ffp_show_dict(ffp, "sws-opts   ", ffp->sws_dict);
    ffp_show_dict(ffp, "swr-opts   ", ffp->swr_opts);
    av_log(NULL, AV_LOG_INFO, "===================\n");
    //设置option
    av_opt_set_dict(ffp, &ffp->player_opts);
    if (!ffp->aout) {
        //音频输出
        ffp->aout = ffpipeline_open_audio_output(ffp->pipeline, ffp);
        if (!ffp->aout)
            return -1;
    }
//滤镜
#if CONFIG_AVFILTER
    if (ffp->vfilter0) {
        GROW_ARRAY(ffp->vfilters_list, ffp->nb_vfilters);
        ffp->vfilters_list[ffp->nb_vfilters - 1] = ffp->vfilter0;
    }
#endif
    //打开流
    VideoState *is = stream_open(ffp, file_name, NULL);
    if (!is) {
        av_log(NULL, AV_LOG_WARNING, "ffp_prepare_async_l: stream_open failed OOM");
        return EIJK_OUT_OF_MEMORY;
    }

    ffp->is = is;
    ffp->input_filename = av_strdup(file_name);
    return 0;
}

 这个时候,开始重要的函数stream_open

stream_open为音、视频和字幕使用frame_queue_init初始化了单独的解码帧队列,初始化了时钟clock,创建了read_thread线程和video_refresh_thread,并进行初始化视频解码器init_video_decoder
init_clock调用了set_clock然后av_gettime_relative方法。

相关知识


     is->pictq为显示帧画面队列
     ffp->pictq_size为最小显示帧画面队列的长度,ffp->pictq_size = VIDEO_PICTURE_QUEUE_SIZE_DEFAULT;
     #define VIDEO_PICTURE_QUEUE_SIZE_MIN        (3)
      #define VIDEO_PICTURE_QUEUE_SIZE_MAX        (16)
      #define VIDEO_PICTURE_QUEUE_SIZE_DEFAULT    (VIDEO_PICTURE_QUEUE_SIZE_MIN)
      #define SUBPICTURE_QUEUE_SIZE 16
      #define SAMPLE_QUEUE_SIZE 9

SDL线程创建:SDL_CreateThread 

SDL线程等待:SDL_WaitThead

SDL互斥锁:SDL_CreateMutex / SDL_DestroyMutex

SDL锁定互斥:SDL_LockMutex / SDL_UnlockMutex

SDL 条件变量(信号量):SDL_CreateCond / SDL_DestoryCond

SDL 条件变量(信号量)等待 / 通知 :SDL_CondWait / SDL_CondSingal
 

static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)
{
    assert(!ffp->is);
    VideoState *is;

    is = av_mallocz(sizeof(VideoState));
    if (!is)
        return NULL;
    is->filename = av_strdup(filename);
    if (!is->filename)
        goto fail;
    is->iformat = iformat;
    is->ytop    = 0;
    is->xleft   = 0;
#if defined(__ANDROID__)
    if (ffp->soundtouch_enable) {
        is->handle = ijk_soundtouch_create();
    }
#endif


    if (frame_queue_init(&is->pictq, &is->videoq, ffp->pictq_size, 1) < 0)
        goto fail;
    if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
        goto fail;
    if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
        goto fail;

    if (packet_queue_init(&is->videoq) < 0 ||
        packet_queue_init(&is->audioq) < 0 ||
        packet_queue_init(&is->subtitleq) < 0)
        goto fail;
    /*
     * SDL线程创建:SDL_CreateThread 

SDL线程等待:SDL_WaitThead

SDL互斥锁:SDL_CreateMutex / SDL_DestroyMutex

SDL锁定互斥:SDL_LockMutex / SDL_UnlockMutex

SDL 条件变量(信号量):SDL_CreateCond / SDL_DestoryCond

SDL 条件变量(信号量)等待 / 通知 :SDL_CondWait / SDL_CondSingal
     */
    //分别给读线程/音频定位/视频定位/创建条件信号量
    if (!(is->continue_read_thread = SDL_CreateCond())) {
        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
        goto fail;
    }

    if (!(is->video_accurate_seek_cond = SDL_CreateCond())) {
        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
        ffp->enable_accurate_seek = 0;
    }

    if (!(is->audio_accurate_seek_cond = SDL_CreateCond())) {
        av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
        ffp->enable_accurate_seek = 0;
    }
    //初始化视频/音频/ext时钟
    init_clock(&is->vidclk, &is->videoq.serial);
    init_clock(&is->audclk, &is->audioq.serial);
    init_clock(&is->extclk, &is->extclk.serial);
    is->audio_clock_serial = -1;
    if (ffp->startup_volume < 0)
        av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", ffp->startup_volume);
    if (ffp->startup_volume > 100)
        av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", ffp->startup_volume);
    ffp->startup_volume = av_clip(ffp->startup_volume, 0, 100);
    ffp->startup_volume = av_clip(SDL_MIX_MAXVOLUME * ffp->startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
    is->audio_volume = ffp->startup_volume;
    is->muted = 0;
    is->av_sync_type = ffp->av_sync_type;
    //创建paly信号量
    is->play_mutex = SDL_CreateMutex();
    //创建seek信号量
    is->accurate_seek_mutex = SDL_CreateMutex();
    ffp->is = is;
    is->pause_req = !ffp->start_on_prepared;
    //创建刷新画面线程
    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->initialized_decoder = 0;
    //创建ff_read线程-read_thread(重要)
    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());
        goto fail;
    }

    if (ffp->async_init_decoder && !ffp->video_disable && ffp->video_mime_type && strlen(ffp->video_mime_type) > 0
                    && ffp->mediacodec_default_name && strlen(ffp->mediacodec_default_name) > 0) {
        if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2) {
            //初始化解码器,给viddec赋值videoq,continue_read_thread等
            decoder_init(&is->viddec, NULL, &is->videoq, is->continue_read_thread);
            //初始化解码器,通过反射调用android的mediacodec,包括解码器选择,比如是选择
            ffp->node_vdec = ffpipeline_init_video_decoder(ffp->pipeline, ffp);
        }
    }
    is->initialized_decoder = 1;

    return is;
fail:
    is->initialized_decoder = 1;
    is->abort_request = true;
    if (is->video_refresh_tid)
        SDL_WaitThread(is->video_refresh_tid, NULL);
    stream_close(ffp);
    return NULL;
}
video_refresh_thread:这个线程负责 video 的渲染。

下面分析ffpipeline_init_video_decoder方法,这个接口主要是对视频解码器的初始化,通过func_init_video_decoder对应了上篇文章定义的func_init_video_decoder

static IJKFF_Pipenode *func_init_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    IJKFF_Pipenode        *node = NULL;

    if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2)
        node = ffpipenode_init_decoder_from_android_mediacodec(ffp, pipeline, opaque->weak_vout);

    return node;
}
ffpipenode_init_decoder_from_android_mediacodec主要做了两个工作:定义解码接口func_run_sync和创建解码器,这两个也比较复杂,后期再用另外的文章做详细介绍
IJKFF_Pipenode *ffpipenode_init_decoder_from_android_mediacodec(FFPlayer *ffp, IJKFF_Pipeline *pipeline, SDL_Vout *vout)
{
.....

    node->func_destroy  = func_destroy;
    if (ffp->mediacodec_sync) {
        node->func_run_sync = func_run_sync_loop;
    } else {
        node->func_run_sync = func_run_sync;
    }

    opaque->acodec = SDL_AMediaCodecJava_createByCodecName(env, ffp->mediacodec_default_name);
....

}

read_thread读线程,这个重要的函数,后面会写专门的文章来介绍

其中在这里会调用

ffp->prepared = true;
    ffp_notify_msg1(ffp, FFP_MSG_PREPARED);
FFP_MSG_PREPARED这个消息就是通知说明解码器已经准备好
case FFP_MSG_PREPARED:
            MPTRACE("FFP_MSG_PREPARED:\n");
            post_event(env, weak_thiz, MEDIA_PREPARED, 0, 0);

发出MEDIA_PREPARED消息

            case MEDIA_PREPARED:
                player.notifyOnPrepared();
                return;

通知播放器

    protected final void notifyOnPrepared() {
        if (mOnPreparedListener != null)
            mOnPreparedListener.onPrepared(this);
    }

到此调用到了上层的onPrepared回调,播放器已经准备好

你可能感兴趣的:(流媒体,android,jikplayer)