我们在IjkMediaPlayer.java中有一个函数setOption,用过ijkplayer的开发者都知道这个函数,
private native void _setOption(int category, String name, String value);
private native void _setOption(int category, String name, long value);
这个setOption就像是ijkplayer的开关设置一样,可以打开某一个特定的功能或者特性,有三个参数。
category 有四种情况:
OPT_CATEGORY_FORMAT表示格式方面的设置,一般直接操作的是avformat模块;
OPT_CATEGORY_CODEC是编解码方面的设置,一般直接操作的是avcodec模块;
OPT_CATEGORY_SWS是图像大小方面的设置,一般直接操作的是swscale模块;
OPT_CATEGORY_PLAYER是播放器方面的设置,这个就是ijkplayer方面定制的参数了,我们优化播放器性能方面主要靠OPT_CATEGORY_PLAYER参数的选择了。
public static final int OPT_CATEGORY_FORMAT = 1;
public static final int OPT_CATEGORY_CODEC = 2;
public static final int OPT_CATEGORY_SWS = 3;
public static final int OPT_CATEGORY_PLAYER = 4;
name和value就是设置的key-value对象。
设置option最终会调用到ff_ffplay.c中的ffp_set_option函数,如下:
void ffp_set_option(FFPlayer *ffp, int opt_category, const char *name, const char *value)
{
if (!ffp)
return;
AVDictionary **dict = ffp_get_opt_dict(ffp, opt_category);
av_dict_set(dict, name, value, 0);
}
ffp_get_opt_dict函数正好说明了ff_ffplay.c中的4中类型的设置,这儿看上去有5个category,但是实际上我们真正用到的只是我上面提到的4个。
static AVDictionary **ffp_get_opt_dict(FFPlayer *ffp, int opt_category)
{
assert(ffp);
switch (opt_category) {
case FFP_OPT_CATEGORY_FORMAT: return &ffp->format_opts;
case FFP_OPT_CATEGORY_CODEC: return &ffp->codec_opts;
case FFP_OPT_CATEGORY_SWS: return &ffp->sws_dict;
case FFP_OPT_CATEGORY_PLAYER: return &ffp->player_opts;
case FFP_OPT_CATEGORY_SWR: return &ffp->swr_opts;
default:
av_log(ffp, AV_LOG_ERROR, "unknown option category %d\n", opt_category);
return NULL;
}
}
ff_ffplay_def.h中的定义了几个全局变量,我们上层设置的option到底层都会存储在一个个对应的AVDictionary中。
ff_ffplay_def.h中的定义了一个FFPlayer结构体,这个结构体是ff_ffplay.h中重要的全局变量,ijkplayer层与底层ffmpeg各模块之前的交互都是通过这个FFPlayer结构体进行的。
/* format/codec options */
AVDictionary *format_opts;
AVDictionary *codec_opts;
AVDictionary *sws_dict;
AVDictionary *player_opts;
AVDictionary *swr_opts;
AVDictionary *swr_preset_opts;
那么ijkplayer中有哪些可供选择的option呢?
答案在ff_ffplay_options.h
static const AVOption ffp_context_options[] = {
// original options in ffplay.c
// FFP_MERGE: x, y, s, fs
{ "an", "disable audio",
OPTION_OFFSET(audio_disable), OPTION_INT(0, 0, 1) },
{ "vn", "disable video",
OPTION_OFFSET(video_disable), OPTION_INT(0, 0, 1) },
// FFP_MERGE: sn, ast, vst, sst
// TODO: ss
{ "nodisp", "disable graphical display",
OPTION_OFFSET(display_disable), OPTION_INT(0, 0, 1) },
{ "volume", "set startup volume 0=min 100=max",
OPTION_OFFSET(startup_volume), OPTION_INT(100, 0, 100) },
// FFP_MERGE: f, pix_fmt, stats
{ "fast", "non spec compliant optimizations",
OPTION_OFFSET(fast), OPTION_INT(0, 0, 1) },
// FFP_MERGE: genpts, drp, lowres, sync, autoexit, exitonkeydown, exitonmousedown
{ "loop", "set number of times the playback shall be looped",
OPTION_OFFSET(loop), OPTION_INT(1, INT_MIN, INT_MAX) },
{ "infbuf", "don't limit the input buffer size (useful with realtime streams)",
OPTION_OFFSET(infinite_buffer), OPTION_INT(0, 0, 1) },
{ "framedrop", "drop frames when cpu is too slow",
OPTION_OFFSET(framedrop), OPTION_INT(0, -1, 120) },
{ "seek-at-start", "set offset of player should be seeked",
OPTION_OFFSET(seek_at_start), OPTION_INT64(0, 0, INT_MAX) },
{ "subtitle", "decode subtitle stream",
OPTION_OFFSET(subtitle), OPTION_INT(0, 0, 1) },
// FFP_MERGE: window_title
#if CONFIG_AVFILTER
{ "af", "audio filters",
OPTION_OFFSET(afilters), OPTION_STR(NULL) },
{ "vf0", "video filters 0",
OPTION_OFFSET(vfilter0), OPTION_STR(NULL) },
#endif
{ "rdftspeed", "rdft speed, in msecs",
OPTION_OFFSET(rdftspeed), OPTION_INT(0, 0, INT_MAX) },
// FFP_MERGE: showmode, default, i, codec, acodec, scodec, vcodec
// TODO: autorotate
{ "find_stream_info", "read and decode the streams to fill missing information with heuristics" ,
OPTION_OFFSET(find_stream_info), OPTION_INT(1, 0, 1) },
// extended options in ff_ffplay.c
{ "max-fps", "drop frames in video whose fps is greater than max-fps",
OPTION_OFFSET(max_fps), OPTION_INT(31, -1, 121) },
{ "overlay-format", "fourcc of overlay format",
OPTION_OFFSET(overlay_format), OPTION_INT(SDL_FCC_RV32, INT_MIN, INT_MAX),
.unit = "overlay-format" },
{ "fcc-_es2", "", 0, OPTION_CONST(SDL_FCC__GLES2), .unit = "overlay-format" },
{ "fcc-i420", "", 0, OPTION_CONST(SDL_FCC_I420), .unit = "overlay-format" },
{ "fcc-yv12", "", 0, OPTION_CONST(SDL_FCC_YV12), .unit = "overlay-format" },
{ "fcc-rv16", "", 0, OPTION_CONST(SDL_FCC_RV16), .unit = "overlay-format" },
{ "fcc-rv24", "", 0, OPTION_CONST(SDL_FCC_RV24), .unit = "overlay-format" },
{ "fcc-rv32", "", 0, OPTION_CONST(SDL_FCC_RV32), .unit = "overlay-format" },
{ "start-on-prepared", "automatically start playing on prepared",
OPTION_OFFSET(start_on_prepared), OPTION_INT(1, 0, 1) },
{ "video-pictq-size", "max picture queue frame count",
OPTION_OFFSET(pictq_size), OPTION_INT(VIDEO_PICTURE_QUEUE_SIZE_DEFAULT,
VIDEO_PICTURE_QUEUE_SIZE_MIN,
VIDEO_PICTURE_QUEUE_SIZE_MAX) },
{ "max-buffer-size", "max buffer size should be pre-read",
OPTION_OFFSET(dcc.max_buffer_size), OPTION_INT(MAX_QUEUE_SIZE, 0, MAX_QUEUE_SIZE) },
{ "min-frames", "minimal frames to stop pre-reading",
OPTION_OFFSET(dcc.min_frames), OPTION_INT(DEFAULT_MIN_FRAMES, MIN_MIN_FRAMES, MAX_MIN_FRAMES) },
{ "first-high-water-mark-ms", "first chance to wakeup read_thread",
OPTION_OFFSET(dcc.first_high_water_mark_in_ms),
OPTION_INT(DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS,
DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS,
DEFAULT_LAST_HIGH_WATER_MARK_IN_MS) },
{ "next-high-water-mark-ms", "second chance to wakeup read_thread",
OPTION_OFFSET(dcc.next_high_water_mark_in_ms),
OPTION_INT(DEFAULT_NEXT_HIGH_WATER_MARK_IN_MS,
DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS,
DEFAULT_LAST_HIGH_WATER_MARK_IN_MS) },
{ "last-high-water-mark-ms", "last chance to wakeup read_thread",
OPTION_OFFSET(dcc.last_high_water_mark_in_ms),
OPTION_INT(DEFAULT_LAST_HIGH_WATER_MARK_IN_MS,
DEFAULT_FIRST_HIGH_WATER_MARK_IN_MS,
DEFAULT_LAST_HIGH_WATER_MARK_IN_MS) },
{ "packet-buffering", "pause output until enough packets have been read after stalling",
OPTION_OFFSET(packet_buffering), OPTION_INT(1, 0, 1) },
{ "sync-av-start", "synchronise a/v start time",
OPTION_OFFSET(sync_av_start), OPTION_INT(1, 0, 1) },
{ "iformat", "force format",
OPTION_OFFSET(iformat_name), OPTION_STR(NULL) },
{ "no-time-adjust", "return player's real time from the media stream instead of the adjusted time",
OPTION_OFFSET(no_time_adjust), OPTION_INT(0, 0, 1) },
{ "preset-5-1-center-mix-level", "preset center-mix-level for 5.1 channel",
OPTION_OFFSET(preset_5_1_center_mix_level), OPTION_DOUBLE(M_SQRT1_2, -32, 32) },
{ "enable-accurate-seek", "enable accurate seek",
OPTION_OFFSET(enable_accurate_seek), OPTION_INT(0, 0, 1) },
{ "accurate-seek-timeout", "accurate seek timeout",
OPTION_OFFSET(accurate_seek_timeout), OPTION_INT(MAX_ACCURATE_SEEK_TIMEOUT, 0, MAX_ACCURATE_SEEK_TIMEOUT) },
{ "skip-calc-frame-rate", "don't calculate real frame rate",
OPTION_OFFSET(skip_calc_frame_rate), OPTION_INT(0, 0, 1) },
{ "get-frame-mode", "warning, this option only for get frame",
OPTION_OFFSET(get_frame_mode), OPTION_INT(0, 0, 1) },
{ "async-init-decoder", "async create decoder",
OPTION_OFFSET(async_init_decoder), OPTION_INT(0, 0, 1) },
{ "video-mime-type", "default video mime type",
OPTION_OFFSET(video_mime_type), OPTION_STR(NULL) },
// iOS only options
{ "videotoolbox", "VideoToolbox: enable",
OPTION_OFFSET(videotoolbox), OPTION_INT(0, 0, 1) },
{ "videotoolbox-max-frame-width", "VideoToolbox: max width of output frame",
OPTION_OFFSET(vtb_max_frame_width), OPTION_INT(0, 0, INT_MAX) },
{ "videotoolbox-async", "VideoToolbox: use kVTDecodeFrame_EnableAsynchronousDecompression()",
OPTION_OFFSET(vtb_async), OPTION_INT(0, 0, 1) },
{ "videotoolbox-wait-async", "VideoToolbox: call VTDecompressionSessionWaitForAsynchronousFrames()",
OPTION_OFFSET(vtb_wait_async), OPTION_INT(1, 0, 1) },
{ "videotoolbox-handle-resolution-change", "VideoToolbox: handle resolution change automatically",
OPTION_OFFSET(vtb_handle_resolution_change), OPTION_INT(0, 0, 1) },
// Android only options
{ "mediacodec", "MediaCodec: enable H264 (deprecated by 'mediacodec-avc')",
OPTION_OFFSET(mediacodec_avc), OPTION_INT(0, 0, 1) },
{ "mediacodec-auto-rotate", "MediaCodec: auto rotate frame depending on meta",
OPTION_OFFSET(mediacodec_auto_rotate), OPTION_INT(0, 0, 1) },
{ "mediacodec-all-videos", "MediaCodec: enable all videos",
OPTION_OFFSET(mediacodec_all_videos), OPTION_INT(0, 0, 1) },
{ "mediacodec-avc", "MediaCodec: enable H264",
OPTION_OFFSET(mediacodec_avc), OPTION_INT(0, 0, 1) },
{ "mediacodec-hevc", "MediaCodec: enable HEVC",
OPTION_OFFSET(mediacodec_hevc), OPTION_INT(0, 0, 1) },
{ "mediacodec-mpeg2", "MediaCodec: enable MPEG2VIDEO",
OPTION_OFFSET(mediacodec_mpeg2), OPTION_INT(0, 0, 1) },
{ "mediacodec-mpeg4", "MediaCodec: enable MPEG4",
OPTION_OFFSET(mediacodec_mpeg4), OPTION_INT(0, 0, 1) },
{ "mediacodec-handle-resolution-change", "MediaCodec: handle resolution change automatically",
OPTION_OFFSET(mediacodec_handle_resolution_change), OPTION_INT(0, 0, 1) },
{ "opensles", "OpenSL ES: enable",
OPTION_OFFSET(opensles), OPTION_INT(0, 0, 1) },
{ "soundtouch", "SoundTouch: enable",
OPTION_OFFSET(soundtouch_enable), OPTION_INT(0, 0, 1) },
{ "mediacodec-sync", "mediacodec: use msg_queue for synchronise",
OPTION_OFFSET(mediacodec_sync), OPTION_INT(0, 0, 1) },
{ "mediacodec-default-name", "mediacodec default name",
OPTION_OFFSET(mediacodec_default_name), OPTION_STR(NULL) },
{ "ijkmeta-delay-init", "ijkmeta delay init",
OPTION_OFFSET(ijkmeta_delay_init), OPTION_INT(0, 0, 1) },
{ "render-wait-start", "render wait start",
OPTION_OFFSET(render_wait_start), OPTION_INT(0, 0, 1) },
{ NULL }
};
现在我们阐述一下每一个option的作用。
an : 不播放声音,相当于mute
vn : 不播放视频,只有声音
volume : 设置音量,在0 ~ 100中任意选择一个数值
loop : 设置循环播放的次数
infbuf : 当设置为1,不限制输入缓冲区的大小,一般用于RTSP中
framedrop : 丢帧数,在可以设置为0, -1, 120以内,一般而言,建议设置为5个丢帧比较合适
seek-at-start : seek之后,从特定位置开始播放,这是因为一般而言,ijkplayer seek操作之后会寻找最近的关键帧,所以seek之后不一定严格按照seek的位置开始播放,这时候这个属性就比较重要了,可以解决这个问题。
rdftspeed : 音频的变速
find_stream_info : 可以设置为0, 1, 表示是否请求stream_info,优化播放体验的时候可以考虑用这个参数。
max-fps : 设置的最大帧率,当实际帧率大于这个设置的最大帧率的时候,可以丢帧来满足这个设置的最大帧率。
start-on-prepared : 如果设置为1的话,prepared回调之后自动调用start
video-pictq-size : 帧队列的数量大小,这个根据实际情况设置,不建议设置过大。
max-buffer-size : 设置最大的缓存大小。
packet-buffering : 知道读取足够多的packet数据才开始播放
sync-av-start : 音视频同步的起始时间,默认的话是0,一般都不会手动设置的
iformat : 强制使用设置的iformat来识别
enable-accurate-seek : 保证精确的seek位置,之前说seek一般只会找到附近的关键帧,有了这个参数选择,可以保证seek到具体的位置
accurate-seek-timeout : 设置精确seek的超时时间
Android 下面特定的一些option设置:
mediacodec : 开启H264的硬解码,但是现在已经废弃了,用mediacodec-avc代替了。
mediacodec-auto-rotate : 硬解码情况下自动旋转,可以关注我的文章:ijkplayer 开源项目分析(七)ijkplayer自动旋转功能
mediacodec-all-videos : 对所有的video开启硬解码
mediacodec-avc : 替代了mediacodec option,对H264开启硬解码
mediacodec-hevc : 对H265开启硬解码
mediacodec-mpeg2 : 对MPEG2VIDEO开启硬解码
mediacodec-mpeg4 : 对MPEG4开启硬解码
mediacodec-handle-resolution-change : 硬解码下自动切换分辨率
opensles : 音频使用opensles来播放
soundtouch : 开启soundtouch声音变频
mediacodec-sync : 开启msg_queue来实现硬解码同步
render-wait-start : 等待开始之后才开始绘制
还有很多额外的option设置,这儿列以下,
libavcodec/options_table.h
static const AVOption avcodec_options[] = {
{"b", "set bitrate (in bits/s)", OFFSET(bit_rate), AV_OPT_TYPE_INT64, {.i64 = AV_CODEC_DEFAULT_BITRATE }, 0, INT64_MAX, A|V|E},
{"ab", "set bitrate (in bits/s)", OFFSET(bit_rate), AV_OPT_TYPE_INT64, {.i64 = 128*1000 }, 0, INT_MAX, A|E},
{"bt", "Set video bitrate tolerance (in bits/s). In 1-pass mode, bitrate tolerance specifies how far "
"ratecontrol is willing to deviate from the target average bitrate value. This is not related "
"to minimum/maximum bitrate. Lowering tolerance too much has an adverse effect on quality.",
OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE*20 }, 1, INT_MAX, V|E},
{"flags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, UINT_MAX, V|A|S|E|D, "flags"},
{"unaligned", "allow decoders to produce unaligned output", 0, AV_OPT_TYPE_CONST, { .i64 = AV_CODEC_FLAG_UNALIGNED }, INT_MIN, INT_MAX, V | D, "flags" },
{"mv4", "use four motion vectors per macroblock (MPEG-4)", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_4MV }, INT_MIN, INT_MAX, V|E, "flags"},
{"qpel", "use 1/4-pel motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QPEL }, INT_MIN, INT_MAX, V|E, "flags"},
{"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, "flags"},
{"qscale", "use fixed qscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QSCALE }, INT_MIN, INT_MAX, 0, "flags"},
{"pass1", "use internal 2-pass ratecontrol in first pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS1 }, INT_MIN, INT_MAX, 0, "flags"},
{"pass2", "use internal 2-pass ratecontrol in second pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS2 }, INT_MIN, INT_MAX, 0, "flags"},
{"gray", "only decode/encode grayscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GRAY }, INT_MIN, INT_MAX, V|E|D, "flags"},
{"psnr", "error[?] variables will be set during encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PSNR }, INT_MIN, INT_MAX, V|E, "flags"},
{"truncated", "Input bitstream might be randomly truncated", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_TRUNCATED }, INT_MIN, INT_MAX, V|D, "flags"},
{"ildct", "use interlaced DCT", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_DCT }, INT_MIN, INT_MAX, V|E, "flags"},
{"low_delay", "force low delay", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOW_DELAY }, INT_MIN, INT_MAX, V|D|E, "flags"},
{"global_header", "place global headers in extradata instead of every keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GLOBAL_HEADER }, INT_MIN, INT_MAX, V|A|E, "flags"},
{

本文深入分析了ijkplayer中的关键函数_setOption,介绍了四个主要类别:格式、编解码、图像处理和播放器设置。通过setOption可以开启或关闭特定功能,并详细列举了多个可用的选项,包括音频、视频、播放控制等,为优化ijkplayer的首帧显示和播放性能提供了关键信息。

4095

被折叠的 条评论
为什么被折叠?



