ijkplayer源码阅读

本文主要针对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源码阅读)