mirror of https://git.ffmpeg.org/ffmpeg.git
Merge commit '8c9af5b2051b9927f845c7afdfeb30b82670ee77'
* commit '8c9af5b2051b9927f845c7afdfeb30b82670ee77': cmdutils: add a commandline pre-parser. Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
commit
33f9b2436a
241
cmdutils.c
241
cmdutils.c
|
@ -374,6 +374,29 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_optgroup(void *optctx, OptionGroup *g)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, "Parsing a group of options: %s %s.\n",
|
||||||
|
g->group_def->name, g->arg);
|
||||||
|
|
||||||
|
for (i = 0; i < g->nb_opts; i++) {
|
||||||
|
Option *o = &g->opts[i];
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, "Applying option %s (%s) with argument %s.\n",
|
||||||
|
o->key, o->opt->help, o->val);
|
||||||
|
|
||||||
|
ret = write_option(optctx, o->opt, o->key, o->val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, "Successfully parsed a group of options.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int locate_option(int argc, char **argv, const OptionDef *options,
|
int locate_option(int argc, char **argv, const OptionDef *options,
|
||||||
const char *optname)
|
const char *optname)
|
||||||
{
|
{
|
||||||
|
@ -507,6 +530,224 @@ int opt_default(void *optctx, const char *opt, const char *arg)
|
||||||
return AVERROR_OPTION_NOT_FOUND;
|
return AVERROR_OPTION_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether given option is a group separator.
|
||||||
|
*
|
||||||
|
* @return index of the group definition that matched or -1 if none
|
||||||
|
*/
|
||||||
|
static int match_group_separator(const OptionGroupDef *groups, const char *opt)
|
||||||
|
{
|
||||||
|
const OptionGroupDef *p = groups;
|
||||||
|
|
||||||
|
while (p->name) {
|
||||||
|
if (p->sep && !strcmp(p->sep, opt))
|
||||||
|
return p - groups;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finish parsing an option group.
|
||||||
|
*
|
||||||
|
* @param group_idx which group definition should this group belong to
|
||||||
|
* @param arg argument of the group delimiting option
|
||||||
|
*/
|
||||||
|
static void finish_group(OptionParseContext *octx, int group_idx,
|
||||||
|
const char *arg)
|
||||||
|
{
|
||||||
|
OptionGroupList *l = &octx->groups[group_idx];
|
||||||
|
OptionGroup *g;
|
||||||
|
|
||||||
|
GROW_ARRAY(l->groups, l->nb_groups);
|
||||||
|
g = &l->groups[l->nb_groups - 1];
|
||||||
|
|
||||||
|
*g = octx->cur_group;
|
||||||
|
g->arg = arg;
|
||||||
|
g->group_def = l->group_def;
|
||||||
|
#if CONFIG_SWSCALE
|
||||||
|
g->sws_opts = sws_opts;
|
||||||
|
#endif
|
||||||
|
g->codec_opts = codec_opts;
|
||||||
|
g->format_opts = format_opts;
|
||||||
|
|
||||||
|
codec_opts = NULL;
|
||||||
|
format_opts = NULL;
|
||||||
|
#if CONFIG_SWSCALE
|
||||||
|
sws_opts = NULL;
|
||||||
|
#endif
|
||||||
|
init_opts();
|
||||||
|
|
||||||
|
memset(&octx->cur_group, 0, sizeof(octx->cur_group));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add an option instance to currently parsed group.
|
||||||
|
*/
|
||||||
|
static void add_opt(OptionParseContext *octx, const OptionDef *opt,
|
||||||
|
const char *key, const char *val)
|
||||||
|
{
|
||||||
|
int global = !(opt->flags & (OPT_PERFILE | OPT_SPEC | OPT_OFFSET));
|
||||||
|
OptionGroup *g = global ? &octx->global_opts : &octx->cur_group;
|
||||||
|
|
||||||
|
GROW_ARRAY(g->opts, g->nb_opts);
|
||||||
|
g->opts[g->nb_opts - 1].opt = opt;
|
||||||
|
g->opts[g->nb_opts - 1].key = key;
|
||||||
|
g->opts[g->nb_opts - 1].val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_parse_context(OptionParseContext *octx,
|
||||||
|
const OptionGroupDef *groups)
|
||||||
|
{
|
||||||
|
static const OptionGroupDef global_group = { "global" };
|
||||||
|
const OptionGroupDef *g = groups;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(octx, 0, sizeof(*octx));
|
||||||
|
|
||||||
|
while (g->name)
|
||||||
|
g++;
|
||||||
|
octx->nb_groups = g - groups;
|
||||||
|
octx->groups = av_mallocz(sizeof(*octx->groups) * octx->nb_groups);
|
||||||
|
if (!octx->groups)
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
for (i = 0; i < octx->nb_groups; i++)
|
||||||
|
octx->groups[i].group_def = &groups[i];
|
||||||
|
|
||||||
|
octx->global_opts.group_def = &global_group;
|
||||||
|
octx->global_opts.arg = "";
|
||||||
|
|
||||||
|
init_opts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninit_parse_context(OptionParseContext *octx)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < octx->nb_groups; i++) {
|
||||||
|
OptionGroupList *l = &octx->groups[i];
|
||||||
|
|
||||||
|
for (j = 0; j < l->nb_groups; j++) {
|
||||||
|
av_freep(&l->groups[j].opts);
|
||||||
|
av_dict_free(&l->groups[j].codec_opts);
|
||||||
|
av_dict_free(&l->groups[j].format_opts);
|
||||||
|
#if CONFIG_SWSCALE
|
||||||
|
sws_freeContext(l->groups[j].sws_opts);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
av_freep(&l->groups);
|
||||||
|
}
|
||||||
|
av_freep(&octx->groups);
|
||||||
|
|
||||||
|
av_freep(&octx->cur_group.opts);
|
||||||
|
av_freep(&octx->global_opts.opts);
|
||||||
|
|
||||||
|
uninit_opts();
|
||||||
|
}
|
||||||
|
|
||||||
|
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
|
||||||
|
const OptionDef *options,
|
||||||
|
const OptionGroupDef *groups)
|
||||||
|
{
|
||||||
|
int optindex = 1;
|
||||||
|
|
||||||
|
/* perform system-dependent conversions for arguments list */
|
||||||
|
prepare_app_arguments(&argc, &argv);
|
||||||
|
|
||||||
|
init_parse_context(octx, groups);
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n");
|
||||||
|
|
||||||
|
while (optindex < argc) {
|
||||||
|
const char *opt = argv[optindex++], *arg;
|
||||||
|
const OptionDef *po;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt);
|
||||||
|
|
||||||
|
/* unnamed group separators, e.g. output filename */
|
||||||
|
if (opt[0] != '-' || !opt[1]) {
|
||||||
|
finish_group(octx, 0, opt);
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
opt++;
|
||||||
|
|
||||||
|
#define GET_ARG(arg) \
|
||||||
|
do { \
|
||||||
|
arg = argv[optindex++]; \
|
||||||
|
if (!arg) { \
|
||||||
|
av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);\
|
||||||
|
return AVERROR(EINVAL); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* named group separators, e.g. -i */
|
||||||
|
if ((ret = match_group_separator(groups, opt)) >= 0) {
|
||||||
|
GET_ARG(arg);
|
||||||
|
finish_group(octx, ret, arg);
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n",
|
||||||
|
groups[ret].name, arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* normal options */
|
||||||
|
po = find_option(options, opt);
|
||||||
|
if (po->name) {
|
||||||
|
if (po->flags & OPT_EXIT) {
|
||||||
|
/* optional argument, e.g. -h */
|
||||||
|
arg = argv[optindex++];
|
||||||
|
} else if (po->flags & HAS_ARG) {
|
||||||
|
GET_ARG(arg);
|
||||||
|
} else {
|
||||||
|
arg = "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
add_opt(octx, po, opt, arg);
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
|
||||||
|
"argument '%s'.\n", po->name, po->help, arg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AVOptions */
|
||||||
|
if (argv[optindex]) {
|
||||||
|
ret = opt_default(NULL, opt, argv[optindex]);
|
||||||
|
if (ret >= 0) {
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with "
|
||||||
|
"argument '%s'.\n", opt, argv[optindex]);
|
||||||
|
optindex++;
|
||||||
|
continue;
|
||||||
|
} else if (ret != AVERROR_OPTION_NOT_FOUND) {
|
||||||
|
av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' "
|
||||||
|
"with argument '%s'.\n", opt, argv[optindex]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* boolean -nofoo options */
|
||||||
|
if (opt[0] == 'n' && opt[1] == 'o' &&
|
||||||
|
(po = find_option(options, opt + 2)) &&
|
||||||
|
po->name && po->flags & OPT_BOOL) {
|
||||||
|
add_opt(octx, po, opt, "0");
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
|
||||||
|
"argument 0.\n", po->name, po->help);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'.\n", opt);
|
||||||
|
return AVERROR_OPTION_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (octx->cur_group.nb_opts || codec_opts || format_opts)
|
||||||
|
av_log(NULL, AV_LOG_WARNING, "Trailing options were found on the "
|
||||||
|
"commandline.\n");
|
||||||
|
|
||||||
|
av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int opt_loglevel(void *optctx, const char *opt, const char *arg)
|
int opt_loglevel(void *optctx, const char *opt, const char *arg)
|
||||||
{
|
{
|
||||||
const struct { const char *name; int level; } log_levels[] = {
|
const struct { const char *name; int level; } log_levels[] = {
|
||||||
|
|
88
cmdutils.h
88
cmdutils.h
|
@ -223,6 +223,94 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options
|
||||||
int parse_option(void *optctx, const char *opt, const char *arg,
|
int parse_option(void *optctx, const char *opt, const char *arg,
|
||||||
const OptionDef *options);
|
const OptionDef *options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An option extracted from the commandline.
|
||||||
|
* Cannot use AVDictionary because of options like -map which can be
|
||||||
|
* used multiple times.
|
||||||
|
*/
|
||||||
|
typedef struct Option {
|
||||||
|
const OptionDef *opt;
|
||||||
|
const char *key;
|
||||||
|
const char *val;
|
||||||
|
} Option;
|
||||||
|
|
||||||
|
typedef struct OptionGroupDef {
|
||||||
|
/**< group name */
|
||||||
|
const char *name;
|
||||||
|
/**
|
||||||
|
* Option to be used as group separator. Can be NULL for groups which
|
||||||
|
* are terminated by a non-option argument (e.g. ffmpeg output files)
|
||||||
|
*/
|
||||||
|
const char *sep;
|
||||||
|
} OptionGroupDef;
|
||||||
|
|
||||||
|
typedef struct OptionGroup {
|
||||||
|
const OptionGroupDef *group_def;
|
||||||
|
const char *arg;
|
||||||
|
|
||||||
|
Option *opts;
|
||||||
|
int nb_opts;
|
||||||
|
|
||||||
|
AVDictionary *codec_opts;
|
||||||
|
AVDictionary *format_opts;
|
||||||
|
struct SwsContext *sws_opts;
|
||||||
|
} OptionGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of option groups that all have the same group type
|
||||||
|
* (e.g. input files or output files)
|
||||||
|
*/
|
||||||
|
typedef struct OptionGroupList {
|
||||||
|
const OptionGroupDef *group_def;
|
||||||
|
|
||||||
|
OptionGroup *groups;
|
||||||
|
int nb_groups;
|
||||||
|
} OptionGroupList;
|
||||||
|
|
||||||
|
typedef struct OptionParseContext {
|
||||||
|
OptionGroup global_opts;
|
||||||
|
|
||||||
|
OptionGroupList *groups;
|
||||||
|
int nb_groups;
|
||||||
|
|
||||||
|
/* parsing state */
|
||||||
|
OptionGroup cur_group;
|
||||||
|
} OptionParseContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an options group and write results into optctx.
|
||||||
|
*
|
||||||
|
* @param optctx an app-specific options context. NULL for global options group
|
||||||
|
*/
|
||||||
|
int parse_optgroup(void *optctx, OptionGroup *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split the commandline into an intermediate form convenient for further
|
||||||
|
* processing.
|
||||||
|
*
|
||||||
|
* The commandline is assumed to be composed of options which either belong to a
|
||||||
|
* group (those with OPT_SPEC, OPT_OFFSET or OPT_PERFILE) or are global
|
||||||
|
* (everything else).
|
||||||
|
*
|
||||||
|
* A group (defined by an OptionGroupDef struct) is a sequence of options
|
||||||
|
* terminated by either a group separator option (e.g. -i) or a parameter that
|
||||||
|
* is not an option (doesn't start with -). A group without a separator option
|
||||||
|
* must always be first in the supplied groups list.
|
||||||
|
*
|
||||||
|
* All options within the same group are stored in one OptionGroup struct in an
|
||||||
|
* OptionGroupList, all groups with the same group definition are stored in one
|
||||||
|
* OptionGroupList in OptionParseContext.groups. The order of group lists is the
|
||||||
|
* same as the order of group definitions.
|
||||||
|
*/
|
||||||
|
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
|
||||||
|
const OptionDef *options,
|
||||||
|
const OptionGroupDef *groups);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free all allocated memory in an OptionParseContext.
|
||||||
|
*/
|
||||||
|
void uninit_parse_context(OptionParseContext *octx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the '-loglevel' option in the command line args and apply it.
|
* Find the '-loglevel' option in the command line args and apply it.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue