diff mbox series

[FFmpeg-devel,12/20] fftools/ffmpeg: improve WARN_MULTIPLE_OPT_USAGE()

Message ID 20231218095722.25879-12-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,01/20] fftools/ffmpeg_filter: only set framerate for video | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Anton Khirnov Dec. 18, 2023, 9:57 a.m. UTC
Currently it requires every single OPT_SPEC option to be accompanied by
an array of alternate names for this option. The vast majority of
options have no alternate names, resulting in a large numbers of
unnecessary single-element arrays that merely contain the option name.

Extend the option parsing API to allow marking options as having
alternate names, or as being the canonical name for some existing
alternatives. Use this new information to avoid the need for
abovementioned unnecessary single-element arrays.
---
 fftools/cmdutils.c        |  13 +--
 fftools/cmdutils.h        |  21 ++++-
 fftools/ffmpeg.h          |  29 +++----
 fftools/ffmpeg_demux.c    |  14 ----
 fftools/ffmpeg_mux_init.c |  41 ----------
 fftools/ffmpeg_opt.c      | 165 ++++++++++++++++++++++----------------
 6 files changed, 139 insertions(+), 144 deletions(-)
diff mbox series

Patch

diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c
index 26e5e6e986..44228ea637 100644
--- a/fftools/cmdutils.c
+++ b/fftools/cmdutils.c
@@ -233,7 +233,7 @@  static int opt_has_arg(const OptionDef *o)
 }
 
 static int write_option(void *optctx, const OptionDef *po, const char *opt,
-                        const char *arg)
+                        const char *arg, const OptionDef *defs)
 {
     /* new-style options contain an offset into optctx, old-style address of
      * a global var*/
@@ -313,8 +313,11 @@  static int write_option(void *optctx, const OptionDef *po, const char *opt,
     if (po->flags & OPT_EXIT)
         return AVERROR_EXIT;
 
-    if (sol)
+    if (sol) {
         sol->type = po->type;
+        sol->opt_canon = (po->flags & OPT_HAS_CANON) ?
+                         find_option(defs, po->u1.name_canon) : po;
+    }
 
     return 0;
 }
@@ -352,7 +355,7 @@  int parse_option(void *optctx, const char *opt, const char *arg,
         return AVERROR(EINVAL);
     }
 
-    ret = write_option(optctx, po, opt, arg);
+    ret = write_option(optctx, po, opt, arg, options);
     if (ret < 0)
         return ret;
 
@@ -395,7 +398,7 @@  int parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
     return 0;
 }
 
-int parse_optgroup(void *optctx, OptionGroup *g)
+int parse_optgroup(void *optctx, OptionGroup *g, const OptionDef *defs)
 {
     int i, ret;
 
@@ -418,7 +421,7 @@  int parse_optgroup(void *optctx, OptionGroup *g)
         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);
+        ret = write_option(optctx, o->opt, o->key, o->val, defs);
         if (ret < 0)
             return ret;
     }
diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h
index db91b788f8..53227abb47 100644
--- a/fftools/cmdutils.h
+++ b/fftools/cmdutils.h
@@ -118,6 +118,8 @@  typedef struct SpecifierOptList {
     SpecifierOpt    *opt;
     int           nb_opt;
 
+    /* Canonical option definition that was parsed into this list. */
+    const struct OptionDef *opt_canon;
     enum OptionType type;
 } SpecifierOptList;
 
@@ -160,6 +162,14 @@  typedef struct OptionDef {
  * output, or both. */
 #define OPT_INPUT       (1 << 10)
 #define OPT_OUTPUT      (1 << 11)
+
+/* This option is a "canonical" form, to which one or more alternatives
+ * exist. These alternatives are listed in u1.names_alt. */
+#define OPT_HAS_ALT     (1 << 12)
+/* This option is an alternative form of some other option, whose
+ * name is stored in u1.name_canon */
+#define OPT_HAS_CANON   (1 << 13)
+
      union {
         void *dst_ptr;
         int (*func_arg)(void *, const char *, const char *);
@@ -167,6 +177,15 @@  typedef struct OptionDef {
     } u;
     const char *help;
     const char *argname;
+
+    union {
+        /* Name of the canonical form of this option.
+         * Is valid when OPT_HAS_CANON is set. */
+        const char *name_canon;
+        /* A NULL-terminated list of alternate forms of this option.
+         * Is valid when OPT_HAS_ALT is set. */
+        const char * const *names_alt;
+    } u1;
 } OptionDef;
 
 /**
@@ -281,7 +300,7 @@  typedef struct OptionParseContext {
  *
  * @param optctx an app-specific options context. NULL for global options group
  */
-int parse_optgroup(void *optctx, OptionGroup *g);
+int parse_optgroup(void *optctx, OptionGroup *g, const OptionDef *defs);
 
 /**
  * Split the commandline into an intermediate form convenient for further
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 9905d16095..1a8254d422 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -775,43 +775,40 @@  void update_benchmark(const char *fmt, ...);
 #define SPECIFIER_OPT_FMT_f    "%f"
 #define SPECIFIER_OPT_FMT_dbl  "%lf"
 
-#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st)\
+#define WARN_MULTIPLE_OPT_USAGE(optname, type, idx, st)\
 {\
     char namestr[128] = "";\
+    const SpecifierOpt *so = &o->optname.opt[idx];\
     const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\
-    for (int _i = 0; opt_name_##name[_i]; _i++)\
-        av_strlcatf(namestr, sizeof(namestr), "-%s%s", opt_name_##name[_i], opt_name_##name[_i+1] ? (opt_name_##name[_i+2] ? ", " : " or ") : "");\
+    snprintf(namestr, sizeof(namestr), "-%s", o->optname.opt_canon->name);\
+    if (o->optname.opt_canon->flags & OPT_HAS_ALT) {\
+        const char * const *names_alt = o->optname.opt_canon->u1.names_alt;\
+        for (int _i = 0; names_alt[_i]; _i++)\
+            av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[_i]);\
+    }\
     av_log(NULL, AV_LOG_WARNING, "Multiple %s options specified for stream %d, only the last option '-%s%s%s "SPECIFIER_OPT_FMT_##type"' will be used.\n",\
-           namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", spec, so->u.type);\
+           namestr, st->index, o->optname.opt_canon->name, spec[0] ? ":" : "", spec, so->u.type);\
 }
 
 #define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
 {\
-    int _ret, _matches = 0;\
-    SpecifierOpt *so;\
+    int _ret, _matches = 0, _match_idx;\
     for (int _i = 0; _i < o->name.nb_opt; _i++) {\
         char *spec = o->name.opt[_i].specifier;\
         if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\
             outvar = o->name.opt[_i].u.type;\
-            so = &o->name.opt[_i];\
+            _match_idx = _i;\
             _matches++;\
         } else if (_ret < 0)\
             return _ret;\
     }\
-    if (_matches > 1)\
-       WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\
+    if (_matches > 1 && o->name.opt_canon)\
+       WARN_MULTIPLE_OPT_USAGE(name, type, _match_idx, st);\
 }
 
 const char *opt_match_per_type_str(const SpecifierOptList *sol,
                                    char mediatype);
 
-extern const char * const opt_name_codec_names[];
-extern const char * const opt_name_codec_tags[];
-extern const char * const opt_name_frame_rates[];
-#if FFMPEG_OPT_TOP
-extern const char * const opt_name_top_field_first[];
-#endif
-
 void *muxer_thread(void *arg);
 void *decoder_thread(void *arg);
 void *encoder_thread(void *arg);
diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c
index 5594286a79..6e6be149ff 100644
--- a/fftools/ffmpeg_demux.c
+++ b/fftools/ffmpeg_demux.c
@@ -38,20 +38,6 @@ 
 
 #include "libavformat/avformat.h"
 
-static const char *const opt_name_discard[]                   = {"discard", NULL};
-static const char *const opt_name_reinit_filters[]            = {"reinit_filter", NULL};
-static const char *const opt_name_fix_sub_duration[]          = {"fix_sub_duration", NULL};
-static const char *const opt_name_canvas_sizes[]              = {"canvas_size", NULL};
-static const char *const opt_name_guess_layout_max[]          = {"guess_layout_max", NULL};
-static const char *const opt_name_ts_scale[]                  = {"itsscale", NULL};
-static const char *const opt_name_hwaccels[]                  = {"hwaccel", NULL};
-static const char *const opt_name_hwaccel_devices[]           = {"hwaccel_device", NULL};
-static const char *const opt_name_hwaccel_output_formats[]    = {"hwaccel_output_format", NULL};
-static const char *const opt_name_autorotate[]                = {"autorotate", NULL};
-static const char *const opt_name_display_rotations[]         = {"display_rotation", NULL};
-static const char *const opt_name_display_hflips[]            = {"display_hflip", NULL};
-static const char *const opt_name_display_vflips[]            = {"display_vflip", NULL};
-
 typedef struct DemuxStream {
     InputStream ist;
 
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 6dbee50d1c..5ab3f14a01 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -49,47 +49,6 @@ 
 
 #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass"
 
-static const char *const opt_name_apad[]                      = {"apad", NULL};
-static const char *const opt_name_autoscale[]                 = {"autoscale", NULL};
-static const char *const opt_name_bits_per_raw_sample[]       = {"bits_per_raw_sample", NULL};
-static const char *const opt_name_bitstream_filters[]         = {"bsf", "absf", "vbsf", NULL};
-static const char *const opt_name_copy_initial_nonkeyframes[] = {"copyinkf", NULL};
-static const char *const opt_name_copy_prior_start[]          = {"copypriorss", NULL};
-static const char *const opt_name_disposition[]               = {"disposition", NULL};
-static const char *const opt_name_enc_time_bases[]            = {"enc_time_base", NULL};
-static const char *const opt_name_enc_stats_pre[]             = {"enc_stats_pre", NULL};
-static const char *const opt_name_enc_stats_post[]            = {"enc_stats_post", NULL};
-static const char *const opt_name_mux_stats[]                 = {"mux_stats", NULL};
-static const char *const opt_name_enc_stats_pre_fmt[]         = {"enc_stats_pre_fmt", NULL};
-static const char *const opt_name_enc_stats_post_fmt[]        = {"enc_stats_post_fmt", NULL};
-static const char *const opt_name_mux_stats_fmt[]             = {"mux_stats_fmt", NULL};
-static const char *const opt_name_filters[]                   = {"filter", "af", "vf", NULL};
-static const char *const opt_name_filter_scripts[]            = {"filter_script", NULL};
-static const char *const opt_name_fix_sub_duration_heartbeat[] = {"fix_sub_duration_heartbeat", NULL};
-static const char *const opt_name_fps_mode[]                  = {"fps_mode", NULL};
-static const char *const opt_name_force_fps[]                 = {"force_fps", NULL};
-static const char *const opt_name_forced_key_frames[]         = {"forced_key_frames", NULL};
-static const char *const opt_name_frame_aspect_ratios[]       = {"aspect", NULL};
-static const char *const opt_name_intra_matrices[]            = {"intra_matrix", NULL};
-static const char *const opt_name_inter_matrices[]            = {"inter_matrix", NULL};
-static const char *const opt_name_chroma_intra_matrices[]     = {"chroma_intra_matrix", NULL};
-static const char *const opt_name_max_frame_rates[]           = {"fpsmax", NULL};
-static const char *const opt_name_max_frames[]                = {"frames", "aframes", "vframes", "dframes", NULL};
-static const char *const opt_name_max_muxing_queue_size[]     = {"max_muxing_queue_size", NULL};
-static const char *const opt_name_muxing_queue_data_threshold[] = {"muxing_queue_data_threshold", NULL};
-static const char *const opt_name_pass[]                      = {"pass", NULL};
-static const char *const opt_name_passlogfiles[]              = {"passlogfile", NULL};
-static const char *const opt_name_presets[]                   = {"pre", "apre", "vpre", "spre", NULL};
-static const char *const opt_name_qscale[]                    = {"q", "qscale", NULL};
-static const char *const opt_name_rc_overrides[]              = {"rc_override", NULL};
-static const char *const opt_name_time_bases[]                = {"time_base", NULL};
-static const char *const opt_name_audio_channels[]            = {"ac", NULL};
-static const char *const opt_name_audio_ch_layouts[]          = {"channel_layout", "ch_layout", NULL};
-static const char *const opt_name_audio_sample_rate[]         = {"ar", NULL};
-static const char *const opt_name_frame_sizes[]               = {"s", NULL};
-static const char *const opt_name_frame_pix_fmts[]            = {"pix_fmt", NULL};
-static const char *const opt_name_sample_fmts[]               = {"sample_fmt", NULL};
-
 static int check_opt_bitexact(void *ctx, const AVDictionary *opts,
                               const char *opt_name, int flag)
 {
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 567eff917e..8b2c30e2e6 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -54,13 +54,6 @@ 
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
 
-const char *const opt_name_codec_names[]                      = {"c", "codec", "acodec", "vcodec", "scodec", "dcodec", NULL};
-const char *const opt_name_frame_rates[]                      = {"r", NULL};
-const char *const opt_name_codec_tags[]                       = {"tag", "atag", "vtag", "stag", NULL};
-#if FFMPEG_OPT_TOP
-const char *const opt_name_top_field_first[]                  = {"top", NULL};
-#endif
-
 HWDevice *filter_hw_device;
 
 char *vstats_filename;
@@ -1289,7 +1282,7 @@  static int open_files(OptionGroupList *l, const char *inout, Scheduler *sch,
         init_options(&o);
         o.g = g;
 
-        ret = parse_optgroup(&o, g);
+        ret = parse_optgroup(&o, g, options);
         if (ret < 0) {
             av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file "
                    "%s.\n", inout, g->arg);
@@ -1328,7 +1321,7 @@  int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch)
     }
 
     /* apply global options */
-    ret = parse_optgroup(sch, &octx.global_opts);
+    ret = parse_optgroup(sch, &octx.global_opts, options);
     if (ret < 0) {
         errmsg = "parsing global options";
         goto fail;
@@ -1430,6 +1423,15 @@  static int opt_adrift_threshold(void *optctx, const char *opt, const char *arg)
 }
 #endif
 
+static const char *const alt_bsf[]            = { "absf", "vbsf", NULL };
+static const char *const alt_channel_layout[] = { "ch_layout", NULL};
+static const char *const alt_codec[]          = { "c", "acodec", "vcodec", "scodec", "dcodec", NULL };
+static const char *const alt_filter[]         = { "af", "vf", NULL };
+static const char *const alt_frames[]         = { "aframes", "vframes", "dframes", NULL };
+static const char *const alt_pre[]            = { "apre", "vpre", "spre", NULL};
+static const char *const alt_qscale[]         = { "q", NULL};
+static const char *const alt_tag[]            = { "atag", "vtag", "stag", NULL };
+
 #define OFFSET(x) offsetof(OptionsContext, x)
 const OptionDef options[] = {
     /* main options */
@@ -1452,15 +1454,18 @@  const OptionDef options[] = {
     { "recast_media",           OPT_TYPE_BOOL, OPT_EXPERT,
         {              &recast_media },
         "allow recasting stream type in order to force a decoder of different media type" },
-    { "c",                      OPT_TYPE_STRING, OPT_SPEC | OPT_INPUT | OPT_OUTPUT,
+    { "c",                      OPT_TYPE_STRING, OPT_SPEC | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .off       = OFFSET(codec_names) },
-        "codec name", "codec" },
-    { "codec",                  OPT_TYPE_STRING, OPT_SPEC | OPT_INPUT | OPT_OUTPUT,
+        "codec name", "codec",
+        .u1.name_canon = "codec", },
+    { "codec",                  OPT_TYPE_STRING, OPT_SPEC | OPT_INPUT | OPT_OUTPUT | OPT_HAS_ALT,
         { .off       = OFFSET(codec_names) },
-        "codec name", "codec" },
-    { "pre",                    OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT,
+        "codec name", "codec",
+        .u1.names_alt = alt_codec, },
+    { "pre",                    OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT | OPT_HAS_ALT,
         { .off       = OFFSET(presets) },
-        "preset name", "preset" },
+        "preset name", "preset",
+        .u1.names_alt = alt_pre, },
     { "map",                    OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT,
         { .func_arg = opt_map },
         "set input stream mapping",
@@ -1512,9 +1517,10 @@  const OptionDef options[] = {
     { "program",                OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(program) },
         "add program with specified streams", "title=string:st=number..." },
-    { "dframes",                OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_PERFILE | OPT_EXPERT | OPT_OUTPUT,
+    { "dframes",                OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_PERFILE | OPT_EXPERT | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_data_frames },
-        "set the number of data frames to output", "number" },
+        "set the number of data frames to output", "number",
+        .u1.name_canon = "frames" },
     { "benchmark",              OPT_TYPE_BOOL, OPT_EXPERT,
         { &do_benchmark },
         "add timings for benchmarking" },
@@ -1594,24 +1600,29 @@  const OptionDef options[] = {
     { "copypriorss",            OPT_TYPE_INT, OPT_EXPERT | OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(copy_prior_start) },
         "copy or discard frames before start time" },
-    { "frames",                 OPT_TYPE_INT64, OPT_SPEC | OPT_OUTPUT,
+    { "frames",                 OPT_TYPE_INT64, OPT_SPEC | OPT_OUTPUT | OPT_HAS_ALT,
         { .off = OFFSET(max_frames) },
-        "set the number of frames to output", "number" },
-    { "tag",                    OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_INPUT,
+        "set the number of frames to output", "number",
+        .u1.names_alt = alt_frames, },
+    { "tag",                    OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_INPUT | OPT_HAS_ALT,
         { .off = OFFSET(codec_tags) },
-        "force codec tag/fourcc", "fourcc/tag" },
-    { "q",                      OPT_TYPE_DOUBLE, OPT_EXPERT | OPT_SPEC | OPT_OUTPUT,
+        "force codec tag/fourcc", "fourcc/tag",
+        .u1.names_alt = alt_tag, },
+    { "q",                      OPT_TYPE_DOUBLE, OPT_EXPERT | OPT_SPEC | OPT_OUTPUT | OPT_HAS_CANON,
         { .off = OFFSET(qscale) },
-        "use fixed quality scale (VBR)", "q" },
-    { "qscale",                 OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT,
+        "use fixed quality scale (VBR)", "q",
+        .u1.name_canon = "qscale", },
+    { "qscale",                 OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_ALT,
         { .func_arg = opt_qscale },
-        "use fixed quality scale (VBR)", "q" },
+        "use fixed quality scale (VBR)", "q",
+        .u1.names_alt = alt_qscale, },
     { "profile",                OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT,
         { .func_arg = opt_profile },
         "set profile", "profile" },
-    { "filter",                 OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT,
+    { "filter",                 OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT | OPT_HAS_ALT,
         { .off = OFFSET(filters) },
-        "set stream filtergraph", "filter_graph" },
+        "set stream filtergraph", "filter_graph",
+        .u1.names_alt = alt_filter, },
     { "filter_threads",         OPT_TYPE_FUNC, OPT_FUNC_ARG,
         { .func_arg = opt_filter_threads },
         "number of non-complex filter threads" },
@@ -1692,9 +1703,10 @@  const OptionDef options[] = {
         "format of the stats written with -stats_mux_pre" },
 
     /* video options */
-    { "vframes",                    OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT,
+    { "vframes",                    OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_video_frames },
-        "set the number of video frames to output", "number" },
+        "set the number of video frames to output", "number",
+        .u1.name_canon = "frames", },
     { "r",                          OPT_TYPE_STRING, OPT_VIDEO | OPT_SPEC | OPT_INPUT | OPT_OUTPUT,
         { .off = OFFSET(frame_rates) },
         "set frame rate (Hz value, fraction or abbreviation)", "rate" },
@@ -1728,9 +1740,10 @@  const OptionDef options[] = {
     { "rc_override",                OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT  | OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(rc_overrides) },
         "rate control override for specific intervals", "override" },
-    { "vcodec",                     OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT,
+    { "vcodec",                     OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_video_codec },
-        "force video codec ('copy' to copy stream)", "codec" },
+        "force video codec ('copy' to copy stream)", "codec",
+        .u1.name_canon = "codec", },
     { "timecode",                   OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT,
         { .func_arg = opt_timecode },
         "set initial TimeCode value.", "hh:mm:ss[:;.]ff" },
@@ -1749,9 +1762,10 @@  const OptionDef options[] = {
     { "vstats_version",             OPT_TYPE_INT,    OPT_VIDEO | OPT_EXPERT,
         { &vstats_version },
         "Version of the vstats format to use."},
-    { "vf",                         OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT,
+    { "vf",                         OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_video_filters },
-        "set video filters", "filter_graph" },
+        "set video filters", "filter_graph",
+        .u1.name_canon = "filter", },
     { "intra_matrix",               OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(intra_matrices) },
         "specify intra matrix coeffs", "matrix" },
@@ -1761,9 +1775,10 @@  const OptionDef options[] = {
     { "chroma_intra_matrix",        OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(chroma_intra_matrices) },
         "specify intra matrix coeffs", "matrix" },
-    { "vtag",                       OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT,
+    { "vtag",                       OPT_TYPE_FUNC,   OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_old2new },
-        "force video tag/fourcc", "fourcc/tag" },
+        "force video tag/fourcc", "fourcc/tag",
+        .u1.name_canon = "tag", },
     { "fps_mode",                   OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT,
         { .off = OFFSET(fps_mode) },
         "set framerate mode for matching video streams; overrides vsync" },
@@ -1804,9 +1819,10 @@  const OptionDef options[] = {
         "random access points" },
 
     /* audio options */
-    { "aframes",          OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT,
+    { "aframes",          OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_audio_frames },
-        "set the number of audio frames to output", "number" },
+        "set the number of audio frames to output", "number",
+        .u1.name_canon = "frames", },
     { "aq",               OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT,
         { .func_arg = opt_audio_qscale },
         "set audio quality (codec-specific)", "quality", },
@@ -1819,27 +1835,32 @@  const OptionDef options[] = {
     { "an",               OPT_TYPE_BOOL,    OPT_AUDIO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,
         { .off = OFFSET(audio_disable) },
         "disable audio" },
-    { "acodec",           OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT,
+    { "acodec",           OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_audio_codec },
-        "force audio codec ('copy' to copy stream)", "codec" },
+        "force audio codec ('copy' to copy stream)", "codec",
+        .u1.name_canon = "codec", },
     { "ab",               OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT,
         { .func_arg = opt_bitrate },
         "audio bitrate (please use -b:a)", "bitrate" },
-    { "atag",             OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT,
+    { "atag",             OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_old2new },
-        "force audio tag/fourcc", "fourcc/tag" },
+        "force audio tag/fourcc", "fourcc/tag",
+        .u1.name_canon = "tag", },
     { "sample_fmt",       OPT_TYPE_STRING,  OPT_AUDIO | OPT_EXPERT | OPT_SPEC | OPT_INPUT | OPT_OUTPUT,
         { .off = OFFSET(sample_fmts) },
         "set sample format", "format" },
-    { "channel_layout",   OPT_TYPE_STRING,  OPT_AUDIO | OPT_EXPERT | OPT_SPEC | OPT_INPUT | OPT_OUTPUT,
+    { "channel_layout",   OPT_TYPE_STRING,  OPT_AUDIO | OPT_EXPERT | OPT_SPEC | OPT_INPUT | OPT_OUTPUT | OPT_HAS_ALT,
         { .off = OFFSET(audio_ch_layouts) },
-        "set channel layout", "layout" },
-    { "ch_layout",        OPT_TYPE_STRING,  OPT_AUDIO | OPT_EXPERT | OPT_SPEC | OPT_INPUT | OPT_OUTPUT,
+        "set channel layout", "layout",
+        .u1.names_alt = alt_channel_layout, },
+    { "ch_layout",        OPT_TYPE_STRING,  OPT_AUDIO | OPT_EXPERT | OPT_SPEC | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .off = OFFSET(audio_ch_layouts) },
-        "set channel layout", "layout" },
-    { "af",               OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT,
+        "set channel layout", "layout",
+        .u1.name_canon = "channel_layout", },
+    { "af",               OPT_TYPE_FUNC,    OPT_AUDIO | OPT_FUNC_ARG  | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_audio_filters },
-        "set audio filters", "filter_graph" },
+        "set audio filters", "filter_graph",
+        .u1.name_canon = "filter", },
     { "guess_layout_max", OPT_TYPE_INT,     OPT_AUDIO | OPT_SPEC | OPT_EXPERT | OPT_INPUT,
         { .off = OFFSET(guess_layout_max) },
       "set the maximum number of channels to try to guess the channel layout" },
@@ -1848,12 +1869,14 @@  const OptionDef options[] = {
     { "sn",     OPT_TYPE_BOOL, OPT_SUBTITLE | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,
         { .off = OFFSET(subtitle_disable) },
         "disable subtitle" },
-    { "scodec", OPT_TYPE_FUNC, OPT_SUBTITLE | OPT_FUNC_ARG  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT,
+    { "scodec", OPT_TYPE_FUNC, OPT_SUBTITLE | OPT_FUNC_ARG  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_subtitle_codec },
-        "force subtitle codec ('copy' to copy stream)", "codec" },
-    { "stag",   OPT_TYPE_FUNC, OPT_SUBTITLE | OPT_FUNC_ARG  | OPT_EXPERT  | OPT_PERFILE | OPT_OUTPUT,
+        "force subtitle codec ('copy' to copy stream)", "codec",
+        .u1.name_canon = "codec", },
+    { "stag",   OPT_TYPE_FUNC, OPT_SUBTITLE | OPT_FUNC_ARG  | OPT_EXPERT  | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_old2new }
-        , "force subtitle tag/fourcc", "fourcc/tag" },
+        , "force subtitle tag/fourcc", "fourcc/tag",
+        .u1.name_canon = "tag" },
     { "fix_sub_duration", OPT_TYPE_BOOL, OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC | OPT_INPUT,
         { .off = OFFSET(fix_sub_duration) },
         "fix subtitles duration" },
@@ -1882,28 +1905,35 @@  const OptionDef options[] = {
         "0 = use frame rate (video) or sample rate (audio),"
         "-1 = match source time base", "ratio" },
 
-    { "bsf", OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | OPT_OUTPUT,
+    { "bsf", OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_HAS_ALT,
         { .off = OFFSET(bitstream_filters) },
-        "A comma-separated list of bitstream filters", "bitstream_filters" },
-    { "absf", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,
+        "A comma-separated list of bitstream filters", "bitstream_filters",
+        .u1.names_alt = alt_bsf, },
+    { "absf", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_old2new },
-        "deprecated", "audio bitstream_filters" },
-    { "vbsf", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,
+        "deprecated", "audio bitstream_filters",
+        .u1.name_canon = "bsf", },
+    { "vbsf", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_old2new },
-        "deprecated", "video bitstream_filters" },
+        "deprecated", "video bitstream_filters",
+        .u1.name_canon = "bsf", },
 
-    { "apre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,
+    { "apre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_preset },
-        "set the audio options to the indicated preset", "preset" },
-    { "vpre", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,
+        "set the audio options to the indicated preset", "preset",
+        .u1.name_canon = "pre", },
+    { "vpre", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_preset },
-        "set the video options to the indicated preset", "preset" },
-    { "spre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_SUBTITLE | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,
+        "set the video options to the indicated preset", "preset",
+        .u1.name_canon = "pre", },
+    { "spre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_SUBTITLE | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_preset },
-        "set the subtitle options to the indicated preset", "preset" },
-    { "fpre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT,
+        "set the subtitle options to the indicated preset", "preset",
+        .u1.name_canon = "pre", },
+    { "fpre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_preset },
-        "set options from indicated preset file", "filename" },
+        "set options from indicated preset file", "filename",
+        .u1.name_canon = "pre", },
 
     { "max_muxing_queue_size", OPT_TYPE_INT, OPT_SPEC | OPT_EXPERT | OPT_OUTPUT,
         { .off = OFFSET(max_muxing_queue_size) },
@@ -1913,9 +1943,10 @@  const OptionDef options[] = {
         "set the threshold after which max_muxing_queue_size is taken into account", "bytes" },
 
     /* data codec support */
-    { "dcodec", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT,
+    { "dcodec", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON,
         { .func_arg = opt_data_codec },
-        "force data codec ('copy' to copy stream)", "codec" },
+        "force data codec ('copy' to copy stream)", "codec",
+        .u1.name_canon = "codec", },
     { "dn", OPT_TYPE_BOOL, OPT_VIDEO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,
         { .off = OFFSET(data_disable) }, "disable data" },