本文主要针对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()
- native_init()
- initPlayer()
- setSurface()
- native _setVideoSurface(surface);
- SDL_VoutAndroid_SetAndroidSurface() // @link SDL_VoutAndroid_SetAndroidSurface()
- ffpipeline_set_surface()
- SDL_VoutAndroid_setAMediaCodec(pipeline->opaque->weak_vout, NULL)
- native _setVideoSurface(surface);
- setDataSource()
- native _setDataSource()
- ijkmp_set_data_source()
- mp->data_source = strdup(url);
- ijkmp_set_data_source()
- native _setDataSource()
- prepareAsync()
- native IjkMediaPlayer_prepareAsync()
- ijkmp_prepare_async() // @link ijkmp_prepare_async_l()
- native IjkMediaPlayer_prepareAsync()
- start()
- native _start()
- ijkmp_start()
- ffp_notify_msg1(mp->ffplayer, FFP_REQ_START);
- ijkmp_start()
- native _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);
- ijkmp_stop()
- native _stop()
- native seekTo()
- native ijkmp_seek_to_l()
- ffp_notify_msg2(mp->ffplayer, FFP_REQ_SEEK, (int)msec);
- native ijkmp_seek_to_l()
- native getCurrentPosition()
- native ijkmp_get_current_position_l
- ffp_get_current_position_l()
- pos = fftime_to_milliseconds(is->seek_pos);
- ffp_get_current_position_l()
- native ijkmp_get_current_position_l
- native getDuration()
- native ijkmp_get_duration_l()
- ffp_get_duration_l()
- int64_t duration = fftime_to_milliseconds(is->ic->duration);
- ffp_get_duration_l()
- native ijkmp_get_duration_l()
- setOption
- native _setOption()
- ijkmp_set_option()
- ffp_set_option(mp->ffplayer, opt_category, name, value);
- av_dict_set
- ffp_set_option(mp->ffplayer, opt_category, name, value);
- ijkmp_set_option()
- native _setOption()
- 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