1. 对每个选项逐一解析, 如果没有带"-"的, 可能为选项参数(解析实际选项时同时被解析掉)或者实际的输入文件名. 选项参数会占argc 之一, 一般输入文件选项 最好放在最好, 否则会解析出错
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (*parse_arg_function)(void *, const char*))
{
const char *opt;
int optindex, handleoptions = 1, ret;
/* perform system-dependent conversions for arguments list */
prepare_app_arguments(&argc, &argv);
/* parse options */
optindex = 1;
while (optindex < argc) {
opt = argv[optindex++];
if (handleoptions && opt[0] == '-' && opt[1] != '\0') {
if (opt[1] == '-' && opt[2] == '\0') {
handleoptions = 0;
continue;
}
opt++;
if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0)
exit_program(1);
optindex += ret;/* 消耗掉选项参数, 0或者 1 */
} else {
if (parse_arg_function)
parse_arg_function(optctx, opt);
}
}
}
2. 解析单一选项
最后一个参数实际是一张表, 在ffplay或者其他主程序里面事先写好了, 以指针形式传进来的
前面这个头文件里面是一些共用的选项处理, 如help 之类的选项, 下面的是ffplay 里面的特殊处理
从选项的结构定义看, ffmpeg 对选项进行了分类, 也就是属性, 就是flags, 可见有些属性是可以叠加的.最终解析出的选项参数(如果有的话)会被存储那个union里面的指针dst_ptr里面, 这个指针对不同的选项来说是不同的, 可能是新分配的内存, 可能是全局变量, 分散在不同文件里面, 使用时直接使用表里面的名字即可, 可以说是比较巧妙的处理方法.
dst = po->flags & (OPT_OFFSET | OPT_SPEC) ? (uint8_t *)optctx + po->u.off
: po->u.dst_ptr;
typedef struct {
const char *name;
int flags;
#define HAS_ARG 0x0001
#define OPT_BOOL 0x0002
#define OPT_EXPERT 0x0004
#define OPT_STRING 0x0008
#define OPT_VIDEO 0x0010
#define OPT_AUDIO 0x0020
#define OPT_GRAB 0x0040
#define OPT_INT 0x0080
#define OPT_FLOAT 0x0100
#define OPT_SUBTITLE 0x0200
#define OPT_INT64 0x0400
#define OPT_EXIT 0x0800
#define OPT_DATA 0x1000
#define OPT_FUNC2 0x2000
#define OPT_OFFSET 0x4000 /* option is specified as an offset in a passed optctx */
#define OPT_SPEC 0x8000 /* option is to be stored in an array of SpecifierOpt.
Implies OPT_OFFSET. Next element after the offset is
an int containing element count in the array. */
#define OPT_TIME 0x10000
#define OPT_DOUBLE 0x20000
union {
void *dst_ptr;
int (*func_arg)(const char *, const char *);
int (*func2_arg)(void *, const char *, const char *);
size_t off;
} u;
const char *help;
const char *argname;
} OptionDef;
static const OptionDef options[] = {
#include "cmdutils_common_opts.h"
{ "x", HAS_ARG, { (void*)opt_width }, "force displayed width", "width" },
{ "y", HAS_ARG, { (void*)opt_height }, "force displayed height", "height" },
{ "s", HAS_ARG | OPT_VIDEO, { (void*)opt_frame_size }, "set frame size (WxH or abbreviation)", "size" },
{ "fs", OPT_BOOL, { (void*)&is_full_screen }, "force full screen" },
{ "an", OPT_BOOL, { (void*)&audio_disable }, "disable audio" },
{ "vn", OPT_BOOL, { (void*)&video_disable }, "disable video" },
{ "ast", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&wanted_stream[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_number" },
{ "vst", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&wanted_stream[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_number" },
{ "sst", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&wanted_stream[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_number" },
{ "ss", HAS_ARG, { (void*)&opt_seek }, "seek to a given position in seconds", "pos" },
{ "t", HAS_ARG, { (void*)&opt_duration }, "play \"duration\" seconds of audio/video", "duration" },
{ "bytes", OPT_INT | HAS_ARG, { (void*)&seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" },
{ "nodisp", OPT_BOOL, { (void*)&display_disable }, "disable graphical display" },
{ "f", HAS_ARG, { (void*)opt_format }, "force format", "fmt" },
{ "pix_fmt", HAS_ARG | OPT_EXPERT | OPT_VIDEO, { (void*)opt_frame_pix_fmt }, "set pixel format", "format" },
{ "stats", OPT_BOOL | OPT_EXPERT, { (void*)&show_status }, "show status", "" },
{ "bug", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&workaround_bugs }, "workaround bugs", "" },
{ "fast", OPT_BOOL | OPT_EXPERT, { (void*)&fast }, "non spec compliant optimizations", "" },
{ "genpts", OPT_BOOL | OPT_EXPERT, { (void*)&genpts }, "generate pts", "" },
{ "drp", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""},
{ "lowres", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&lowres }, "", "" },
{ "skiploop", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&skip_loop_filter }, "", "" },
{ "skipframe", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&skip_frame }, "", "" },
{ "skipidct", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&skip_idct }, "", "" },
{ "idct", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&idct }, "set idct algo", "algo" },
{ "ec", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&error_concealment }, "set error concealment options", "bit_mask" },
{ "sync", HAS_ARG | OPT_EXPERT, { (void*)opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" },
{ "autoexit", OPT_BOOL | OPT_EXPERT, { (void*)&autoexit }, "exit at the end", "" },
{ "exitonkeydown", OPT_BOOL | OPT_EXPERT, { (void*)&exit_on_keydown }, "exit on key down", "" },
{ "exitonmousedown", OPT_BOOL | OPT_EXPERT, { (void*)&exit_on_mousedown }, "exit on mouse down", "" },
{ "loop", OPT_INT | HAS_ARG | OPT_EXPERT, { (void*)&loop }, "set number of times the playback shall be looped", "loop count" },
{ "framedrop", OPT_BOOL | OPT_EXPERT, { (void*)&framedrop }, "drop frames when cpu is too slow", "" },
{ "window_title", OPT_STRING | HAS_ARG, { (void*)&window_title }, "set window title", "window title" },
#if CONFIG_AVFILTER
{ "vf", OPT_STRING | HAS_ARG, { (void*)&vfilters }, "video filters", "filter list" },
#endif
{ "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { (void*)&rdftspeed }, "rdft speed", "msecs" },
{ "showmode", HAS_ARG, {(void*)opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" },
{ "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, { (void*)opt_default }, "generic catch all option", "" },
{ "i", OPT_BOOL, {(void *)&dummy}, "read specified file", "input_file"},
{ "codec", HAS_ARG | OPT_FUNC2, {(void*)opt_codec}, "force decoder", "decoder" },
{ NULL, },
};
int parse_option(void *optctx, const char *opt, const char *arg,
const OptionDef *options)
{
const OptionDef *po;
int bool_val = 1;
int *dstcount;
void *dst;
po = find_option(options, opt);
if (!po->name && opt[0] == 'n' && opt[1] == 'o') {
/* handle 'no' bool option */
po = find_option(options, opt + 2);
if (!(po->name && (po->flags & OPT_BOOL)))
goto unknown_opt;
bool_val = 0;
}
if (!po->name)
po = find_option(options, "default");
if (!po->name) {
unknown_opt:
av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'\n", opt);
return AVERROR(EINVAL);
}
if (po->flags & HAS_ARG && !arg) {
av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'\n", opt);
return AVERROR(EINVAL);
}
/* new-style options contain an offset into optctx, old-style address of
* a global var*/
dst = po->flags & (OPT_OFFSET | OPT_SPEC) ? (uint8_t *)optctx + po->u.off
: po->u.dst_ptr;
if (po->flags & OPT_SPEC) {
SpecifierOpt **so = dst;
char *p = strchr(opt, ':');
dstcount = (int *)(so + 1);
*so = grow_array(*so, sizeof(**so), dstcount, *dstcount + 1);
(*so)[*dstcount - 1].specifier = av_strdup(p ? p + 1 : "");
dst = &(*so)[*dstcount - 1].u;
}
if (po->flags & OPT_STRING) {
char *str;
str = av_strdup(arg);
*(char **)dst = str;
} else if (po->flags & OPT_BOOL) {
*(int *)dst = bool_val;
} else if (po->flags & OPT_INT) {
*(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX);
} else if (po->flags & OPT_INT64) {
*(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX);
} else if (po->flags & OPT_TIME) {
*(int64_t *)dst = parse_time_or_die(opt, arg, 1);
} else if (po->flags & OPT_FLOAT) {
*(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY);
} else if (po->flags & OPT_DOUBLE) {
*(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY);
} else if (po->u.func_arg) {
int ret = po->flags & OPT_FUNC2 ? po->u.func2_arg(optctx, opt, arg)
: po->u.func_arg(opt, arg);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR,
"Failed to set value '%s' for option '%s'\n", arg, opt);
return ret;
}
}
if (po->flags & OPT_EXIT)
exit_program(0);
return !!(po->flags & HAS_ARG);
}