ijkplayer源码阅读

作者: 骆驼骑士 | 来源:发表于2020-05-23 15:40 被阅读0次

    本文主要针对B站开源播放器IJKPlayer的部分源码阅读笔记,包括Java代码和C代码,涉及到部分FFmpeg和SDL的接口调用(笔记未经过仔细整理)。

    哔哩哔哩 (゜-゜)つロ 干杯~-bilibili**

    https://github.com/Bilibili/ijkplayer

    ## [Java] IjkMediaPlayer.java

    实现了 android.media.MediaPlayer 类似的API.

    内部主要是native实现和交互. native的mediaPlayer保存在field, mNativeMediaPlayer里.

    主要的几个方法:

    • IjkMediaPlayer()
      • initPlayer()
        • native_init()
          • IjkMediaPlayer_native_init() // do nothing
        • native_setup()
          • IjkMediaPlayer_native_setup() -> @link native_setup()
    • setSurface()
      • native _setVideoSurface(surface);
        • SDL_VoutAndroid_SetAndroidSurface() // @link SDL_VoutAndroid_SetAndroidSurface()
        • ffpipeline_set_surface()
          • SDL_VoutAndroid_setAMediaCodec(pipeline->opaque->weak_vout, NULL)
    • setDataSource()
      • native _setDataSource()
        • ijkmp_set_data_source()
          • mp->data_source = strdup(url);
    • prepareAsync()
      • native IjkMediaPlayer_prepareAsync()
        • ijkmp_prepare_async() // @link ijkmp_prepare_async_l()
    • start()
      • native _start()
        • ijkmp_start()
          • ffp_notify_msg1(mp->ffplayer, FFP_REQ_START);
    • stop()
      • native _stop()
        • ijkmp_stop()
          • toggle_pause(ffp, 1);
          • msg_queue_abort(&ffp->msg_queue);
          • SDL_CondSignal(is->audio_accurate_seek_cond);
          • SDL_CondSignal(is->video_accurate_seek_cond);
    • native seekTo()
      • native ijkmp_seek_to_l()
        • ffp_notify_msg2(mp->ffplayer, FFP_REQ_SEEK, (int)msec);
    • native getCurrentPosition()
      • native ijkmp_get_current_position_l
        • ffp_get_current_position_l()
          • pos = fftime_to_milliseconds(is->seek_pos);
    • native getDuration()
      • native ijkmp_get_duration_l()
        • ffp_get_duration_l()
          • int64_t duration = fftime_to_milliseconds(is->ic->duration);
    • setOption
      • native _setOption()
        • ijkmp_set_option()
          • ffp_set_option(mp->ffplayer, opt_category, name, value);
            • av_dict_set
    • postEventFromNative() // @link postEventFromNative()
    • onNativeInvoke() // TODO
    • @link onSelectCodec()

    具体native的方法bind在 ijkplayer_jni.c 里的 g_methods 里.

    参数

    参数为了如下几个类别:

    • OPT_CATEGORY_FORMAT 1
    • OPT_CATEGORY_CODEC 2
    • OPT_CATEGORY_SWS 3
    • OPT_CATEGORY_PLAYER 4 ffplay参数

    IjkVideoView.java里设置的参数:

    • OPT_CATEGORY_PLAYER
      • mediacodec 0/1 硬解码
      • mediacodec-auto-rotate 0/1
      • mediacodec-handle-resolution-change 0/1
      • opensles 0/1 是否使用OpenSLES
      • overlay-format SDL_FCC_RV32(RGBX8888) / RGB565 / YV12
      • framedrop 1
      • start-on-prepared 0
    • OPT_CATEGORY_CODEC
      • skip_loop_filter 48
    • OPT_CATEGORY_FORMAT
      • http-detect-range-support 0
      • timeout 30 * 1000 * 1000
      • reconnect 1

    ### OPT_CATEGORY_PLAYER

    源码:
    ijkmedia\ijkplayer\ff_ffplay_options.h

    • start-on-prepared: automatically start playing on prepared
    • overlay-format: fourcc of overlay format
    • framedrop: drop frames when cpu is too slow
    • seek-at-start: set offset of player should be seeked
    • max-buffer-size max buffer size should be pre-read
    • min-frames minimal frames to stop pre-reading

    android only options

    • mediacodec: MediaCodec: enable H264 (deprecated by 'mediacodec-avc')
    • mediacodec-auto-rotate: MediaCodec: auto rotate frame depending on meta
    • mediacodec-hevc: MediaCodec: enable HEVC
    • mediacodec-handle-resolution-change: MediaCodec: handle resolution change automatically
    • opensles: OpenSL ES: enable

    [Native] ff_ffplay.c

    @link ffp_set_inject_opaque()
    // TODO

    @link SDL_VoutAndroid_SetAndroidSurface()
    ijkmedia\ijksdl\android\ijksdl_vout_android_surface.c

    ### ffp_prepare_async_l
    @link ffp_prepare_async_l

    int ffp_prepare_async_l(FFPlayer *ffp, const char *file_name)
    {
        av_opt_set_dict(ffp, &ffp->player_opts);
        ffp->aout = ffpipeline_open_audio_output(ffp->pipeline, ffp);
    
    
        VideoState *is = stream_open(ffp, file_name, NULL);  // _@link stream_open()_
    }
    

    @link stream_open()

    static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)
    {
        /* start video display */
        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;
    
    
        is->video_refresh_tid = SDL_CreateThreadEx(&is->_video_refresh_tid, **video_refresh_thread** , ffp, "ff_vout");
        is->read_tid = SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read");
    
        decoder_init(&is->viddec, NULL, &is->videoq, is->continue_read_thread);
    }
    
    
    static int **video_refresh_thread** (void *arg)
    {
        FFPlayer *ffp = arg;
        VideoState *is = ffp->is;
        double remaining_time = 0.0;
        while (!is->abort_request) {
            if (remaining_time > 0.0)
                av_usleep((int)(int64_t)(remaining_time * 1000000.0));
                remaining_time = REFRESH_RATE;
            if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
                video_refresh(ffp, &remaining_time);
        }
    
    
        return 0;
    }
    
    
    
    
    
    
    

    read_thread

    /* this thread gets the stream from the disk or the network */
    static int read_thread(void *arg)
    {
        AVPacket pkt1, *pkt = &pkt1;
    
    
        err = avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts);
        ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);
    
    
        st_index[AVMEDIA_TYPE_VIDEO] =
            av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);
        st_index[AVMEDIA_TYPE_AUDIO] =
            av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, st_index[AVMEDIA_TYPE_AUDIO], st_index[AVMEDIA_TYPE_VIDEO], NULL, 0);
        st_index[AVMEDIA_TYPE_SUBTITLE] =
            av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE, st_index[AVMEDIA_TYPE_SUBTITLE], (st_index[AVMEDIA_TYPE_AUDIO] >= 0 ? st_index[AVMEDIA_TYPE_AUDIO] : st_index[AVMEDIA_TYPE_VIDEO]), NULL, 0)
    
    
        stream_component_open(ffp, st_index[AVMEDIA_TYPE_AUDIO]);
        stream_component_open(ffp, st_index[AVMEDIA_TYPE_VIDEO]);
        stream_component_open(ffp, st_index[AVMEDIA_TYPE_SUBTITLE]);
    
    
        for (;;) {
            if (is->abort_request)
                break;
            if (is->seek_req) {
                ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);
            }
    
    
            ret = av_read_frame(ic, pkt);
    
        }
    }
    

    native_setup()

    @link native_setup()

    [Native] ijkplayer.c

    状态:

    • MP_STATE_IDLE
    • MP_STATE_INITIALIZED
    • MP_STATE_ASYNC_PREPARING
    • MP_STATE_PREPARED
    • MP_STATE_STARTED
    • MP_STATE_PAUSED
    • MP_STATE_COMPLETED
    • MP_STATE_STOPPED
    • MP_STATE_ERROR
    • MP_STATE_END

    参数类型:

    • IJKMP_OPT_CATEGORY_FORMAT 1
    • IJKMP_OPT_CATEGORY_CODEC 2
    • IJKMP_OPT_CATEGORY_SWS 3
    • IJKMP_OPT_CATEGORY_PLAYER 4
    • IJKMP_OPT_CATEGORY_SWR

    native_setup()

    @link native_setup()

    IjkMediaPlayer.java 构造器中 initPlayer() 函数中调用了

    native_setup(weakRef(this));
    

    IjkMediaPlayer_native_setup() at ijkplayer_jni.c

    static void IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
    {
        *mediaPlayer = ijkmp_android_create( **message_loop** )  //  _@link ijkmp_android_create()_
     _
    _
        IjkMediaPlayer.setNativeMediaPlayer(mediaPlayer);
    
    
        ijkmp_set_inject_opaque(); // -> _@link ffp_set_inject_opaque()_
        ijkmp_set_ijkio_inject_opaque();
        ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));  // _@link onSelectCodec()_
    }
    

    @link ijkmp_android_create() at ijkplayer_android.c

    IjkMediaPlayer *mp = ijkmp_create(msg_loop);  //  _@link ijkmp_create()_
    
    
    mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();   //  _@link SDL_VoutAndroid_CreateForAndroidSurface()_
    mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);
    
    
    ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout);
    

    @link ijkmp_create() at ijkplayer.c

    IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
    mp->ffplayer = ffp_create();  //  _@link ffp_create()_
    mp->msg_loop = **msg_loop** ;  // _@link message_loop_n()_
    return mp;
    

    @link ffp_create() at ff_ffplay.c

        FFPlayer* ffp = (FFPlayer*) av_mallocz(sizeof(FFPlayer));
    
    
        msg_queue_init(&ffp->msg_queue);
        ffp->af_mutex = SDL_CreateMutex();
        ffp->vf_mutex = SDL_CreateMutex();
    
    
        ffp_reset_internal(ffp);
        ffp->av_class = &ffp_context_class;
        ffp->meta = ijkmeta_create();
    
    
        av_opt_set_defaults(ffp);
    

    @link SDL_VoutAndroid_CreateForAndroidSurface() at ijkmedia\ijksdl\android\ijksdl_vout_android_surface.c
    -> SDL_VoutAndroid_CreateForANativeWindow at ijkmedia\ijksdl\android\ijksdl_vout_android_nativewindow.c

    SDL_Vout *SDL_VoutAndroid_CreateForANativeWindow()
    {
        SDL_Vout *vout = SDL_Vout_CreateInternal(sizeof(SDL_Vout_Opaque));
        opaque->native_window = NULL;
        opaque->egl = IJK_EGL_create();
        // ...
    }
    

    @link message_loop_n

    static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp) {
    
    
        while (1) {
            AVMessage msg;
            ijkmp_get_msg(mp, &msg, 1);
    
            switch(msg.what) {
                case ...:
                    post_event();
                    break;
            }
        }
    }
    
    
    
    
    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);  // to java ->  _@link postEventFromNative_
    }
    

    ijkmp_prepare_async_l()

    @link ijkmp_prepare_async_l()

    static int ijkmp_prepare_async_l(IjkMediaPlayer *mp) {
    
    
    
    
        msg_queue_start(&mp->ffplayer->msg_queue);
        mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, **ijkmp_msg_loop** , mp, "ff_msg_loop");
        int retval = ffp_prepare_async_l(mp->ffplayer, mp->data_source);  // _@link ffp_prepare_async_l_
    
    
    }
    
    
    static int **ijkmp_msg_loop** (void *arg)
    {
        IjkMediaPlayer *mp = arg;
        int ret = mp->msg_loop(arg);
        return ret;
    }
    

    Config

    • AVOption

    ijkmedia\ijkplayer\ff_ffplay_options.h

      ijkExoMediaPlayer = null;
                        if (this.f1460g != null) {
                            ijkExoMediaPlayer = new IjkMediaPlayer(new C01825());
                            IjkMediaPlayer.native_setLogLevel(3);
                            ijkExoMediaPlayer.setOnNativeInvokeListener(this.f1449R);
                            if (this.f1474u.f1411d) {
                                ijkExoMediaPlayer.setOption(4, "mediacodec", 1);                      // "MediaCodec: enable H264 (deprecated by 'mediacodec-avc')"
                            } else {
                                ijkExoMediaPlayer.setOption(4, "mediacodec", 0);
                            }
                            ijkExoMediaPlayer.setOption(4, "mediacodec-auto-rotate", 0);               // "MediaCodec: auto rotate frame depending on meta"
                            ijkExoMediaPlayer.setOption(4, "mediacodec-handle-resolution-change", 0);    // "MediaCodec: handle resolution change automatically",
                            ijkExoMediaPlayer.setOption(4, "opensles", 0);                               // "OpenSL ES: enable",
                            ijkExoMediaPlayer.setOption(4, "overlay-format", 842225234);                // "fourcc of overlay format",
                            ijkExoMediaPlayer.setOption(4, "framedrop", 1);                              // "drop frames when cpu is too slow",
                            String str2 = "start-on-prepared";                                           // "automatically start playing on prepared",
                            if (this.f1454a) {
                                j = 1;
                            }
                            ijkExoMediaPlayer.setOption(4, str2, j);
                            ijkExoMediaPlayer.setOption(1, "http-detect-range-support", 0);
                            ijkExoMediaPlayer.setOption(2, "skip_loop_filter", 0);
                            ijkExoMediaPlayer.setOption(2, "skip_frame", 0);
                            ijkExoMediaPlayer.setOption(1, "timeout", (long) ((int) ((this.f1474u.f1410c * 1000.0f) * 1000.0f)));
                            ijkExoMediaPlayer.setOption(1, "reconnect", 1);
                            ijkExoMediaPlayer.setOption(1, "analyzeduration", 90000000);
                            if (this.f1474u.f1415h != null) {
                                str2 = null;
                                for (String str3 : this.f1474u.f1415h.keySet()) {
                                    if (str2 == null) {
                                        str3 = String.format("%s: %s", new Object[]{str3, this.f1474u.f1415h.get(str3)});
                                    } else {
                                        str3 = str2 + "\r\n" + String.format("%s: %s", new Object[]{str3, this.f1474u.f1415h.get(str3)});
                                    }
                                    str2 = str3;
                                }
                                ijkExoMediaPlayer.setOption(1, "headers", str2);
                            }
                            IjkMediaPlayer.native_setLogLevel(5);
                            if (this.f1474u.f1412e != null && this.f1433B.mo1065c(uri)) {
                                this.f1433B.mo1062a(this.f1474u.f1412e);
                                this.f1433B.mo1061a(this.f1474u.f1413f);
                                this.f1432A = this.f1433B.mo1063b(uri);
                                if (this.f1432A.mo1056b().endsWith("mp4")) {
                                    ijkExoMediaPlayer.setOption(1, "cache_file_path", this.f1432A.mo1056b());
                                    str3 = "ijkio:cache:ffio:" + this.f1460g.toString();
                                } else if (this.f1432A.mo1056b().endsWith(IjkMediaMeta.IJKM_KEY_M3U8)) {
                                    ijkExoMediaPlayer.setOption(1, "cache_file_path", this.f1432A.mo1058c());
                                    ijkExoMediaPlayer.setOption(1, "scheme_proxy", "ijkhttpcache");
                                    ijkExoMediaPlayer.setOption(1, "protocol_whitelist", "tls,file,crypto,tcp,http,https,ijkhttpcache");
                                    if (new File(this.f1432A.mo1056b()).exists()) {
                                        str3 = this.f1432A.mo1056b();
                                    }
                                }
                                TXCLog.m416i(this.f1459f, "ijk media player");
                                break;
                            }
                        }
                        str3 = uri;
                        TXCLog.m416i(this.f1459f, "ijk media player");
    
    static IJKFF_Pipenode *func_open_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_create_video_decoder_from_android_mediacodec(ffp, pipeline, opaque->weak_vout);
        if (!node) {
            node = ffpipenode_create_video_decoder_from_ffplay(ffp);
        }
        return node
    }
    
    
    
    
    
    #define MEDIACODEC_MODULE_NAME "MediaCodec"
    
    
    
    
    
    
    
    
    IJKFF_Pipenode *ffpipenode_create_video_decoder_from_android_mediacodec(FFPlayer *ffp, IJKFF_Pipeline *pipeline, SDL_Vout *vout)
    {
        ALOGD("ffpipenode_create_video_decoder_from_android_mediacodec()\n")
    
    
        ffp_set_video_codec_info(ffp, MEDIACODEC_MODULE_NAME, opaque->mcc.codec_name);
        ffp->stat.vdec_type = FFP_PROPV_DECODER_MEDIACODEC;
    
    
    }
    
    #define AVCODEC_MODULE_NAME "avcodec"
    
    
    
    
    IJKFF_Pipenode *ffpipenode_create_video_decoder_from_ffplay(FFPlayer *ffp) {
    
    
    
    
        ffp_set_video_codec_info(ffp, AVCODEC_MODULE_NAME, avcodec_get_name(ffp->is->viddec.avctx->codec_id));
        ffp->stat.vdec_type = FFP_PROPV_DECODER_AVCODEC;
    }
    

    NOTE ATTRIBUTES

    Created Date: 2018-11-21 09:32:27
    Last Evernote Update Date: 2020-05-23 07:35:27

    相关文章

      网友评论

        本文标题:ijkplayer源码阅读

        本文链接:https://www.haomeiwen.com/subject/puflahtx.html