quantbox java_ijkplayer 开源项目分析(九)核心option解析

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

我们在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"},

{

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值