diff --git a/cmdutils.c b/cmdutils.c index dc32807b55..c1b81a176b 100644 --- a/cmdutils.c +++ b/cmdutils.c @@ -568,6 +568,7 @@ static void finish_group(OptionParseContext *octx, int group_idx, #if CONFIG_SWSCALE g->sws_opts = sws_opts; #endif + g->swr_opts = swr_opts; g->codec_opts = codec_opts; g->format_opts = format_opts; @@ -576,6 +577,7 @@ static void finish_group(OptionParseContext *octx, int group_idx, #if CONFIG_SWSCALE sws_opts = NULL; #endif + swr_opts = NULL; init_opts(); memset(&octx->cur_group, 0, sizeof(octx->cur_group)); @@ -635,6 +637,8 @@ void uninit_parse_context(OptionParseContext *octx) #if CONFIG_SWSCALE sws_freeContext(l->groups[j].sws_opts); #endif + if(CONFIG_SWRESAMPLE) + swr_free(&l->groups[j].swr_opts); } av_freep(&l->groups); } diff --git a/cmdutils.h b/cmdutils.h index 85ad0245d4..43cb6a09d9 100644 --- a/cmdutils.h +++ b/cmdutils.h @@ -254,6 +254,7 @@ typedef struct OptionGroup { AVDictionary *codec_opts; AVDictionary *format_opts; struct SwsContext *sws_opts; + struct SwrContext *swr_opts; } OptionGroup; /** diff --git a/ffmpeg.c b/ffmpeg.c index feff2c4f9e..73b5dff59c 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -3152,22 +3152,13 @@ static void log_callback_null(void *ptr, int level, const char *fmt, va_list vl) { } -static void parse_cpuflags(int argc, char **argv, const OptionDef *options) -{ - int idx = locate_option(argc, argv, options, "cpuflags"); - if (idx && argv[idx + 1]) - opt_cpuflags(NULL, "cpuflags", argv[idx + 1]); -} - int main(int argc, char **argv) { - OptionsContext o = { 0 }; + int ret; int64_t ti; atexit(exit_program); - reset_options(&o, 0); - setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */ av_log_set_flags(AV_LOG_SKIP_REPEATED); @@ -3192,10 +3183,10 @@ int main(int argc, char **argv) term_init(); - parse_cpuflags(argc, argv, options); - - /* parse options */ - parse_options(&o, argc, argv, options, opt_output_file); + /* parse options and open all input/output files */ + ret = ffmpeg_parse_options(argc, argv); + if (ret < 0) + exit(1); if (nb_output_files <= 0 && nb_input_files == 0) { show_usage(); diff --git a/ffmpeg.h b/ffmpeg.h index afef6fb98b..1260563cb8 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -71,6 +71,8 @@ typedef struct { } AudioChannelMap; typedef struct OptionsContext { + OptionGroup *g; + /* input/output options */ int64_t start_time; const char *format; @@ -413,4 +415,6 @@ int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOu int ist_in_filtergraph(FilterGraph *fg, InputStream *ist); FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost); +int ffmpeg_parse_options(int argc, char **argv); + #endif /* FFMPEG_H */ diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index 2b1563612f..6867a0073a 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -95,10 +95,9 @@ static int intra_dc_precision = 8; static int do_psnr = 0; static int input_sync; -void reset_options(OptionsContext *o, int is_input) +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,9 +125,16 @@ void reset_options(OptionsContext *o, int is_input) av_freep(&o->audio_channel_maps); av_freep(&o->streamid_map); + if (!is_input) + o->recording_time = INT64_MAX; +} + +static void init_options(OptionsContext *o, int is_input) +{ + OptionsContext bak= *o; memset(o, 0, sizeof(*o)); - if (is_input) { + if (!is_input) { o->recording_time = bak.recording_time; if (o->recording_time != INT64_MAX) av_log(NULL, AV_LOG_WARNING, @@ -139,12 +145,8 @@ void reset_options(OptionsContext *o, int is_input) 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); @@ -567,7 +569,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) 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->opts = filter_codec_opts(o->g->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); @@ -692,9 +694,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; @@ -727,7 +728,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 @@ -738,7 +739,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) { @@ -747,15 +748,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"); @@ -771,19 +772,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 @@ -840,7 +841,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; } @@ -937,7 +937,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)))) { @@ -1008,10 +1008,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) { @@ -1434,9 +1434,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; @@ -1620,7 +1619,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); @@ -1673,7 +1672,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) { @@ -1798,7 +1797,7 @@ loop_end: } } - reset_options(o, 0); + return 0; } static int opt_target(void *optctx, const char *opt, const char *arg) @@ -1853,22 +1852,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 @@ -1885,18 +1885,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")) { @@ -1907,17 +1907,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)) { @@ -1975,6 +1975,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; + AVCodecContext *cbak = codec_opts; + AVCodecContext *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; @@ -1992,7 +2012,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; @@ -2011,7 +2031,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); @@ -2037,9 +2057,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) @@ -2062,10 +2084,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) @@ -2105,9 +2128,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) @@ -2235,6 +2258,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" }, + { 0 }, +}; + +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); + 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) { @@ -2259,8 +2370,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 }, @@ -2515,7 +2624,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, }, };