]> git.sesse.net Git - ffmpeg/blobdiff - ffmpeg_opt.c
Merge commit '69f086e0f90f23d89e5739b099a4f984fa6a7885'
[ffmpeg] / ffmpeg_opt.c
index df85230805a54c49829c4583d5465187cde137cc..fcd6048cfa1c4517629baa74d6c70957f34815f8 100644 (file)
@@ -95,10 +95,11 @@ static int intra_dc_precision = 8;
 static int do_psnr            = 0;
 static int input_sync;
 
-void reset_options(OptionsContext *o, int is_input)
+static int64_t recording_time = INT64_MAX;
+
+static void uninit_options(OptionsContext *o, int is_input)
 {
     const OptionDef *po = options;
-    OptionsContext bak= *o;
     int i;
 
     /* all OPT_SPEC and OPT_STRING can be freed in generic way */
@@ -126,25 +127,28 @@ void reset_options(OptionsContext *o, int is_input)
     av_freep(&o->audio_channel_maps);
     av_freep(&o->streamid_map);
 
+    if (is_input)
+        recording_time = o->recording_time;
+    else
+        recording_time = INT64_MAX;
+}
+
+static void init_options(OptionsContext *o, int is_input)
+{
     memset(o, 0, sizeof(*o));
 
-    if (is_input) {
-        o->recording_time = bak.recording_time;
-        if (o->recording_time != INT64_MAX)
-            av_log(NULL, AV_LOG_WARNING,
-                   "-t is not an input option, keeping it for the next output;"
-                   " consider fixing your command line.\n");
+    if (!is_input && recording_time != INT64_MAX) {
+        o->recording_time = recording_time;
+        av_log(NULL, AV_LOG_WARNING,
+                "-t is not an input option, keeping it for the next output;"
+                " consider fixing your command line.\n");
     } else
     o->recording_time = INT64_MAX;
     o->mux_max_delay  = 0.7;
     o->limit_filesize = UINT64_MAX;
     o->chapters_input_file = INT_MAX;
-
-    uninit_opts();
-    init_opts();
 }
 
-
 static int opt_frame_crop(void *optctx, const char *opt, const char *arg)
 {
     av_log(NULL, AV_LOG_FATAL, "Option '%s' has been removed, use the crop filter instead\n", opt);
@@ -244,8 +248,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
     if (map[0] == '[') {
         /* this mapping refers to lavfi output */
         const char *c = map + 1;
-        o->stream_maps = grow_array(o->stream_maps, sizeof(*o->stream_maps),
-                                    &o->nb_stream_maps, o->nb_stream_maps + 1);
+        GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
         m = &o->stream_maps[o->nb_stream_maps - 1];
         m->linklabel = av_get_token(&c, "]");
         if (!m->linklabel) {
@@ -273,8 +276,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
                 if (check_stream_specifier(input_files[file_idx]->ctx, input_files[file_idx]->ctx->streams[i],
                             *p == ':' ? p + 1 : p) <= 0)
                     continue;
-                o->stream_maps = grow_array(o->stream_maps, sizeof(*o->stream_maps),
-                                            &o->nb_stream_maps, o->nb_stream_maps + 1);
+                GROW_ARRAY(o->stream_maps, o->nb_stream_maps);
                 m = &o->stream_maps[o->nb_stream_maps - 1];
 
                 m->file_index   = file_idx;
@@ -302,8 +304,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg)
 static int opt_attach(void *optctx, const char *opt, const char *arg)
 {
     OptionsContext *o = optctx;
-    o->attachments = grow_array(o->attachments, sizeof(*o->attachments),
-                                &o->nb_attachments, o->nb_attachments + 1);
+    GROW_ARRAY(o->attachments, o->nb_attachments);
     o->attachments[o->nb_attachments - 1] = arg;
     return 0;
 }
@@ -315,9 +316,7 @@ static int opt_map_channel(void *optctx, const char *opt, const char *arg)
     AVStream *st;
     AudioChannelMap *m;
 
-    o->audio_channel_maps =
-        grow_array(o->audio_channel_maps, sizeof(*o->audio_channel_maps),
-                   &o->nb_audio_channel_maps, o->nb_audio_channel_maps + 1);
+    GROW_ARRAY(o->audio_channel_maps, o->nb_audio_channel_maps);
     m = &o->audio_channel_maps[o->nb_audio_channel_maps - 1];
 
     /* muted channel syntax */
@@ -565,14 +564,13 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
         if (!ist)
             exit(1);
 
-        input_streams = grow_array(input_streams, sizeof(*input_streams), &nb_input_streams, nb_input_streams + 1);
+        GROW_ARRAY(input_streams, nb_input_streams);
         input_streams[nb_input_streams - 1] = ist;
 
         ist->st = st;
         ist->file_index = nb_input_files;
         ist->discard = 1;
         st->discard  = AVDISCARD_ALL;
-        ist->opts = filter_codec_opts(codec_opts, ist->st->codec->codec_id, ic, st, choose_decoder(o, ic, st));
 
         ist->ts_scale = 1.0;
         MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st);
@@ -586,6 +584,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
         }
 
         ist->dec = choose_decoder(o, ic, st);
+        ist->opts = filter_codec_opts(o->g->codec_opts, ist->st->codec->codec_id, ic, st, ist->dec);
 
         ist->reinit_filters = -1;
         MATCH_PER_STREAM_OPT(reinit_filters, i, ist->reinit_filters, ic, st);
@@ -697,9 +696,8 @@ static void dump_attachment(AVStream *st, const char *filename)
     avio_close(out);
 }
 
-static int opt_input_file(void *optctx, const char *opt, const char *filename)
+static int open_input_file(OptionsContext *o, const char *filename)
 {
-    OptionsContext *o = optctx;
     AVFormatContext *ic;
     AVInputFormat *file_iformat = NULL;
     int err, i, ret;
@@ -732,7 +730,7 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
     }
     if (o->nb_audio_sample_rate) {
         snprintf(buf, sizeof(buf), "%d", o->audio_sample_rate[o->nb_audio_sample_rate - 1].u.i);
-        av_dict_set(&format_opts, "sample_rate", buf, 0);
+        av_dict_set(&o->g->format_opts, "sample_rate", buf, 0);
     }
     if (o->nb_audio_channels) {
         /* because we set audio_channels based on both the "ac" and
@@ -743,7 +741,7 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
                         AV_OPT_SEARCH_FAKE_OBJ)) {
             snprintf(buf, sizeof(buf), "%d",
                      o->audio_channels[o->nb_audio_channels - 1].u.i);
-            av_dict_set(&format_opts, "channels", buf, 0);
+            av_dict_set(&o->g->format_opts, "channels", buf, 0);
         }
     }
     if (o->nb_frame_rates) {
@@ -752,15 +750,15 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
         if (file_iformat && file_iformat->priv_class &&
             av_opt_find(&file_iformat->priv_class, "framerate", NULL, 0,
                         AV_OPT_SEARCH_FAKE_OBJ)) {
-            av_dict_set(&format_opts, "framerate",
+            av_dict_set(&o->g->format_opts, "framerate",
                         o->frame_rates[o->nb_frame_rates - 1].u.str, 0);
         }
     }
     if (o->nb_frame_sizes) {
-        av_dict_set(&format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0);
+        av_dict_set(&o->g->format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0);
     }
     if (o->nb_frame_pix_fmts)
-        av_dict_set(&format_opts, "pixel_format", o->frame_pix_fmts[o->nb_frame_pix_fmts - 1].u.str, 0);
+        av_dict_set(&o->g->format_opts, "pixel_format", o->frame_pix_fmts[o->nb_frame_pix_fmts - 1].u.str, 0);
 
     MATCH_PER_TYPE_OPT(codec_names, str,    video_codec_name, ic, "v");
     MATCH_PER_TYPE_OPT(codec_names, str,    audio_codec_name, ic, "a");
@@ -776,19 +774,19 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
     ic->interrupt_callback = int_cb;
 
     /* open the input file with generic avformat function */
-    err = avformat_open_input(&ic, filename, file_iformat, &format_opts);
+    err = avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts);
     if (err < 0) {
         print_error(filename, err);
         exit(1);
     }
-    assert_avoptions(format_opts);
+    assert_avoptions(o->g->format_opts);
 
     /* apply forced codec ids */
     for (i = 0; i < ic->nb_streams; i++)
         choose_decoder(o, ic, ic->streams[i]);
 
     /* Set AVCodecContext options for avformat_find_stream_info */
-    opts = setup_find_stream_info_opts(ic, codec_opts);
+    opts = setup_find_stream_info_opts(ic, o->g->codec_opts);
     orig_nb_streams = ic->nb_streams;
 
     /* If not enough info to get the stream parameters, we decode the
@@ -807,7 +805,7 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
 
     /* if seeking requested, we execute it */
     if (o->start_time != 0) {
-        ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);
+        ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, timestamp, 0);
         if (ret < 0) {
             av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
                    filename, (double)timestamp / AV_TIME_BASE);
@@ -820,7 +818,7 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
     /* dump the file content */
     av_dump_format(ic, nb_input_files, filename, 0);
 
-    input_files = grow_array(input_files, sizeof(*input_files), &nb_input_files, nb_input_files + 1);
+    GROW_ARRAY(input_files, nb_input_files);
     if (!(input_files[nb_input_files - 1] = av_mallocz(sizeof(*input_files[0]))))
         exit(1);
 
@@ -845,7 +843,6 @@ static int opt_input_file(void *optctx, const char *opt, const char *filename)
         av_dict_free(&opts[i]);
     av_freep(&opts);
 
-    reset_options(o, 1);
     return 0;
 }
 
@@ -928,8 +925,7 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e
     if (oc->nb_streams - 1 < o->nb_streamid_map)
         st->id = o->streamid_map[oc->nb_streams - 1];
 
-    output_streams = grow_array(output_streams, sizeof(*output_streams), &nb_output_streams,
-                                nb_output_streams + 1);
+    GROW_ARRAY(output_streams, nb_output_streams);
     if (!(ost = av_mallocz(sizeof(*ost))))
         exit(1);
     output_streams[nb_output_streams - 1] = ost;
@@ -943,7 +939,7 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e
         AVIOContext *s = NULL;
         char *buf = NULL, *arg = NULL, *preset = NULL;
 
-        ost->opts  = filter_codec_opts(codec_opts, ost->enc->id, oc, st, ost->enc);
+        ost->opts  = filter_codec_opts(o->g->codec_opts, ost->enc->id, oc, st, ost->enc);
 
         MATCH_PER_STREAM_OPT(presets, str, preset, oc, st);
         if (preset && (!(ret = get_preset_file_2(preset, ost->enc->name, &s)))) {
@@ -1014,10 +1010,10 @@ static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, e
     if (oc->oformat->flags & AVFMT_GLOBALHEADER)
         st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
-    av_opt_get_int(sws_opts, "sws_flags", 0, &ost->sws_flags);
-    av_opt_get_int   (swr_opts, "filter_type"  , 0, &ost->swr_filter_type);
-    av_opt_get_int   (swr_opts, "dither_method", 0, &ost->swr_dither_method);
-    av_opt_get_double(swr_opts, "dither_scale" , 0, &ost->swr_dither_scale);
+    av_opt_get_int(o->g->sws_opts, "sws_flags", 0, &ost->sws_flags);
+    av_opt_get_int   (o->g->swr_opts, "filter_type"  , 0, &ost->swr_filter_type);
+    av_opt_get_int   (o->g->swr_opts, "dither_method", 0, &ost->swr_dither_method);
+    av_opt_get_double(o->g->swr_opts, "dither_scale" , 0, &ost->swr_dither_scale);
 
     ost->source_index = source_index;
     if (source_index >= 0) {
@@ -1440,9 +1436,8 @@ static int configure_complex_filters(void)
     return 0;
 }
 
-void opt_output_file(void *optctx, const char *filename)
+static int open_output_file(OptionsContext *o, const char *filename)
 {
-    OptionsContext *o = optctx;
     AVFormatContext *oc;
     int i, j, err;
     AVOutputFormat *file_oformat;
@@ -1626,7 +1621,7 @@ loop_end:
         ost = output_streams[i];
 
         if (   ost->stream_copy
-            && (e = av_dict_get(codec_opts, "flags", NULL, AV_DICT_IGNORE_SUFFIX))
+            && (e = av_dict_get(o->g->codec_opts, "flags", NULL, AV_DICT_IGNORE_SUFFIX))
             && (!e->key[5] || check_stream_specifier(oc, ost->st, e->key+6)))
             if (av_opt_set(ost->st->codec, "flags", e->value, 0) < 0)
                 exit(1);
@@ -1667,7 +1662,7 @@ loop_end:
         avio_close(pb);
     }
 
-    output_files = grow_array(output_files, sizeof(*output_files), &nb_output_files, nb_output_files + 1);
+    GROW_ARRAY(output_files, nb_output_files);
     if (!(output_files[nb_output_files - 1] = av_mallocz(sizeof(*output_files[0]))))
         exit(1);
 
@@ -1679,7 +1674,7 @@ loop_end:
     output_files[nb_output_files - 1]->start_time     = o->start_time;
     output_files[nb_output_files - 1]->limit_filesize = o->limit_filesize;
     output_files[nb_output_files - 1]->shortest       = o->shortest;
-    av_dict_copy(&output_files[nb_output_files - 1]->opts, format_opts, 0);
+    av_dict_copy(&output_files[nb_output_files - 1]->opts, o->g->format_opts, 0);
 
     /* check filename in case of an image number is expected */
     if (oc->oformat->flags & AVFMT_NEEDNUMBER) {
@@ -1804,7 +1799,7 @@ loop_end:
         }
     }
 
-    reset_options(o, 0);
+    return 0;
 }
 
 static int opt_target(void *optctx, const char *opt, const char *arg)
@@ -1859,22 +1854,23 @@ static int opt_target(void *optctx, const char *opt, const char *arg)
         opt_video_codec(o, "c:v", "mpeg1video");
         opt_audio_codec(o, "c:a", "mp2");
         parse_option(o, "f", "vcd", options);
+        av_dict_set(&o->g->codec_opts, "b:v", arg, 0);
 
         parse_option(o, "s", norm == PAL ? "352x288" : "352x240", options);
         parse_option(o, "r", frame_rates[norm], options);
-        opt_default(NULL, "g", norm == PAL ? "15" : "18");
+        av_dict_set(&o->g->codec_opts, "g", norm == PAL ? "15" : "18", 0);
 
-        opt_default(NULL, "b:v", "1150000");
-        opt_default(NULL, "maxrate", "1150000");
-        opt_default(NULL, "minrate", "1150000");
-        opt_default(NULL, "bufsize", "327680"); // 40*1024*8;
+        av_dict_set(&o->g->codec_opts, "b:v", "1150000", 0);
+        av_dict_set(&o->g->codec_opts, "maxrate", "1150000", 0);
+        av_dict_set(&o->g->codec_opts, "minrate", "1150000", 0);
+        av_dict_set(&o->g->codec_opts, "bufsize", "327680", 0); // 40*1024*8;
 
-        opt_default(NULL, "b:a", "224000");
+        av_dict_set(&o->g->codec_opts, "b:a", "224000", 0);
         parse_option(o, "ar", "44100", options);
         parse_option(o, "ac", "2", options);
 
-        opt_default(NULL, "packetsize", "2324");
-        opt_default(NULL, "muxrate", "1411200"); // 2352 * 75 * 8;
+        av_dict_set(&o->g->format_opts, "packetsize", "2324", 0);
+        av_dict_set(&o->g->format_opts, "muxrate", "1411200", 0); // 2352 * 75 * 8;
 
         /* We have to offset the PTS, so that it is consistent with the SCR.
            SCR starts at 36000, but the first two packs contain only padding
@@ -1891,18 +1887,18 @@ static int opt_target(void *optctx, const char *opt, const char *arg)
         parse_option(o, "s", norm == PAL ? "480x576" : "480x480", options);
         parse_option(o, "r", frame_rates[norm], options);
         parse_option(o, "pix_fmt", "yuv420p", options);
-        opt_default(NULL, "g", norm == PAL ? "15" : "18");
+        av_dict_set(&o->g->codec_opts, "g", norm == PAL ? "15" : "18", 0);
 
-        opt_default(NULL, "b:v", "2040000");
-        opt_default(NULL, "maxrate", "2516000");
-        opt_default(NULL, "minrate", "0"); // 1145000;
-        opt_default(NULL, "bufsize", "1835008"); // 224*1024*8;
-        opt_default(NULL, "scan_offset", "1");
+        av_dict_set(&o->g->codec_opts, "b:v", "2040000", 0);
+        av_dict_set(&o->g->codec_opts, "maxrate", "2516000", 0);
+        av_dict_set(&o->g->codec_opts, "minrate", "0", 0); // 1145000;
+        av_dict_set(&o->g->codec_opts, "bufsize", "1835008", 0); // 224*1024*8;
+        av_dict_set(&o->g->codec_opts, "scan_offset", "1", 0);
 
-        opt_default(NULL, "b:a", "224000");
+        av_dict_set(&o->g->codec_opts, "b:a", "224000", 0);
         parse_option(o, "ar", "44100", options);
 
-        opt_default(NULL, "packetsize", "2324");
+        av_dict_set(&o->g->format_opts, "packetsize", "2324", 0);
 
     } else if (!strcmp(arg, "dvd")) {
 
@@ -1913,17 +1909,17 @@ static int opt_target(void *optctx, const char *opt, const char *arg)
         parse_option(o, "s", norm == PAL ? "720x576" : "720x480", options);
         parse_option(o, "r", frame_rates[norm], options);
         parse_option(o, "pix_fmt", "yuv420p", options);
-        opt_default(NULL, "g", norm == PAL ? "15" : "18");
+        av_dict_set(&o->g->codec_opts, "g", norm == PAL ? "15" : "18", 0);
 
-        opt_default(NULL, "b:v", "6000000");
-        opt_default(NULL, "maxrate", "9000000");
-        opt_default(NULL, "minrate", "0"); // 1500000;
-        opt_default(NULL, "bufsize", "1835008"); // 224*1024*8;
+        av_dict_set(&o->g->codec_opts, "b:v", "6000000", 0);
+        av_dict_set(&o->g->codec_opts, "maxrate", "9000000", 0);
+        av_dict_set(&o->g->codec_opts, "minrate", "0", 0); // 1500000;
+        av_dict_set(&o->g->codec_opts, "bufsize", "1835008", 0); // 224*1024*8;
 
-        opt_default(NULL, "packetsize", "2048");  // from www.mpucoder.com: DVD sectors contain 2048 bytes of data, this is also the size of one pack.
-        opt_default(NULL, "muxrate", "10080000"); // from mplex project: data_rate = 1260000. mux_rate = data_rate * 8
+        av_dict_set(&o->g->format_opts, "packetsize", "2048", 0);  // from www.mpucoder.com: DVD sectors contain 2048 bytes of data, this is also the size of one pack.
+        av_dict_set(&o->g->format_opts, "muxrate", "10080000", 0); // from mplex project: data_rate = 1260000. mux_rate = data_rate * 8
 
-        opt_default(NULL, "b:a", "448000");
+        av_dict_set(&o->g->codec_opts, "b:a", "448000", 0);
         parse_option(o, "ar", "48000", options);
 
     } else if (!strncmp(arg, "dv", 2)) {
@@ -1981,6 +1977,26 @@ static int opt_data_frames(void *optctx, const char *opt, const char *arg)
     return parse_option(o, "frames:d", arg, options);
 }
 
+static int opt_default_new(OptionsContext *o, const char *opt, const char *arg)
+{
+    int ret;
+    AVDictionary *cbak = codec_opts;
+    AVDictionary *fbak = format_opts;
+    codec_opts = NULL;
+    format_opts = NULL;
+
+    ret = opt_default(NULL, opt, arg);
+
+    av_dict_copy(&o->g->codec_opts , codec_opts, 0);
+    av_dict_copy(&o->g->format_opts, format_opts, 0);
+    av_dict_free(&codec_opts);
+    av_dict_free(&format_opts);
+    codec_opts = cbak;
+    format_opts = fbak;
+
+    return ret;
+}
+
 static int opt_preset(void *optctx, const char *opt, const char *arg)
 {
     OptionsContext *o = optctx;
@@ -1998,7 +2014,7 @@ static int opt_preset(void *optctx, const char *opt, const char *arg)
         }else
             av_log(NULL, AV_LOG_FATAL, "File for preset '%s' not found\n", arg);
         exit(1);
-}
+    }
 
     while (fgets(line, sizeof(line), f)) {
         char *key = tmp_line, *value, *endptr;
@@ -2017,7 +2033,7 @@ static int opt_preset(void *optctx, const char *opt, const char *arg)
         else if (!strcmp(key, "vcodec")) opt_video_codec   (o, key, value);
         else if (!strcmp(key, "scodec")) opt_subtitle_codec(o, key, value);
         else if (!strcmp(key, "dcodec")) opt_data_codec    (o, key, value);
-        else if (opt_default(NULL, key, value) < 0) {
+        else if (opt_default_new(o, key, value) < 0) {
             av_log(NULL, AV_LOG_FATAL, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n",
                    filename, line, key, value);
             exit(1);
@@ -2043,9 +2059,11 @@ static int opt_bitrate(void *optctx, const char *opt, const char *arg)
     OptionsContext *o = optctx;
     if(!strcmp(opt, "b")){
         av_log(NULL, AV_LOG_WARNING, "Please use -b:a or -b:v, -b is ambiguous\n");
-        return parse_option(o, "b:v", arg, options);
+        av_dict_set(&o->g->codec_opts, "b:v", arg, 0);
+        return 0;
     }
-    return opt_default(optctx, opt, arg);
+    av_dict_set(&o->g->codec_opts, opt, arg, 0);
+    return 0;
 }
 
 static int opt_qscale(void *optctx, const char *opt, const char *arg)
@@ -2068,10 +2086,11 @@ static int opt_profile(void *optctx, const char *opt, const char *arg)
     OptionsContext *o = optctx;
     if(!strcmp(opt, "profile")){
         av_log(NULL, AV_LOG_WARNING, "Please use -profile:a or -profile:v, -profile is ambiguous\n");
-        return parse_option(o, "profile:v", arg, options);
+        av_dict_set(&o->g->codec_opts, "profile:v", arg, 0);
+        return 0;
     }
-    return opt_default(optctx, opt, arg);
-
+    av_dict_set(&o->g->codec_opts, opt, arg, 0);
+    return 0;
 }
 
 static int opt_video_filters(void *optctx, const char *opt, const char *arg)
@@ -2111,9 +2130,9 @@ static int opt_timecode(void *optctx, const char *opt, const char *arg)
     char *tcr = av_asprintf("timecode=%s", arg);
     int ret = parse_option(o, "metadata:g", tcr, options);
     if (ret >= 0)
-        ret = opt_default(optctx, "gop_timecode", arg);
+        ret = av_dict_set(&o->g->codec_opts, "gop_timecode", arg, 0);
     av_free(tcr);
-    return ret;
+    return 0;
 }
 
 static int opt_channel_layout(void *optctx, const char *opt, const char *arg)
@@ -2160,8 +2179,7 @@ static int opt_audio_qscale(void *optctx, const char *opt, const char *arg)
 
 static int opt_filter_complex(void *optctx, const char *opt, const char *arg)
 {
-    filtergraphs = grow_array(filtergraphs, sizeof(*filtergraphs),
-                              &nb_filtergraphs, nb_filtergraphs + 1);
+    GROW_ARRAY(filtergraphs, nb_filtergraphs);
     if (!(filtergraphs[nb_filtergraphs - 1] = av_mallocz(sizeof(*filtergraphs[0]))))
         return AVERROR(ENOMEM);
     filtergraphs[nb_filtergraphs - 1]->index       = nb_filtergraphs - 1;
@@ -2242,6 +2260,94 @@ void show_usage(void)
     av_log(NULL, AV_LOG_INFO, "\n");
 }
 
+enum OptGroup {
+    GROUP_OUTFILE,
+    GROUP_INFILE,
+};
+
+static const OptionGroupDef groups[] = {
+    [GROUP_OUTFILE] = { "output file",  NULL },
+    [GROUP_INFILE]  = { "input file",   "i"  },
+};
+
+static int open_files(OptionGroupList *l, const char *inout,
+                      int (*open_file)(OptionsContext*, const char*))
+{
+    int i, ret;
+
+    for (i = 0; i < l->nb_groups; i++) {
+        OptionGroup *g = &l->groups[i];
+        OptionsContext o;
+
+        init_options(&o, !strcmp(inout, "input"));
+        o.g = g;
+
+        ret = parse_optgroup(&o, g);
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file "
+                   "%s.\n", inout, g->arg);
+            return ret;
+        }
+
+        av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\n", inout, g->arg);
+        ret = open_file(&o, g->arg);
+        uninit_options(&o, !strcmp(inout, "input"));
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Error opening %s file %s.\n",
+                   inout, g->arg);
+            return ret;
+        }
+        av_log(NULL, AV_LOG_DEBUG, "Successfully openened the file.\n");
+    }
+
+    return 0;
+}
+
+int ffmpeg_parse_options(int argc, char **argv)
+{
+    OptionParseContext octx;
+    uint8_t error[128];
+    int ret;
+
+    memset(&octx, 0, sizeof(octx));
+
+    /* split the commandline into an internal representation */
+    ret = split_commandline(&octx, argc, argv, options, groups,
+                            FF_ARRAY_ELEMS(groups));
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: ");
+        goto fail;
+    }
+
+    /* apply global options */
+    ret = parse_optgroup(NULL, &octx.global_opts);
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_FATAL, "Error parsing global options: ");
+        goto fail;
+    }
+
+    /* open input files */
+    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_FATAL, "Error opening input files: ");
+        goto fail;
+    }
+
+    /* open output files */
+    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
+    if (ret < 0) {
+        av_log(NULL, AV_LOG_FATAL, "Error opening output files: ");
+        goto fail;
+    }
+
+fail:
+    uninit_parse_context(&octx);
+    if (ret < 0) {
+        av_strerror(ret, error, sizeof(error));
+        av_log(NULL, AV_LOG_FATAL, "%s\n", error);
+    }
+    return ret;
+}
 
 static int opt_progress(void *optctx, const char *opt, const char *arg)
 {
@@ -2266,8 +2372,6 @@ const OptionDef options[] = {
 #include "cmdutils_common_opts.h"
     { "f",              HAS_ARG | OPT_STRING | OPT_OFFSET,           { .off       = OFFSET(format) },
         "force format", "fmt" },
-    { "i",              HAS_ARG | OPT_PERFILE,                       { .func_arg = opt_input_file },
-        "input file name", "filename" },
     { "y",              OPT_BOOL,                                    {              &file_overwrite },
         "overwrite output files" },
     { "n",              OPT_BOOL,                                    {              &no_file_overwrite },
@@ -2522,7 +2626,5 @@ const OptionDef options[] = {
     { "dn", OPT_BOOL | OPT_VIDEO | OPT_OFFSET, { .off = OFFSET(data_disable) },
         "disable data" },
 
-    { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, { .func_arg = opt_default },
-        "generic catch all option", "" },
     { NULL, },
 };