diff --git a/ffmpeg.c b/ffmpeg.c index 1e45cd10b3..9cd6371fda 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -122,6 +122,8 @@ static int nb_output_files = 0; static StreamMap *stream_maps = NULL; static int nb_stream_maps; +static AVDictionary *codec_names; + /* first item specifies output metadata, second is input */ static MetadataMap (*meta_data_maps)[2] = NULL; static int nb_meta_data_maps; @@ -149,7 +151,6 @@ static uint16_t *inter_matrix = NULL; static const char *video_rc_override_string=NULL; static int video_disable = 0; static int video_discard = 0; -static char *video_codec_name = NULL; static unsigned int video_codec_tag = 0; static char *video_language = NULL; static int same_quant = 0; @@ -170,17 +171,14 @@ static int audio_sample_rate = 0; static float audio_qscale = QSCALE_NONE; static int audio_disable = 0; static int audio_channels = 0; -static char *audio_codec_name = NULL; static unsigned int audio_codec_tag = 0; static char *audio_language = NULL; static int subtitle_disable = 0; -static char *subtitle_codec_name = NULL; static char *subtitle_language = NULL; static unsigned int subtitle_codec_tag = 0; static int data_disable = 0; -static char *data_codec_name = NULL; static unsigned int data_codec_tag = 0; static float mux_preload= 0.5; @@ -197,10 +195,6 @@ static int do_pkt_dump = 0; static int do_psnr = 0; static int do_pass = 0; static const char *pass_logfilename_prefix; -static int audio_stream_copy = 0; -static int video_stream_copy = 0; -static int subtitle_stream_copy = 0; -static int data_stream_copy = 0; static int video_sync_method= -1; static int audio_sync_method= 0; static float audio_drift_threshold= 0.1; @@ -550,11 +544,6 @@ static int exit_program(int ret) av_freep(&input_streams); av_freep(&input_files); - av_free(video_codec_name); - av_free(audio_codec_name); - av_free(subtitle_codec_name); - av_free(data_codec_name); - uninit_opts(); av_free(audio_buf); av_free(audio_out); @@ -691,7 +680,59 @@ static void choose_pixel_fmt(AVStream *st, AVCodec *codec) } } -static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx, AVCodec *codec) +static enum CodecID find_codec_or_die(const char *name, enum AVMediaType type, int encoder) +{ + const char *codec_string = encoder ? "encoder" : "decoder"; + AVCodec *codec; + + if(!name) + return CODEC_ID_NONE; + codec = encoder ? + avcodec_find_encoder_by_name(name) : + avcodec_find_decoder_by_name(name); + if(!codec) { + av_log(NULL, AV_LOG_ERROR, "Unknown %s '%s'\n", codec_string, name); + exit_program(1); + } + if(codec->type != type) { + av_log(NULL, AV_LOG_ERROR, "Invalid %s type '%s'\n", codec_string, name); + exit_program(1); + } + return codec->id; +} + +static AVCodec *choose_codec(AVFormatContext *s, AVStream *st, enum AVMediaType type, AVDictionary *codec_names) +{ + AVDictionaryEntry *e = NULL; + char *codec_name = NULL; + int ret; + + while (e = av_dict_get(codec_names, "", e, AV_DICT_IGNORE_SUFFIX)) { + char *p = strchr(e->key, ':'); + + if ((ret = check_stream_specifier(s, st, p ? p + 1 : "")) > 0) + codec_name = e->value; + else if (ret < 0) + exit_program(1); + } + + if (!codec_name) { + if (s->oformat) { + st->codec->codec_id = av_guess_codec(s->oformat, NULL, s->filename, NULL, type); + return avcodec_find_encoder(st->codec->codec_id); + } + } else if (!strcmp(codec_name, "copy")) + st->stream_copy = 1; + else { + st->codec->codec_id = find_codec_or_die(codec_name, type, s->iformat == NULL); + return s->oformat ? avcodec_find_encoder_by_name(codec_name) : + avcodec_find_decoder_by_name(codec_name); + } + + return NULL; +} + +static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx, enum AVMediaType type) { OutputStream *ost; AVStream *st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); @@ -716,13 +757,14 @@ static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx, AVCode ost->file_index = file_idx; ost->index = idx; ost->st = st; - ost->enc = codec; - if (codec) { - st->codec->codec_type = codec->type; - ost->opts = filter_codec_opts(codec_opts, codec->id, oc, st); + st->codec->codec_type = type; + ost->enc = choose_codec(oc, st, type, codec_names); + if (ost->enc) { + ost->opts = filter_codec_opts(codec_opts, ost->enc->id, oc, st); } - avcodec_get_context_defaults3(st->codec, codec); + avcodec_get_context_defaults3(st->codec, ost->enc); + st->codec->codec_type = type; // XXX hack, avcodec_get_context_defaults2() sets type to unknown for stream copy ost->sws_flags = av_get_int(sws_opts, "sws_flags", NULL); return ost; @@ -743,7 +785,7 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) AVCodec *codec; codec = avcodec_find_encoder(ic->streams[i]->codec->codec_id); - ost = new_output_stream(s, nb_output_files, codec); + ost = new_output_stream(s, nb_output_files, codec->type); st = ost->st; // FIXME: a more elegant solution is needed @@ -752,17 +794,10 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) memcpy(st->info, ic->streams[i]->info, sizeof(*st->info)); avcodec_copy_context(st->codec, ic->streams[i]->codec); - if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - if (audio_stream_copy) { - st->stream_copy = 1; - } else - choose_sample_fmt(st, codec); - } else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - if (video_stream_copy) { - st->stream_copy = 1; - } else - choose_pixel_fmt(st, codec); - } + if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && !st->stream_copy) + choose_sample_fmt(st, codec); + else if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && !st->stream_copy) + choose_pixel_fmt(st, codec); } av_close_input_file(ic); @@ -2916,36 +2951,29 @@ static int opt_video_standard(const char *opt, const char *arg) return opt_default("standard", arg); } -static int opt_codec(int *pstream_copy, char **pcodec_name, - int codec_type, const char *arg) +static int opt_codec(const char *opt, const char *arg) { - av_freep(pcodec_name); - if (!strcmp(arg, "copy")) { - *pstream_copy = 1; - } else { - *pcodec_name = av_strdup(arg); - } - return 0; + return av_dict_set(&codec_names, opt, arg, 0); } static int opt_audio_codec(const char *opt, const char *arg) { - return opt_codec(&audio_stream_copy, &audio_codec_name, AVMEDIA_TYPE_AUDIO, arg); + return opt_codec("codec:a", arg); } static int opt_video_codec(const char *opt, const char *arg) { - return opt_codec(&video_stream_copy, &video_codec_name, AVMEDIA_TYPE_VIDEO, arg); + return opt_codec("codec:v", arg); } static int opt_subtitle_codec(const char *opt, const char *arg) { - return opt_codec(&subtitle_stream_copy, &subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, arg); + return opt_codec("codec:s", arg); } static int opt_data_codec(const char *opt, const char *arg) { - return opt_codec(&data_stream_copy, &data_codec_name, AVMEDIA_TYPE_DATA, arg); + return opt_codec("codec:d", arg); } static int opt_codec_tag(const char *opt, const char *arg) @@ -3153,26 +3181,6 @@ static int opt_input_ts_offset(const char *opt, const char *arg) return 0; } -static enum CodecID find_codec_or_die(const char *name, int type, int encoder) -{ - const char *codec_string = encoder ? "encoder" : "decoder"; - AVCodec *codec; - - if(!name) - return CODEC_ID_NONE; - codec = encoder ? - avcodec_find_encoder_by_name(name) : - avcodec_find_decoder_by_name(name); - if(!codec) { - fprintf(stderr, "Unknown %s '%s'\n", codec_string, name); - exit_program(1); - } - if(codec->type != type) { - fprintf(stderr, "Invalid %s type '%s'\n", codec_string, name); - exit_program(1); - } - return codec->id; -} static int opt_input_file(const char *opt, const char *filename) { @@ -3223,12 +3231,6 @@ static int opt_input_file(const char *opt, const char *filename) if (frame_pix_fmt != PIX_FMT_NONE) av_dict_set(&format_opts, "pixel_format", av_get_pix_fmt_name(frame_pix_fmt), 0); - ic->video_codec_id = - find_codec_or_die(video_codec_name , AVMEDIA_TYPE_VIDEO , 0); - ic->audio_codec_id = - find_codec_or_die(audio_codec_name , AVMEDIA_TYPE_AUDIO , 0); - ic->subtitle_codec_id= - find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0); ic->flags |= AVFMT_FLAG_NONBLOCK; if (loop_input) { @@ -3268,6 +3270,10 @@ static int opt_input_file(const char *opt, const char *filename) opt_programid=0; } + /* apply forced codec ids */ + for (i = 0; i < ic->nb_streams; i++) + choose_codec(ic, ic->streams[i], ic->streams[i]->codec->codec_type, codec_names); + /* Set AVCodecContext options for avformat_find_stream_info */ opts = setup_find_stream_info_opts(ic, codec_opts); orig_nb_streams = ic->nb_streams; @@ -3315,16 +3321,16 @@ static int opt_input_file(const char *opt, const char *filename) if (i < nb_ts_scale) ist->ts_scale = ts_scale[i]; + ist->dec = choose_codec(ic, st, dec->codec_type, codec_names); + switch (dec->codec_type) { case AVMEDIA_TYPE_AUDIO: - ist->dec = avcodec_find_decoder_by_name(audio_codec_name); if(!ist->dec) ist->dec = avcodec_find_decoder(dec->codec_id); if(audio_disable) st->discard= AVDISCARD_ALL; break; case AVMEDIA_TYPE_VIDEO: - ist->dec= avcodec_find_decoder_by_name(video_codec_name); if(!ist->dec) ist->dec = avcodec_find_decoder(dec->codec_id); rfps = ic->streams[i]->r_frame_rate.num; @@ -3352,7 +3358,6 @@ static int opt_input_file(const char *opt, const char *filename) case AVMEDIA_TYPE_DATA: break; case AVMEDIA_TYPE_SUBTITLE: - ist->dec = avcodec_find_decoder_by_name(subtitle_codec_name); if(!ist->dec) ist->dec = avcodec_find_decoder(dec->codec_id); if(subtitle_disable) @@ -3390,9 +3395,7 @@ static int opt_input_file(const char *opt, const char *filename) for (i = 0; i < orig_nb_streams; i++) av_dict_free(&opts[i]); av_freep(&opts); - av_freep(&video_codec_name); - av_freep(&audio_codec_name); - av_freep(&subtitle_codec_name); + av_dict_free(&codec_names); uninit_opts(); init_opts(); return 0; @@ -3403,22 +3406,10 @@ static OutputStream *new_video_stream(AVFormatContext *oc, int file_idx) AVStream *st; OutputStream *ost; AVCodecContext *video_enc; - enum CodecID codec_id = CODEC_ID_NONE; - AVCodec *codec= NULL; - if(!video_stream_copy){ - if (video_codec_name) { - codec_id = find_codec_or_die(video_codec_name, AVMEDIA_TYPE_VIDEO, 1); - codec = avcodec_find_encoder_by_name(video_codec_name); - } else { - codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_VIDEO); - codec = avcodec_find_encoder(codec_id); - } - } - - ost = new_output_stream(oc, file_idx, codec); + ost = new_output_stream(oc, file_idx, AVMEDIA_TYPE_VIDEO); st = ost->st; - if (!video_stream_copy) { + if (!st->stream_copy) { ost->frame_aspect_ratio = frame_aspect_ratio; frame_aspect_ratio = 0; #if CONFIG_AVFILTER @@ -3441,9 +3432,7 @@ static OutputStream *new_video_stream(AVFormatContext *oc, int file_idx) video_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } - video_enc->codec_type = AVMEDIA_TYPE_VIDEO; - if (video_stream_copy) { - st->stream_copy = 1; + if (st->stream_copy) { video_enc->sample_aspect_ratio = st->sample_aspect_ratio = av_d2q(frame_aspect_ratio*frame_height/frame_width, 255); } else { @@ -3452,7 +3441,6 @@ static OutputStream *new_video_stream(AVFormatContext *oc, int file_idx) if (frame_rate.num) ost->frame_rate = frame_rate; - video_enc->codec_id = codec_id; video_enc->width = frame_width; video_enc->height = frame_height; @@ -3524,9 +3512,7 @@ static OutputStream *new_video_stream(AVFormatContext *oc, int file_idx) /* reset some key parameters */ video_disable = 0; - av_freep(&video_codec_name); av_freep(&forced_key_frames); - video_stream_copy = 0; frame_pix_fmt = PIX_FMT_NONE; return ost; } @@ -3535,20 +3521,9 @@ static OutputStream *new_audio_stream(AVFormatContext *oc, int file_idx) { AVStream *st; OutputStream *ost; - AVCodec *codec= NULL; AVCodecContext *audio_enc; - enum CodecID codec_id = CODEC_ID_NONE; - if(!audio_stream_copy){ - if (audio_codec_name) { - codec_id = find_codec_or_die(audio_codec_name, AVMEDIA_TYPE_AUDIO, 1); - codec = avcodec_find_encoder_by_name(audio_codec_name); - } else { - codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_AUDIO); - codec = avcodec_find_encoder(codec_id); - } - } - ost = new_output_stream(oc, file_idx, codec); + ost = new_output_stream(oc, file_idx, AVMEDIA_TYPE_AUDIO); st = ost->st; ost->bitstream_filters = audio_bitstream_filters; @@ -3565,11 +3540,7 @@ static OutputStream *new_audio_stream(AVFormatContext *oc, int file_idx) if (oc->oformat->flags & AVFMT_GLOBALHEADER) { audio_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } - if (audio_stream_copy) { - st->stream_copy = 1; - } else { - audio_enc->codec_id = codec_id; - + if (!st->stream_copy) { if (audio_qscale > QSCALE_NONE) { audio_enc->flags |= CODEC_FLAG_QSCALE; audio_enc->global_quality = FF_QP2LAMBDA * audio_qscale; @@ -3588,8 +3559,6 @@ static OutputStream *new_audio_stream(AVFormatContext *oc, int file_idx) /* reset some key parameters */ audio_disable = 0; - av_freep(&audio_codec_name); - audio_stream_copy = 0; return ost; } @@ -3600,29 +3569,22 @@ static OutputStream *new_data_stream(AVFormatContext *oc, int file_idx) OutputStream *ost; AVCodecContext *data_enc; - ost = new_output_stream(oc, file_idx, NULL); + ost = new_output_stream(oc, file_idx, AVMEDIA_TYPE_DATA); st = ost->st; data_enc = st->codec; - if (!data_stream_copy) { + if (!st->stream_copy) { fprintf(stderr, "Data stream encoding not supported yet (only streamcopy)\n"); exit_program(1); } - data_enc->codec_type = AVMEDIA_TYPE_DATA; - if (data_codec_tag) data_enc->codec_tag= data_codec_tag; if (oc->oformat->flags & AVFMT_GLOBALHEADER) { data_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } - if (data_stream_copy) { - st->stream_copy = 1; - } data_disable = 0; - av_freep(&data_codec_name); - data_stream_copy = 0; return ost; } @@ -3630,20 +3592,9 @@ static OutputStream *new_subtitle_stream(AVFormatContext *oc, int file_idx) { AVStream *st; OutputStream *ost; - AVCodec *codec=NULL; AVCodecContext *subtitle_enc; - enum CodecID codec_id = CODEC_ID_NONE; - if(!subtitle_stream_copy){ - if (subtitle_codec_name) { - codec_id = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 1); - codec = avcodec_find_encoder_by_name(subtitle_codec_name); - } else { - codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_SUBTITLE); - codec = avcodec_find_encoder(codec_id); - } - } - ost = new_output_stream(oc, file_idx, codec); + ost = new_output_stream(oc, file_idx, AVMEDIA_TYPE_SUBTITLE); st = ost->st; subtitle_enc = st->codec; @@ -3658,11 +3609,6 @@ static OutputStream *new_subtitle_stream(AVFormatContext *oc, int file_idx) if (oc->oformat->flags & AVFMT_GLOBALHEADER) { subtitle_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } - if (subtitle_stream_copy) { - st->stream_copy = 1; - } else { - subtitle_enc->codec_id = codec_id; - } if (subtitle_language) { av_dict_set(&st->metadata, "language", subtitle_language, 0); @@ -3670,8 +3616,6 @@ static OutputStream *new_subtitle_stream(AVFormatContext *oc, int file_idx) } subtitle_disable = 0; - av_freep(&subtitle_codec_name); - subtitle_stream_copy = 0; return ost; } @@ -3945,6 +3889,8 @@ static int opt_output_file(const char *opt, const char *filename) av_freep(&stream_maps); nb_stream_maps = 0; + av_dict_free(&codec_names); + av_freep(&forced_key_frames); uninit_opts(); init_opts(); @@ -4156,8 +4102,8 @@ static int opt_target(const char *opt, const char *arg) } if(!strcmp(arg, "vcd")) { - opt_video_codec("vcodec", "mpeg1video"); - opt_audio_codec("vcodec", "mp2"); + opt_codec("c:v", "mpeg1video"); + opt_codec("c:a", "mp2"); opt_format("f", "vcd"); opt_frame_size("s", norm == PAL ? "352x288" : "352x240"); @@ -4184,8 +4130,8 @@ static int opt_target(const char *opt, const char *arg) mux_preload= (36000+3*1200) / 90000.0; //0.44 } else if(!strcmp(arg, "svcd")) { - opt_video_codec("vcodec", "mpeg2video"); - opt_audio_codec("acodec", "mp2"); + opt_codec("c:v", "mpeg2video"); + opt_codec("c:a", "mp2"); opt_format("f", "svcd"); opt_frame_size("s", norm == PAL ? "480x576" : "480x480"); @@ -4207,8 +4153,8 @@ static int opt_target(const char *opt, const char *arg) } else if(!strcmp(arg, "dvd")) { - opt_video_codec("vcodec", "mpeg2video"); - opt_audio_codec("vcodec", "ac3"); + opt_codec("c:v", "mpeg2video"); + opt_codec("c:a", "ac3"); opt_format("f", "dvd"); opt_frame_size("vcodec", norm == PAL ? "720x576" : "720x480"); @@ -4305,6 +4251,8 @@ static const OptionDef options[] = { { "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" }, { "i", HAS_ARG, {(void*)opt_input_file}, "input file name", "filename" }, { "y", OPT_BOOL, {(void*)&file_overwrite}, "overwrite output files" }, + { "c", HAS_ARG, {(void*)opt_codec}, "codec name", "codec" }, + { "codec", HAS_ARG, {(void*)opt_codec}, "codec name", "codec" }, { "map", HAS_ARG | OPT_EXPERT, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" }, { "map_meta_data", HAS_ARG | OPT_EXPERT, {(void*)opt_map_meta_data}, "DEPRECATED set meta data information of outfile from infile", "outfile[,metadata]:infile[,metadata]" },