X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=avconv.c;h=cfb231907bfc2647ca67f0b4ffba1e509693c668;hb=6a11686dfc0aed13bf234af85329450cd8c3b225;hp=fbdd89960219eab6d5e92cfa9a3507ed377895d3;hpb=88867844c640c01c6cf0f379fc51409d5bf2c056;p=ffmpeg diff --git a/avconv.c b/avconv.c index fbdd8996021..cfb231907bf 100644 --- a/avconv.c +++ b/avconv.c @@ -90,37 +90,13 @@ typedef struct StreamMap { * select an input file for an output file */ typedef struct MetadataMap { - int file; //< file index - char type; //< type of metadata to copy -- (g)lobal, (s)tream, (c)hapter or (p)rogram - int index; //< stream/chapter/program number + int file; ///< file index + char type; ///< type of metadata to copy -- (g)lobal, (s)tream, (c)hapter or (p)rogram + int index; ///< stream/chapter/program number } MetadataMap; static const OptionDef options[]; -#define MAX_FILES 100 - -static const char *last_asked_format = NULL; -static double *ts_scale; -static int nb_ts_scale; - -static AVFormatContext *output_files[MAX_FILES]; -static AVDictionary *output_opts[MAX_FILES]; -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; -static int metadata_global_autocopy = 1; -static int metadata_streams_autocopy = 1; -static int metadata_chapters_autocopy = 1; - -static int chapters_input_file = INT_MAX; - /* indexed by output file stream index */ static int *streamid_map = NULL; static int nb_streamid_map = 0; @@ -130,16 +106,12 @@ static int frame_height = 0; static float frame_aspect_ratio = 0; static enum PixelFormat frame_pix_fmt = PIX_FMT_NONE; static enum AVSampleFormat audio_sample_fmt = AV_SAMPLE_FMT_NONE; -static int max_frames[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX}; static AVRational frame_rate; static float video_qscale = 0; static uint16_t *intra_matrix = NULL; 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 unsigned int video_codec_tag = 0; -static char *video_language = NULL; static int same_quant = 0; static int do_deinterlace = 0; static int top_field_first = -1; @@ -150,30 +122,11 @@ static int qp_hist = 0; static char *vfilters = NULL; #endif -static int intra_only = 0; static int audio_sample_rate = 0; #define QSCALE_NONE -99999 static float audio_qscale = QSCALE_NONE; -static int audio_disable = 0; -static int audio_channels = 0; -static unsigned int audio_codec_tag = 0; -static char *audio_language = NULL; -static int subtitle_disable = 0; -static char *subtitle_language = NULL; -static unsigned int subtitle_codec_tag = 0; - -static int data_disable = 0; -static unsigned int data_codec_tag = 0; - -static float mux_preload= 0.5; -static float mux_max_delay= 0.7; - -static int64_t recording_time = INT64_MAX; -static int64_t start_time = 0; -static int64_t input_ts_offset = 0; static int file_overwrite = 0; -static AVDictionary *metadata; static int do_benchmark = 0; static int do_hex_dump = 0; static int do_pkt_dump = 0; @@ -191,41 +144,58 @@ static FILE *vstats_file; static int opt_programid = 0; static int copy_initial_nonkeyframes = 0; -static int rate_emu = 0; - static int audio_volume = 256; static int exit_on_error = 0; static int using_stdin = 0; static int verbose = 1; -static int thread_count= 1; static int64_t video_size = 0; static int64_t audio_size = 0; static int64_t extra_size = 0; static int nb_frames_dup = 0; static int nb_frames_drop = 0; static int input_sync; -static uint64_t limit_filesize = 0; static int force_fps = 0; static char *forced_key_frames = NULL; static float dts_delta_threshold = 10; -static int64_t timer_start; - static uint8_t *audio_buf; static uint8_t *audio_out; static unsigned int allocated_audio_out_size, allocated_audio_buf_size; static short *samples; -static AVBitStreamFilterContext *video_bitstream_filters=NULL; -static AVBitStreamFilterContext *audio_bitstream_filters=NULL; -static AVBitStreamFilterContext *subtitle_bitstream_filters=NULL; - #define DEFAULT_PASS_LOGFILENAME_PREFIX "av2pass" -struct InputStream; +typedef struct InputStream { + int file_index; + AVStream *st; + int discard; /* true if stream data should be discarded */ + int decoding_needed; /* true if the packets must be decoded in 'raw_fifo' */ + AVCodec *dec; + + int64_t start; /* time when read started */ + int64_t next_pts; /* synthetic pts for cases where pkt.pts + is not defined */ + int64_t pts; /* current pts */ + PtsCorrectionContext pts_ctx; + double ts_scale; + int is_start; /* is 1 at the start and after a discontinuity */ + int showed_multi_packet_warning; + AVDictionary *opts; +} InputStream; + +typedef struct InputFile { + AVFormatContext *ctx; + int eof_reached; /* true if eof reached */ + int ist_index; /* index of first stream in ist_table */ + int buffer_size; /* current total buffer size */ + int64_t ts_offset; + int nb_streams; /* number of stream that avconv is aware of; may be different + from ctx.nb_streams if new streams appear during av_read_frame() */ + int rate_emu; +} InputFile; typedef struct OutputStream { int file_index; /* file index */ @@ -241,6 +211,7 @@ typedef struct OutputStream { int64_t sync_opts; /* output frame counter, could be changed to some true timestamp */ //FIXME look at frame_number AVBitStreamFilterContext *bitstream_filters; AVCodec *enc; + int64_t max_frames; /* video only */ int video_resample; @@ -279,43 +250,128 @@ typedef struct OutputStream { int sws_flags; AVDictionary *opts; + int is_past_recording_time; } OutputStream; -static OutputStream **output_streams_for_file[MAX_FILES] = { NULL }; -static int nb_output_streams_for_file[MAX_FILES] = { 0 }; -typedef struct InputStream { - int file_index; - AVStream *st; - int discard; /* true if stream data should be discarded */ - int decoding_needed; /* true if the packets must be decoded in 'raw_fifo' */ - AVCodec *dec; - - int64_t start; /* time when read started */ - int64_t next_pts; /* synthetic pts for cases where pkt.pts - is not defined */ - int64_t pts; /* current pts */ - PtsCorrectionContext pts_ctx; - double ts_scale; - int is_start; /* is 1 at the start and after a discontinuity */ - int showed_multi_packet_warning; - int is_past_recording_time; - AVDictionary *opts; -} InputStream; - -typedef struct InputFile { +typedef struct OutputFile { AVFormatContext *ctx; - int eof_reached; /* true if eof reached */ - int ist_index; /* index of first stream in ist_table */ - int buffer_size; /* current total buffer size */ - int64_t ts_offset; -} InputFile; + AVDictionary *opts; + int ost_index; /* index of the first stream in output_streams */ + int64_t recording_time; /* desired length of the resulting file in microseconds */ + int64_t start_time; /* start time in microseconds */ + uint64_t limit_filesize; +} OutputFile; static InputStream *input_streams = NULL; static int nb_input_streams = 0; static InputFile *input_files = NULL; static int nb_input_files = 0; +static OutputStream *output_streams = NULL; +static int nb_output_streams = 0; +static OutputFile *output_files = NULL; +static int nb_output_files = 0; + +typedef struct OptionsContext { + /* input/output options */ + int64_t start_time; + const char *format; + + SpecifierOpt *codec_names; + int nb_codec_names; + SpecifierOpt *audio_channels; + int nb_audio_channels; + + /* input options */ + int64_t input_ts_offset; + int rate_emu; + + SpecifierOpt *ts_scale; + int nb_ts_scale; + + /* output options */ + StreamMap *stream_maps; + int nb_stream_maps; + /* first item specifies output metadata, second is input */ + MetadataMap (*meta_data_maps)[2]; + int nb_meta_data_maps; + int metadata_global_manual; + int metadata_streams_manual; + int metadata_chapters_manual; + + int chapters_input_file; + + int64_t recording_time; + uint64_t limit_filesize; + float mux_preload; + float mux_max_delay; + + int video_disable; + int audio_disable; + int subtitle_disable; + int data_disable; + + SpecifierOpt *metadata; + int nb_metadata; + SpecifierOpt *max_frames; + int nb_max_frames; + SpecifierOpt *bitstream_filters; + int nb_bitstream_filters; + SpecifierOpt *codec_tags; + int nb_codec_tags; +} OptionsContext; + +#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\ +{\ + int i, ret;\ + for (i = 0; i < o->nb_ ## name; i++) {\ + char *spec = o->name[i].specifier;\ + if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0)\ + outvar = o->name[i].u.type;\ + else if (ret < 0)\ + exit_program(1);\ + }\ +} + +static void reset_options(OptionsContext *o) +{ + const OptionDef *po = options; + + /* all OPT_SPEC and OPT_STRING can be freed in generic way */ + while (po->name) { + void *dst = (uint8_t*)o + po->u.off; + + if (po->flags & OPT_SPEC) { + SpecifierOpt **so = dst; + int i, *count = (int*)(so + 1); + for (i = 0; i < *count; i++) { + av_freep(&(*so)[i].specifier); + if (po->flags & OPT_STRING) + av_freep(&(*so)[i].u.str); + } + av_freep(so); + *count = 0; + } else if (po->flags & OPT_OFFSET && po->flags & OPT_STRING) + av_freep(dst); + po++; + } + + av_freep(&o->stream_maps); + av_freep(&o->meta_data_maps); + + memset(o, 0, sizeof(*o)); + + o->mux_preload = 0.5; + o->mux_max_delay = 0.7; + o->recording_time = INT64_MAX; + o->limit_filesize = UINT64_MAX; + o->chapters_input_file = INT_MAX; + + uninit_opts(); + init_opts(); +} + #if CONFIG_AVFILTER static int configure_video_filters(InputStream *ist, OutputStream *ost) @@ -394,7 +450,7 @@ static int configure_video_filters(InputStream *ist, OutputStream *ost) codec->width = ost->output_video_filter->inputs[0]->w; codec->height = ost->output_video_filter->inputs[0]->h; codec->sample_aspect_ratio = ost->st->sample_aspect_ratio = - ost->frame_aspect_ratio ? // overriden by the -aspect cli option + ost->frame_aspect_ratio ? // overridden by the -aspect cli option av_d2q(ost->frame_aspect_ratio*codec->height/codec->width, 255) : ost->output_video_filter->inputs[0]->sample_aspect_ratio; @@ -432,18 +488,17 @@ static int decode_interrupt_cb(void) return received_nb_signals > 1; } -static int exit_program(int ret) +void exit_program(int ret) { int i; /* close files */ for(i=0;ioformat->flags & AVFMT_NOFILE) && s->pb) avio_close(s->pb); avformat_free_context(s); - av_free(output_streams_for_file[i]); - av_dict_free(&output_opts[i]); + av_dict_free(&output_files[i].opts); } for(i=0;i= INT_MAX / elem_size) { - fprintf(stderr, "Array too big.\n"); - exit_program(1); - } - if (*size < new_size) { - uint8_t *tmp = av_realloc(array, new_size*elem_size); - if (!tmp) { - fprintf(stderr, "Could not alloc buffer.\n"); - exit_program(1); - } - memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size); - *size = new_size; - return tmp; - } - return array; -} - static void choose_sample_fmt(AVStream *st, AVCodec *codec) { if(codec && codec->sample_fmts){ @@ -641,7 +674,8 @@ static double get_sync_ipts(const OutputStream *ost) { const InputStream *ist = ost->sync_ist; - return (double)(ist->pts - start_time)/AV_TIME_BASE; + OutputFile *of = &output_files[ost->file_index]; + return (double)(ist->pts - of->start_time)/AV_TIME_BASE; } static void write_frame(AVFormatContext *s, AVPacket *pkt, AVCodecContext *avctx, AVBitStreamFilterContext *bsfc){ @@ -676,8 +710,6 @@ static void write_frame(AVFormatContext *s, AVPacket *pkt, AVCodecContext *avctx } } -#define MAX_AUDIO_PACKET_SIZE (128 * 1024) - static void do_audio_out(AVFormatContext *s, OutputStream *ost, InputStream *ist, @@ -961,9 +993,6 @@ static void pre_process_video_frame(InputStream *ist, AVPicture *picture, void * *bufp = buf; } -/* we begin to correct av delay at this threshold */ -#define AV_DELAY_MAX 0.100 - static void do_subtitle_out(AVFormatContext *s, OutputStream *ost, InputStream *ist, @@ -1079,7 +1108,7 @@ static void do_video_resample(OutputStream *ost, if (resample_changed) { avfilter_graph_free(&ost->graph); if (configure_video_filters(ist, ost)) { - fprintf(stderr, "Error reinitialising filters!\n"); + fprintf(stderr, "Error reinitializing filters!\n"); exit_program(1); } } @@ -1100,11 +1129,10 @@ static void do_video_out(AVFormatContext *s, { int nb_frames, i, ret, format_video_sync; AVFrame *final_picture; - AVCodecContext *enc, *dec; + AVCodecContext *enc; double sync_ipts; enc = ost->st->codec; - dec = ist->st->codec; sync_ipts = get_sync_ipts(ost) / av_q2d(enc->time_base); @@ -1142,7 +1170,7 @@ static void do_video_out(AVFormatContext *s, }else ost->sync_opts= lrintf(sync_ipts); - nb_frames= FFMIN(nb_frames, max_frames[AVMEDIA_TYPE_VIDEO] - ost->frame_number); + nb_frames = FFMIN(nb_frames, ost->max_frames - ost->frame_number); if (nb_frames <= 0) return; @@ -1156,17 +1184,16 @@ static void do_video_out(AVFormatContext *s, if (s->oformat->flags & AVFMT_RAWPICTURE) { /* raw pictures are written as AVPicture structure to - avoid any copies. We support temorarily the older + avoid any copies. We support temporarily the older method. */ - AVFrame* old_frame = enc->coded_frame; - enc->coded_frame = dec->coded_frame; //FIXME/XXX remove this hack + enc->coded_frame->interlaced_frame = in_picture->interlaced_frame; + enc->coded_frame->top_field_first = in_picture->top_field_first; pkt.data= (uint8_t *)final_picture; pkt.size= sizeof(AVPicture); pkt.pts= av_rescale_q(ost->sync_opts, enc->time_base, ost->st->time_base); pkt.flags |= AV_PKT_FLAG_KEY; write_frame(s, &pkt, ost->st->codec, ost->bitstream_filters); - enc->coded_frame = old_frame; } else { AVFrame big_picture; @@ -1271,9 +1298,9 @@ static void do_video_stats(AVFormatContext *os, OutputStream *ost, } } -static void print_report(AVFormatContext **output_files, - OutputStream **ost_table, int nb_ostreams, - int is_last_report) +static void print_report(OutputFile *output_files, + OutputStream *ost_table, int nb_ostreams, + int is_last_report, int64_t timer_start) { char buf[1024]; OutputStream *ost; @@ -1299,7 +1326,7 @@ static void print_report(AVFormatContext **output_files, } - oc = output_files[0]; + oc = output_files[0].ctx; total_size = avio_size(oc->pb); if(total_size<0) // FIXME improve avio_size() so it works with non seekable output too @@ -1310,7 +1337,7 @@ static void print_report(AVFormatContext **output_files, vid = 0; for(i=0;ist->codec; if (!ost->st->stream_copy && enc->coded_frame) q = enc->coded_frame->quality/(float)FF_QP2LAMBDA; @@ -1401,9 +1428,94 @@ static void generate_silence(uint8_t* buf, enum AVSampleFormat sample_fmt, size_ memset(buf, fill_char, size); } +static void flush_encoders(OutputStream *ost_table, int nb_ostreams) +{ + int i, ret; + + for (i = 0; i < nb_ostreams; i++) { + OutputStream *ost = &ost_table[i]; + AVCodecContext *enc = ost->st->codec; + AVFormatContext *os = output_files[ost->file_index].ctx; + + if (!ost->encoding_needed) + continue; + + if (ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO && enc->frame_size <=1) + continue; + if (ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (os->oformat->flags & AVFMT_RAWPICTURE)) + continue; + + for(;;) { + AVPacket pkt; + int fifo_bytes; + av_init_packet(&pkt); + pkt.stream_index= ost->index; + + switch (ost->st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + fifo_bytes = av_fifo_size(ost->fifo); + ret = 0; + /* encode any samples remaining in fifo */ + if (fifo_bytes > 0) { + int osize = av_get_bytes_per_sample(enc->sample_fmt); + int fs_tmp = enc->frame_size; + + av_fifo_generic_read(ost->fifo, audio_buf, fifo_bytes, NULL); + if (enc->codec->capabilities & CODEC_CAP_SMALL_LAST_FRAME) { + enc->frame_size = fifo_bytes / (osize * enc->channels); + } else { /* pad */ + int frame_bytes = enc->frame_size*osize*enc->channels; + if (allocated_audio_buf_size < frame_bytes) + exit_program(1); + generate_silence(audio_buf+fifo_bytes, enc->sample_fmt, frame_bytes - fifo_bytes); + } + + ret = avcodec_encode_audio(enc, bit_buffer, bit_buffer_size, (short *)audio_buf); + pkt.duration = av_rescale((int64_t)enc->frame_size*ost->st->time_base.den, + ost->st->time_base.num, enc->sample_rate); + enc->frame_size = fs_tmp; + } + if (ret <= 0) { + ret = avcodec_encode_audio(enc, bit_buffer, bit_buffer_size, NULL); + } + if (ret < 0) { + fprintf(stderr, "Audio encoding failed\n"); + exit_program(1); + } + audio_size += ret; + pkt.flags |= AV_PKT_FLAG_KEY; + break; + case AVMEDIA_TYPE_VIDEO: + ret = avcodec_encode_video(enc, bit_buffer, bit_buffer_size, NULL); + if (ret < 0) { + fprintf(stderr, "Video encoding failed\n"); + exit_program(1); + } + video_size += ret; + if(enc->coded_frame && enc->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; + if (ost->logfile && enc->stats_out) { + fprintf(ost->logfile, "%s", enc->stats_out); + } + break; + default: + ret=-1; + } + + if (ret <= 0) + break; + pkt.data = bit_buffer; + pkt.size = ret; + if (enc->coded_frame && enc->coded_frame->pts != AV_NOPTS_VALUE) + pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base); + write_frame(os, &pkt, ost->st->codec, ost->bitstream_filters); + } + } +} + /* pkt = NULL means EOF (needed to flush decoder buffers) */ static int output_packet(InputStream *ist, int ist_index, - OutputStream **ost_table, int nb_ostreams, + OutputStream *ost_table, int nb_ostreams, const AVPacket *pkt) { AVFormatContext *os; @@ -1546,29 +1658,9 @@ static int output_packet(InputStream *ist, int ist_index, } break; } - ret = avpkt.size; avpkt.size = 0; } -#if CONFIG_AVFILTER - if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - for (i = 0; i < nb_ostreams; i++) { - ost = ost_table[i]; - if (ost->input_video_filter && ost->source_index == ist_index) { - AVRational sar; - if (ist->st->sample_aspect_ratio.num) - sar = ist->st->sample_aspect_ratio; - else - sar = ist->st->codec->sample_aspect_ratio; - // add it to be filtered - av_vsrc_buffer_add_frame(ost->input_video_filter, &picture, - ist->pts, - sar); - } - } - } -#endif - // preprocess audio (volume) if (ist->st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { if (audio_volume != 256) { @@ -1576,15 +1668,13 @@ static int output_packet(InputStream *ist, int ist_index, volp = samples; for(i=0;i<(decoded_data_size / sizeof(short));i++) { int v = ((*volp) * audio_volume + 128) >> 8; - if (v < -32768) v = -32768; - if (v > 32767) v = 32767; - *volp++ = v; + *volp++ = av_clip_int16(v); } } } /* frame rate emulation */ - if (rate_emu) { + if (input_files[ist->file_index].rate_emu) { int64_t pts = av_rescale(ist->pts, 1000000, AV_TIME_BASE); int64_t now = av_gettime() - ist->start; if (pts > now) @@ -1592,119 +1682,139 @@ static int output_packet(InputStream *ist, int ist_index, } /* if output time reached then transcode raw format, encode packets and output them */ - if (start_time == 0 || ist->pts >= start_time) - for(i=0;isource_index != ist_index) + continue; + + if (of->start_time && ist->pts < of->start_time) + continue; + + if (of->recording_time != INT64_MAX && + av_compare_ts(ist->pts, AV_TIME_BASE_Q, of->recording_time + of->start_time, + (AVRational){1, 1000000}) >= 0) { + ost->is_past_recording_time = 1; + continue; + } - ost = ost_table[i]; - if (ost->source_index == ist_index) { #if CONFIG_AVFILTER - frame_available = ist->st->codec->codec_type != AVMEDIA_TYPE_VIDEO || - !ost->output_video_filter || avfilter_poll_frame(ost->output_video_filter->inputs[0]); - while (frame_available) { - AVRational ist_pts_tb; - if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->output_video_filter) - get_filtered_video_frame(ost->output_video_filter, &picture, &ost->picref, &ist_pts_tb); - if (ost->picref) - ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, AV_TIME_BASE_Q); + if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && + ost->input_video_filter) { + AVRational sar; + if (ist->st->sample_aspect_ratio.num) + sar = ist->st->sample_aspect_ratio; + else + sar = ist->st->codec->sample_aspect_ratio; + av_vsrc_buffer_add_frame(ost->input_video_filter, &picture, ist->pts, sar); + } + frame_available = ist->st->codec->codec_type != AVMEDIA_TYPE_VIDEO || + !ost->output_video_filter || avfilter_poll_frame(ost->output_video_filter->inputs[0]); + while (frame_available) { + AVRational ist_pts_tb; + if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ost->output_video_filter) + get_filtered_video_frame(ost->output_video_filter, &picture, &ost->picref, &ist_pts_tb); + if (ost->picref) + ist->pts = av_rescale_q(ost->picref->pts, ist_pts_tb, AV_TIME_BASE_Q); #endif - os = output_files[ost->file_index]; + os = output_files[ost->file_index].ctx; - /* set the input output pts pairs */ - //ost->sync_ipts = (double)(ist->pts + input_files[ist->file_index].ts_offset - start_time)/ AV_TIME_BASE; + /* set the input output pts pairs */ + //ost->sync_ipts = (double)(ist->pts + input_files[ist->file_index].ts_offset - start_time)/ AV_TIME_BASE; - if (ost->encoding_needed) { - av_assert0(ist->decoding_needed); - switch(ost->st->codec->codec_type) { - case AVMEDIA_TYPE_AUDIO: - do_audio_out(os, ost, ist, decoded_data_buf, decoded_data_size); - break; - case AVMEDIA_TYPE_VIDEO: + if (ost->encoding_needed) { + av_assert0(ist->decoding_needed); + switch(ost->st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: + do_audio_out(os, ost, ist, decoded_data_buf, decoded_data_size); + break; + case AVMEDIA_TYPE_VIDEO: #if CONFIG_AVFILTER - if (ost->picref->video && !ost->frame_aspect_ratio) - ost->st->codec->sample_aspect_ratio = ost->picref->video->pixel_aspect; + if (ost->picref->video && !ost->frame_aspect_ratio) + ost->st->codec->sample_aspect_ratio = ost->picref->video->pixel_aspect; #endif - do_video_out(os, ost, ist, &picture, &frame_size, - same_quant ? quality : ost->st->codec->global_quality); - if (vstats_filename && frame_size) - do_video_stats(os, ost, frame_size); - break; - case AVMEDIA_TYPE_SUBTITLE: - do_subtitle_out(os, ost, ist, &subtitle, - pkt->pts); - break; - default: - abort(); - } - } else { - AVFrame avframe; //FIXME/XXX remove this - AVPacket opkt; - int64_t ost_tb_start_time= av_rescale_q(start_time, AV_TIME_BASE_Q, ost->st->time_base); + do_video_out(os, ost, ist, &picture, &frame_size, + same_quant ? quality : ost->st->codec->global_quality); + if (vstats_filename && frame_size) + do_video_stats(os, ost, frame_size); + break; + case AVMEDIA_TYPE_SUBTITLE: + do_subtitle_out(os, ost, ist, &subtitle, + pkt->pts); + break; + default: + abort(); + } + } else { + AVFrame avframe; //FIXME/XXX remove this + AVPacket opkt; + int64_t ost_tb_start_time= av_rescale_q(of->start_time, AV_TIME_BASE_Q, ost->st->time_base); - av_init_packet(&opkt); + av_init_packet(&opkt); - if ((!ost->frame_number && !(pkt->flags & AV_PKT_FLAG_KEY)) && !copy_initial_nonkeyframes) + if ((!ost->frame_number && !(pkt->flags & AV_PKT_FLAG_KEY)) && !copy_initial_nonkeyframes) #if !CONFIG_AVFILTER - continue; + continue; #else - goto cont; + goto cont; #endif - /* no reencoding needed : output the packet directly */ - /* force the input stream PTS */ + /* no reencoding needed : output the packet directly */ + /* force the input stream PTS */ - avcodec_get_frame_defaults(&avframe); - ost->st->codec->coded_frame= &avframe; - avframe.key_frame = pkt->flags & AV_PKT_FLAG_KEY; + avcodec_get_frame_defaults(&avframe); + ost->st->codec->coded_frame= &avframe; + avframe.key_frame = pkt->flags & AV_PKT_FLAG_KEY; - if(ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO) - audio_size += data_size; - else if (ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - video_size += data_size; - ost->sync_opts++; - } + if(ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + audio_size += data_size; + else if (ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + video_size += data_size; + ost->sync_opts++; + } - opkt.stream_index= ost->index; - if(pkt->pts != AV_NOPTS_VALUE) - opkt.pts= av_rescale_q(pkt->pts, ist->st->time_base, ost->st->time_base) - ost_tb_start_time; - else - opkt.pts= AV_NOPTS_VALUE; - - if (pkt->dts == AV_NOPTS_VALUE) - opkt.dts = av_rescale_q(ist->pts, AV_TIME_BASE_Q, ost->st->time_base); - else - opkt.dts = av_rescale_q(pkt->dts, ist->st->time_base, ost->st->time_base); - opkt.dts -= ost_tb_start_time; - - opkt.duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->st->time_base); - opkt.flags= pkt->flags; - - //FIXME remove the following 2 lines they shall be replaced by the bitstream filters - if( ost->st->codec->codec_id != CODEC_ID_H264 - && ost->st->codec->codec_id != CODEC_ID_MPEG1VIDEO - && ost->st->codec->codec_id != CODEC_ID_MPEG2VIDEO - ) { - if(av_parser_change(ist->st->parser, ost->st->codec, &opkt.data, &opkt.size, data_buf, data_size, pkt->flags & AV_PKT_FLAG_KEY)) - opkt.destruct= av_destruct_packet; - } else { - opkt.data = data_buf; - opkt.size = data_size; - } + opkt.stream_index= ost->index; + if(pkt->pts != AV_NOPTS_VALUE) + opkt.pts= av_rescale_q(pkt->pts, ist->st->time_base, ost->st->time_base) - ost_tb_start_time; + else + opkt.pts= AV_NOPTS_VALUE; - write_frame(os, &opkt, ost->st->codec, ost->bitstream_filters); - ost->st->codec->frame_number++; - ost->frame_number++; - av_free_packet(&opkt); + if (pkt->dts == AV_NOPTS_VALUE) + opkt.dts = av_rescale_q(ist->pts, AV_TIME_BASE_Q, ost->st->time_base); + else + opkt.dts = av_rescale_q(pkt->dts, ist->st->time_base, ost->st->time_base); + opkt.dts -= ost_tb_start_time; + + opkt.duration = av_rescale_q(pkt->duration, ist->st->time_base, ost->st->time_base); + opkt.flags= pkt->flags; + + //FIXME remove the following 2 lines they shall be replaced by the bitstream filters + if( ost->st->codec->codec_id != CODEC_ID_H264 + && ost->st->codec->codec_id != CODEC_ID_MPEG1VIDEO + && ost->st->codec->codec_id != CODEC_ID_MPEG2VIDEO + ) { + if(av_parser_change(ist->st->parser, ost->st->codec, &opkt.data, &opkt.size, data_buf, data_size, pkt->flags & AV_PKT_FLAG_KEY)) + opkt.destruct= av_destruct_packet; + } else { + opkt.data = data_buf; + opkt.size = data_size; } -#if CONFIG_AVFILTER - cont: - frame_available = (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) && - ost->output_video_filter && avfilter_poll_frame(ost->output_video_filter->inputs[0]); - if (ost->picref) - avfilter_unref_buffer(ost->picref); + + write_frame(os, &opkt, ost->st->codec, ost->bitstream_filters); + ost->st->codec->frame_number++; + ost->frame_number++; + av_free_packet(&opkt); } +#if CONFIG_AVFILTER + cont: + frame_available = (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO) && + ost->output_video_filter && avfilter_poll_frame(ost->output_video_filter->inputs[0]); + if (ost->picref) + avfilter_unref_buffer(ost->picref); + } #endif - } } av_free(buffer_to_free); @@ -1715,152 +1825,102 @@ static int output_packet(InputStream *ist, int ist_index, } } discard_packet: - if (pkt == NULL) { - /* EOF handling */ - - for(i=0;isource_index == ist_index) { - AVCodecContext *enc= ost->st->codec; - os = output_files[ost->file_index]; - - if(ost->st->codec->codec_type == AVMEDIA_TYPE_AUDIO && enc->frame_size <=1) - continue; - if(ost->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && (os->oformat->flags & AVFMT_RAWPICTURE)) - continue; - - if (ost->encoding_needed) { - for(;;) { - AVPacket pkt; - int fifo_bytes; - av_init_packet(&pkt); - pkt.stream_index= ost->index; - - switch(ost->st->codec->codec_type) { - case AVMEDIA_TYPE_AUDIO: - fifo_bytes = av_fifo_size(ost->fifo); - ret = 0; - /* encode any samples remaining in fifo */ - if (fifo_bytes > 0) { - int osize = av_get_bytes_per_sample(enc->sample_fmt); - int fs_tmp = enc->frame_size; - - av_fifo_generic_read(ost->fifo, audio_buf, fifo_bytes, NULL); - if (enc->codec->capabilities & CODEC_CAP_SMALL_LAST_FRAME) { - enc->frame_size = fifo_bytes / (osize * enc->channels); - } else { /* pad */ - int frame_bytes = enc->frame_size*osize*enc->channels; - if (allocated_audio_buf_size < frame_bytes) - exit_program(1); - generate_silence(audio_buf+fifo_bytes, enc->sample_fmt, frame_bytes - fifo_bytes); - } - - ret = avcodec_encode_audio(enc, bit_buffer, bit_buffer_size, (short *)audio_buf); - pkt.duration = av_rescale((int64_t)enc->frame_size*ost->st->time_base.den, - ost->st->time_base.num, enc->sample_rate); - enc->frame_size = fs_tmp; - } - if(ret <= 0) { - ret = avcodec_encode_audio(enc, bit_buffer, bit_buffer_size, NULL); - } - if (ret < 0) { - fprintf(stderr, "Audio encoding failed\n"); - exit_program(1); - } - audio_size += ret; - pkt.flags |= AV_PKT_FLAG_KEY; - break; - case AVMEDIA_TYPE_VIDEO: - ret = avcodec_encode_video(enc, bit_buffer, bit_buffer_size, NULL); - if (ret < 0) { - fprintf(stderr, "Video encoding failed\n"); - exit_program(1); - } - video_size += ret; - if(enc->coded_frame && enc->coded_frame->key_frame) - pkt.flags |= AV_PKT_FLAG_KEY; - if (ost->logfile && enc->stats_out) { - fprintf(ost->logfile, "%s", enc->stats_out); - } - break; - default: - ret=-1; - } - - if(ret<=0) - break; - pkt.data= bit_buffer; - pkt.size= ret; - if(enc->coded_frame && enc->coded_frame->pts != AV_NOPTS_VALUE) - pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base); - write_frame(os, &pkt, ost->st->codec, ost->bitstream_filters); - } - } - } - } - } return 0; } -static void print_sdp(AVFormatContext **avc, int n) +static void print_sdp(OutputFile *output_files, int n) { char sdp[2048]; + int i; + AVFormatContext **avc = av_malloc(sizeof(*avc)*n); + + if (!avc) + exit_program(1); + for (i = 0; i < n; i++) + avc[i] = output_files[i].ctx; av_sdp_create(avc, n, sdp, sizeof(sdp)); printf("SDP:\n%s\n", sdp); fflush(stdout); + av_freep(&avc); } -/* - * The following code is the main loop of the file converter - */ -static int transcode(AVFormatContext **output_files, - int nb_output_files, - InputFile *input_files, - int nb_input_files) +static int init_input_stream(int ist_index, OutputStream *output_streams, int nb_output_streams, + char *error, int error_len) { - int ret = 0, i, j, k, n, nb_ostreams = 0; - AVFormatContext *is, *os; + int i; + InputStream *ist = &input_streams[ist_index]; + if (ist->decoding_needed) { + AVCodec *codec = ist->dec; + if (!codec) { + snprintf(error, error_len, "Decoder (codec id %d) not found for input stream #%d.%d", + ist->st->codec->codec_id, ist->file_index, ist->st->index); + return AVERROR(EINVAL); + } + + /* update requested sample format for the decoder based on the + corresponding encoder sample format */ + for (i = 0; i < nb_output_streams; i++) { + OutputStream *ost = &output_streams[i]; + if (ost->source_index == ist_index) { + update_sample_fmt(ist->st->codec, codec, ost->st->codec); + break; + } + } + + if (avcodec_open2(ist->st->codec, codec, &ist->opts) < 0) { + snprintf(error, error_len, "Error while opening decoder for input stream #%d.%d", + ist->file_index, ist->st->index); + return AVERROR(EINVAL); + } + assert_codec_experimental(ist->st->codec, 0); + assert_avoptions(ist->opts); + } + + ist->pts = ist->st->avg_frame_rate.num ? - ist->st->codec->has_b_frames*AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0; + ist->next_pts = AV_NOPTS_VALUE; + init_pts_correction(&ist->pts_ctx); + ist->is_start = 1; + + return 0; +} + +static int transcode_init(OutputFile *output_files, + int nb_output_files, + InputFile *input_files, + int nb_input_files) +{ + int ret = 0, i, j; + AVFormatContext *os; AVCodecContext *codec, *icodec; - OutputStream *ost, **ost_table = NULL; + OutputStream *ost; InputStream *ist; char error[1024]; int want_sdp = 1; - uint8_t no_packet[MAX_FILES]={0}; - int no_packet_count=0; - if (rate_emu) - for (i = 0; i < nb_input_streams; i++) - input_streams[i].start = av_gettime(); + /* init framerate emulation */ + for (i = 0; i < nb_input_files; i++) { + InputFile *ifile = &input_files[i]; + if (ifile->rate_emu) + for (j = 0; j < ifile->nb_streams; j++) + input_streams[j + ifile->ist_index].start = av_gettime(); + } /* output stream init */ - nb_ostreams = 0; for(i=0;inb_streams && !(os->oformat->flags & AVFMT_NOSTREAMS)) { - av_dump_format(output_files[i], i, output_files[i]->filename, 1); + av_dump_format(os, i, os->filename, 1); fprintf(stderr, "Output file #%d does not contain any stream\n", i); - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } - nb_ostreams += os->nb_streams; - } - - ost_table = av_mallocz(sizeof(OutputStream *) * nb_ostreams); - if (!ost_table) - goto fail; - n = 0; - for(k=0;knb_streams; i++, n++) - ost_table[n] = output_streams_for_file[k][i]; } /* for each output stream, we compute the right encoding parameters */ - for(i=0;ifile_index]; + for (i = 0; i < nb_output_streams; i++) { + ost = &output_streams[i]; + os = output_files[ost->file_index].ctx; ist = &input_streams[ost->source_index]; codec = ost->st->codec; @@ -1873,8 +1933,9 @@ static int transcode(AVFormatContext **output_files, if (ost->st->stream_copy) { uint64_t extra_size = (uint64_t)icodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE; - if (extra_size > INT_MAX) - goto fail; + if (extra_size > INT_MAX) { + return AVERROR(EINVAL); + } /* if stream_copy is selected, no need to decode or encode */ codec->codec_id = icodec->codec_id; @@ -1891,8 +1952,9 @@ static int transcode(AVFormatContext **output_files, codec->rc_max_rate = icodec->rc_max_rate; codec->rc_buffer_size = icodec->rc_buffer_size; codec->extradata= av_mallocz(extra_size); - if (!codec->extradata) - goto fail; + if (!codec->extradata) { + return AVERROR(ENOMEM); + } memcpy(codec->extradata, icodec->extradata, icodec->extradata_size); codec->extradata_size= icodec->extradata_size; if(!copy_tb && av_q2d(icodec->time_base)*icodec->ticks_per_frame > av_q2d(ist->st->time_base) && av_q2d(ist->st->time_base) < 1.0/500){ @@ -1947,8 +2009,9 @@ static int transcode(AVFormatContext **output_files, switch(codec->codec_type) { case AVMEDIA_TYPE_AUDIO: ost->fifo= av_fifo_alloc(1024); - if(!ost->fifo) - goto fail; + if (!ost->fifo) { + return AVERROR(ENOMEM); + } ost->reformat_pair = MAKE_SFMT_PAIR(AV_SAMPLE_FMT_NONE,AV_SAMPLE_FMT_NONE); if (!codec->sample_rate) { codec->sample_rate = icodec->sample_rate; @@ -2082,13 +2145,12 @@ static int transcode(AVFormatContext **output_files, if (!bit_buffer) { fprintf(stderr, "Cannot allocate %d bytes output buffer\n", bit_buffer_size); - ret = AVERROR(ENOMEM); - goto fail; + return AVERROR(ENOMEM); } /* open each encoder */ - for(i=0;iencoding_needed) { AVCodec *codec = ost->enc; AVCodecContext *dec = input_streams[ost->source_index].st->codec; @@ -2122,62 +2184,21 @@ static int transcode(AVFormatContext **output_files, } } - /* open each decoder */ - for (i = 0; i < nb_input_streams; i++) { - ist = &input_streams[i]; - if (ist->decoding_needed) { - AVCodec *codec = ist->dec; - if (!codec) - codec = avcodec_find_decoder(ist->st->codec->codec_id); - if (!codec) { - snprintf(error, sizeof(error), "Decoder (codec id %d) not found for input stream #%d.%d", - ist->st->codec->codec_id, ist->file_index, ist->st->index); - ret = AVERROR(EINVAL); - goto dump_format; - } - - /* update requested sample format for the decoder based on the - corresponding encoder sample format */ - for (j = 0; j < nb_ostreams; j++) { - ost = ost_table[j]; - if (ost->source_index == i) { - update_sample_fmt(ist->st->codec, codec, ost->st->codec); - break; - } - } - - if (avcodec_open2(ist->st->codec, codec, &ist->opts) < 0) { - snprintf(error, sizeof(error), "Error while opening decoder for input stream #%d.%d", - ist->file_index, ist->st->index); - ret = AVERROR(EINVAL); - goto dump_format; - } - assert_codec_experimental(ist->st->codec, 0); - assert_avoptions(ost->opts); - } - } - - /* init pts */ - for (i = 0; i < nb_input_streams; i++) { - AVStream *st; - ist = &input_streams[i]; - st= ist->st; - ist->pts = st->avg_frame_rate.num ? - st->codec->has_b_frames*AV_TIME_BASE / av_q2d(st->avg_frame_rate) : 0; - ist->next_pts = AV_NOPTS_VALUE; - init_pts_correction(&ist->pts_ctx); - ist->is_start = 1; - } + /* init input streams */ + for (i = 0; i < nb_input_streams; i++) + if ((ret = init_input_stream(i, output_streams, nb_output_streams, error, sizeof(error))) < 0) + goto dump_format; /* open files and write file headers */ - for(i=0;ioformat->name, "rtp")) { + assert_avoptions(output_files[i].opts); + if (strcmp(os->oformat->name, "rtp")) { want_sdp = 0; } } @@ -2186,14 +2207,14 @@ static int transcode(AVFormatContext **output_files, /* dump the file output parameters - cannot be done before in case of stream copy */ for(i=0;ifilename, 1); + av_dump_format(output_files[i].ctx, i, output_files[i].ctx->filename, 1); } /* dump the stream mapping */ if (verbose >= 0) { fprintf(stderr, "Stream mapping:\n"); - for(i=0;i #%d.%d", input_streams[ost->source_index].file_index, input_streams[ost->source_index].st->index, @@ -2205,19 +2226,49 @@ static int transcode(AVFormatContext **output_files, ost->sync_ist->st->index); if (ost->st->stream_copy) fprintf(stderr, " (copy)"); + else + fprintf(stderr, " (%s -> %s)", input_streams[ost->source_index].dec ? + input_streams[ost->source_index].dec->name : "?", + ost->enc ? ost->enc->name : "?"); fprintf(stderr, "\n"); } } if (ret) { fprintf(stderr, "%s\n", error); - goto fail; + return ret; } if (want_sdp) { print_sdp(output_files, nb_output_files); } + return 0; +} + +/* + * The following code is the main loop of the file converter + */ +static int transcode(OutputFile *output_files, + int nb_output_files, + InputFile *input_files, + int nb_input_files) +{ + int ret, i; + AVFormatContext *is, *os; + OutputStream *ost; + InputStream *ist; + uint8_t *no_packet; + int no_packet_count=0; + int64_t timer_start; + + if (!(no_packet = av_mallocz(nb_input_files))) + exit_program(1); + + ret = transcode_init(output_files, nb_output_files, input_files, nb_input_files); + if (ret < 0) + goto fail; + if (verbose >= 0) fprintf(stderr, "Press ctrl-c to stop encoding\n"); term_init(); @@ -2230,22 +2281,24 @@ static int transcode(AVFormatContext **output_files, int64_t ipts_min; double opts_min; - redo: ipts_min = INT64_MAX; opts_min= 1e100; /* select the stream that we must read now by looking at the smallest output pts */ file_index = -1; - for(i=0;ifile_index]; + ost = &output_streams[i]; + of = &output_files[ost->file_index]; + os = output_files[ost->file_index].ctx; ist = &input_streams[ost->source_index]; - if(ist->is_past_recording_time || no_packet[ist->file_index]) + if (ost->is_past_recording_time || no_packet[ist->file_index] || + (os->pb && avio_tell(os->pb) >= of->limit_filesize)) continue; - opts = ost->st->pts.val * av_q2d(ost->st->time_base); + opts = ost->st->pts.val * av_q2d(ost->st->time_base); ipts = ist->pts; if (!input_files[ist->file_index].eof_reached){ if(ipts < ipts_min) { @@ -2257,26 +2310,24 @@ static int transcode(AVFormatContext **output_files, if(!input_sync) file_index = ist->file_index; } } - if(ost->frame_number >= max_frames[ost->st->codec->codec_type]){ - file_index= -1; - break; + if (ost->frame_number >= ost->max_frames) { + int j; + for (j = of->ost_index; j < of->ctx->nb_streams; j++) + output_streams[j].is_past_recording_time = 1; + continue; } } /* if none, if is finished */ if (file_index < 0) { if(no_packet_count){ no_packet_count=0; - memset(no_packet, 0, sizeof(no_packet)); + memset(no_packet, 0, nb_input_files); usleep(10000); continue; } break; } - /* finish if limit size exhausted */ - if (limit_filesize != 0 && limit_filesize <= avio_tell(output_files[0]->pb)) - break; - /* read a frame from it and output it in the fifo */ is = input_files[file_index].ctx; ret= av_read_frame(is, &pkt); @@ -2294,7 +2345,7 @@ static int transcode(AVFormatContext **output_files, } no_packet_count=0; - memset(no_packet, 0, sizeof(no_packet)); + memset(no_packet, 0, nb_input_files); if (do_pkt_dump) { av_pkt_dump_log2(NULL, AV_LOG_DEBUG, &pkt, do_hex_dump, @@ -2302,7 +2353,7 @@ static int transcode(AVFormatContext **output_files, } /* the following test is needed in case new streams appear dynamically in stream : we ignore them */ - if (pkt.stream_index >= input_files[file_index].ctx->nb_streams) + if (pkt.stream_index >= input_files[file_index].nb_streams) goto discard_packet; ist_index = input_files[file_index].ist_index + pkt.stream_index; ist = &input_streams[ist_index]; @@ -2314,12 +2365,10 @@ static int transcode(AVFormatContext **output_files, if (pkt.pts != AV_NOPTS_VALUE) pkt.pts += av_rescale_q(input_files[ist->file_index].ts_offset, AV_TIME_BASE_Q, ist->st->time_base); - if (ist->ts_scale) { - if(pkt.pts != AV_NOPTS_VALUE) - pkt.pts *= ist->ts_scale; - if(pkt.dts != AV_NOPTS_VALUE) - pkt.dts *= ist->ts_scale; - } + if(pkt.pts != AV_NOPTS_VALUE) + pkt.pts *= ist->ts_scale; + if(pkt.dts != AV_NOPTS_VALUE) + pkt.dts *= ist->ts_scale; // fprintf(stderr, "next:%"PRId64" dts:%"PRId64" off:%"PRId64" %d\n", ist->next_pts, pkt.dts, input_files[ist->file_index].ts_offset, ist->st->codec->codec_type); if (pkt.dts != AV_NOPTS_VALUE && ist->next_pts != AV_NOPTS_VALUE @@ -2337,15 +2386,8 @@ static int transcode(AVFormatContext **output_files, } } - /* finish if recording time exhausted */ - if (recording_time != INT64_MAX && - av_compare_ts(pkt.pts, ist->st->time_base, recording_time + start_time, (AVRational){1, 1000000}) >= 0) { - ist->is_past_recording_time = 1; - goto discard_packet; - } - //fprintf(stderr,"read #%d.%d size=%d\n", ist->file_index, ist->st->index, pkt.size); - if (output_packet(ist, ist_index, ost_table, nb_ostreams, &pkt) < 0) { + if (output_packet(ist, ist_index, output_streams, nb_output_streams, &pkt) < 0) { if (verbose >= 0) fprintf(stderr, "Error while decoding stream #%d.%d\n", @@ -2353,38 +2395,39 @@ static int transcode(AVFormatContext **output_files, if (exit_on_error) exit_program(1); av_free_packet(&pkt); - goto redo; + continue; } discard_packet: av_free_packet(&pkt); /* dump report by using the output first video and audio streams */ - print_report(output_files, ost_table, nb_ostreams, 0); + print_report(output_files, output_streams, nb_output_streams, 0, timer_start); } /* at the end of stream, we must flush the decoder buffers */ for (i = 0; i < nb_input_streams; i++) { ist = &input_streams[i]; if (ist->decoding_needed) { - output_packet(ist, i, ost_table, nb_ostreams, NULL); + output_packet(ist, i, output_streams, nb_output_streams, NULL); } } + flush_encoders(output_streams, nb_output_streams); term_exit(); /* write the trailer if needed and close file */ for(i=0;iencoding_needed) { av_freep(&ost->st->codec->stats_in); avcodec_close(ost->st->codec); @@ -2407,10 +2450,11 @@ static int transcode(AVFormatContext **output_files, fail: av_freep(&bit_buffer); + av_freep(&no_packet); - if (ost_table) { - for(i=0;ist->stream_copy) av_freep(&ost->st->codec->extradata); @@ -2430,20 +2474,12 @@ static int transcode(AVFormatContext **output_files, if (ost->reformat_ctx) av_audio_convert_free(ost->reformat_ctx); av_dict_free(&ost->opts); - av_free(ost); } } - av_free(ost_table); } return ret; } -static int opt_format(const char *opt, const char *arg) -{ - last_asked_format = arg; - return 0; -} - static int opt_video_rc_override_string(const char *opt, const char *arg) { video_rc_override_string = arg; @@ -2471,12 +2507,6 @@ static int opt_frame_rate(const char *opt, const char *arg) return 0; } -static int opt_frame_crop(const char *opt, const char *arg) -{ - fprintf(stderr, "Option '%s' has been removed, use the crop filter instead\n", opt); - return AVERROR(EINVAL); -} - static int opt_frame_size(const char *opt, const char *arg) { if (av_parse_video_size(&frame_width, &frame_height, arg) < 0) { @@ -2486,11 +2516,6 @@ static int opt_frame_size(const char *opt, const char *arg) return 0; } -static int opt_pad(const char *opt, const char *arg) { - fprintf(stderr, "Option '%s' has been removed, use the pad filter instead\n", opt); - return -1; -} - static int opt_frame_pix_fmt(const char *opt, const char *arg) { if (strcmp(arg, "list")) { @@ -2531,21 +2556,6 @@ static int opt_frame_aspect_ratio(const char *opt, const char *arg) return 0; } -static int opt_metadata(const char *opt, const char *arg) -{ - char *mid= strchr(arg, '='); - - if(!mid){ - fprintf(stderr, "Missing =\n"); - exit_program(1); - } - *mid++= 0; - - av_dict_set(&metadata, arg, mid, 0); - - return 0; -} - static int opt_qscale(const char *opt, const char *arg) { video_qscale = parse_number_or_die(opt, arg, OPT_FLOAT, 0, 255); @@ -2562,16 +2572,6 @@ static int opt_top_field_first(const char *opt, const char *arg) return 0; } -static int opt_thread_count(const char *opt, const char *arg) -{ - thread_count= parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX); -#if !HAVE_THREADS - if (verbose >= 0) - fprintf(stderr, "Warning: not compiled with thread support, using thread emulation\n"); -#endif - return 0; -} - static int opt_audio_sample_fmt(const char *opt, const char *arg) { if (strcmp(arg, "list")) { @@ -2596,56 +2596,27 @@ static int opt_audio_rate(const char *opt, const char *arg) return 0; } -static int opt_audio_channels(const char *opt, const char *arg) -{ - audio_channels = parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX); - return 0; -} - -static int opt_codec(const char *opt, const char *arg) +static int opt_audio_codec(OptionsContext *o, const char *opt, const char *arg) { - return av_dict_set(&codec_names, opt, arg, 0); + return parse_option(o, "codec:a", arg, options); } -static int opt_audio_codec(const char *opt, const char *arg) +static int opt_video_codec(OptionsContext *o, const char *opt, const char *arg) { - return opt_codec("codec:a", arg); + return parse_option(o, "codec:v", arg, options); } -static int opt_video_codec(const char *opt, const char *arg) +static int opt_subtitle_codec(OptionsContext *o, const char *opt, const char *arg) { - return opt_codec("codec:v", arg); + return parse_option(o, "codec:s", arg, options); } -static int opt_subtitle_codec(const char *opt, const char *arg) +static int opt_data_codec(OptionsContext *o, const char *opt, const char *arg) { - return opt_codec("codec:s", arg); + return parse_option(o, "codec:d", arg, options); } -static int opt_data_codec(const char *opt, const char *arg) -{ - return opt_codec("codec:d", arg); -} - -static int opt_codec_tag(const char *opt, const char *arg) -{ - char *tail; - uint32_t *codec_tag; - - codec_tag = !strcmp(opt, "atag") ? &audio_codec_tag : - !strcmp(opt, "vtag") ? &video_codec_tag : - !strcmp(opt, "stag") ? &subtitle_codec_tag : NULL; - if (!codec_tag) - return -1; - - *codec_tag = strtol(arg, &tail, 0); - if (!tail || *tail) - *codec_tag = AV_RL32(arg); - - return 0; -} - -static int opt_map(const char *opt, const char *arg) +static int opt_map(OptionsContext *o, const char *opt, const char *arg) { StreamMap *m = NULL; int i, negative = 0, file_idx; @@ -2669,13 +2640,13 @@ static int opt_map(const char *opt, const char *arg) } if (*sync) sync++; - for (i = 0; i < input_files[sync_file_idx].ctx->nb_streams; i++) + for (i = 0; i < input_files[sync_file_idx].nb_streams; i++) if (check_stream_specifier(input_files[sync_file_idx].ctx, input_files[sync_file_idx].ctx->streams[i], sync) == 1) { sync_stream_idx = i; break; } - if (i == input_files[sync_file_idx].ctx->nb_streams) { + if (i == input_files[sync_file_idx].nb_streams) { av_log(NULL, AV_LOG_ERROR, "Sync stream specification in map %s does not " "match any streams.\n", arg); exit_program(1); @@ -2690,20 +2661,21 @@ static int opt_map(const char *opt, const char *arg) } if (negative) /* disable some already defined maps */ - for (i = 0; i < nb_stream_maps; i++) { - m = &stream_maps[i]; + for (i = 0; i < o->nb_stream_maps; i++) { + m = &o->stream_maps[i]; if (check_stream_specifier(input_files[m->file_index].ctx, input_files[m->file_index].ctx->streams[m->stream_index], *p == ':' ? p + 1 : p) > 0) m->disabled = 1; } else - for (i = 0; i < input_files[file_idx].ctx->nb_streams; i++) { + for (i = 0; i < input_files[file_idx].nb_streams; i++) { if (check_stream_specifier(input_files[file_idx].ctx, input_files[file_idx].ctx->streams[i], *p == ':' ? p + 1 : p) <= 0) continue; - stream_maps = grow_array(stream_maps, sizeof(*stream_maps), &nb_stream_maps, nb_stream_maps + 1); - m = &stream_maps[nb_stream_maps - 1]; + o->stream_maps = grow_array(o->stream_maps, sizeof(*o->stream_maps), + &o->nb_stream_maps, o->nb_stream_maps + 1); + m = &o->stream_maps[o->nb_stream_maps - 1]; m->file_index = file_idx; m->stream_index = i; @@ -2728,8 +2700,8 @@ static int opt_map(const char *opt, const char *arg) static void parse_meta_type(char *arg, char *type, int *index) { - if (*arg == ':') { - *type = *(++arg); + if (*arg) { + *type = *arg; switch (*arg) { case 'g': break; @@ -2747,68 +2719,34 @@ static void parse_meta_type(char *arg, char *type, int *index) *type = 'g'; } -static int opt_map_metadata(const char *opt, const char *arg) +static int opt_map_metadata(OptionsContext *o, const char *opt, const char *arg) { MetadataMap *m, *m1; char *p; - meta_data_maps = grow_array(meta_data_maps, sizeof(*meta_data_maps), - &nb_meta_data_maps, nb_meta_data_maps + 1); + o->meta_data_maps = grow_array(o->meta_data_maps, sizeof(*o->meta_data_maps), + &o->nb_meta_data_maps, o->nb_meta_data_maps + 1); - m = &meta_data_maps[nb_meta_data_maps - 1][1]; + m = &o->meta_data_maps[o->nb_meta_data_maps - 1][1]; m->file = strtol(arg, &p, 0); - parse_meta_type(p, &m->type, &m->index); + parse_meta_type(*p ? p + 1 : p, &m->type, &m->index); - m1 = &meta_data_maps[nb_meta_data_maps - 1][0]; + m1 = &o->meta_data_maps[o->nb_meta_data_maps - 1][0]; if (p = strchr(opt, ':')) - parse_meta_type(p, &m1->type, &m1->index); + parse_meta_type(p + 1, &m1->type, &m1->index); else m1->type = 'g'; if (m->type == 'g' || m1->type == 'g') - metadata_global_autocopy = 0; + o->metadata_global_manual = 1; if (m->type == 's' || m1->type == 's') - metadata_streams_autocopy = 0; + o->metadata_streams_manual = 1; if (m->type == 'c' || m1->type == 'c') - metadata_chapters_autocopy = 0; + o->metadata_chapters_manual = 1; return 0; } -static int opt_input_ts_scale(const char *opt, const char *arg) -{ - unsigned int stream; - double scale; - char *p; - - stream = strtol(arg, &p, 0); - if (*p) - p++; - scale= strtod(p, &p); - - ts_scale = grow_array(ts_scale, sizeof(*ts_scale), &nb_ts_scale, stream + 1); - ts_scale[stream] = scale; - return 0; -} - -static int opt_recording_time(const char *opt, const char *arg) -{ - recording_time = parse_time_or_die(opt, arg, 1); - return 0; -} - -static int opt_start_time(const char *opt, const char *arg) -{ - start_time = parse_time_or_die(opt, arg, 1); - return 0; -} - -static int opt_input_ts_offset(const char *opt, const char *arg) -{ - input_ts_offset = parse_time_or_die(opt, arg, 1); - return 0; -} - static enum CodecID find_codec_or_die(const char *name, enum AVMediaType type, int encoder) { const char *codec_string = encoder ? "encoder" : "decoder"; @@ -2830,20 +2768,11 @@ static enum CodecID find_codec_or_die(const char *name, enum AVMediaType type, i return codec->id; } -static AVCodec *choose_codec(AVFormatContext *s, AVStream *st, enum AVMediaType type, AVDictionary *codec_names) +static AVCodec *choose_codec(OptionsContext *o, AVFormatContext *s, AVStream *st, enum AVMediaType type) { - 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); - } + MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, st); if (!codec_name) { if (s->oformat) { @@ -2865,7 +2794,7 @@ static AVCodec *choose_codec(AVFormatContext *s, AVStream *st, enum AVMediaType * Add all the streams from the given input file to the global * list of input streams. */ -static void add_input_streams(AVFormatContext *ic) +static void add_input_streams(OptionsContext *o, AVFormatContext *ic) { int i, rfps, rfps_base; @@ -2873,8 +2802,7 @@ static void add_input_streams(AVFormatContext *ic) AVStream *st = ic->streams[i]; AVCodecContext *dec = st->codec; InputStream *ist; - - dec->thread_count = thread_count; + double scale = 1.0; input_streams = grow_array(input_streams, sizeof(*input_streams), &nb_input_streams, nb_input_streams + 1); ist = &input_streams[nb_input_streams - 1]; @@ -2883,14 +2811,16 @@ static void add_input_streams(AVFormatContext *ic) ist->discard = 1; ist->opts = filter_codec_opts(codec_opts, ist->st->codec->codec_id, ic, st); - if (i < nb_ts_scale) - ist->ts_scale = ts_scale[i]; + MATCH_PER_STREAM_OPT(ts_scale, dbl, scale, ic, st); + ist->ts_scale = scale; - ist->dec = choose_codec(ic, st, dec->codec_type, codec_names); + ist->dec = choose_codec(o, ic, st, dec->codec_type); + if (!ist->dec) + ist->dec = avcodec_find_decoder(dec->codec_id); switch (dec->codec_type) { case AVMEDIA_TYPE_AUDIO: - if(audio_disable) + if (o->audio_disable) st->discard= AVDISCARD_ALL; break; case AVMEDIA_TYPE_VIDEO: @@ -2913,7 +2843,7 @@ static void add_input_streams(AVFormatContext *ic) (float)rfps / rfps_base, rfps, rfps_base); } - if(video_disable) + if (o->video_disable) st->discard= AVDISCARD_ALL; else if(video_discard) st->discard= video_discard; @@ -2921,7 +2851,7 @@ static void add_input_streams(AVFormatContext *ic) case AVMEDIA_TYPE_DATA: break; case AVMEDIA_TYPE_SUBTITLE: - if(subtitle_disable) + if (o->subtitle_disable) st->discard = AVDISCARD_ALL; break; case AVMEDIA_TYPE_ATTACHMENT: @@ -2933,7 +2863,7 @@ static void add_input_streams(AVFormatContext *ic) } } -static int opt_input_file(const char *opt, const char *filename) +static int opt_input_file(OptionsContext *o, const char *opt, const char *filename) { AVFormatContext *ic; AVInputFormat *file_iformat = NULL; @@ -2943,12 +2873,11 @@ static int opt_input_file(const char *opt, const char *filename) AVDictionary **opts; int orig_nb_streams; // number of streams before avformat_find_stream_info - if (last_asked_format) { - if (!(file_iformat = av_find_input_format(last_asked_format))) { - fprintf(stderr, "Unknown input format: '%s'\n", last_asked_format); + if (o->format) { + if (!(file_iformat = av_find_input_format(o->format))) { + fprintf(stderr, "Unknown input format: '%s'\n", o->format); exit_program(1); } - last_asked_format = NULL; } if (!strcmp(filename, "-")) @@ -2967,8 +2896,8 @@ static int opt_input_file(const char *opt, const char *filename) snprintf(buf, sizeof(buf), "%d", audio_sample_rate); av_dict_set(&format_opts, "sample_rate", buf, 0); } - if (audio_channels) { - snprintf(buf, sizeof(buf), "%d", audio_channels); + if (o->nb_audio_channels) { + snprintf(buf, sizeof(buf), "%d", o->audio_channels[o->nb_audio_channels - 1].u.i); av_dict_set(&format_opts, "channels", buf, 0); } if (frame_rate.num) { @@ -3018,7 +2947,7 @@ static int opt_input_file(const char *opt, const char *filename) /* 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); + choose_codec(o, ic, ic->streams[i], ic->streams[i]->codec->codec_type); /* Set AVCodecContext options for avformat_find_stream_info */ opts = setup_find_stream_info_opts(ic, codec_opts); @@ -3033,24 +2962,22 @@ static int opt_input_file(const char *opt, const char *filename) exit_program(1); } - timestamp = start_time; + timestamp = o->start_time; /* add the stream start time */ if (ic->start_time != AV_NOPTS_VALUE) timestamp += ic->start_time; /* if seeking requested, we execute it */ - if (start_time != 0) { + if (o->start_time != 0) { ret = av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD); if (ret < 0) { fprintf(stderr, "%s: could not seek to position %0.3f\n", filename, (double)timestamp / AV_TIME_BASE); } - /* reset seek info */ - start_time = 0; } /* update the current parameters so that they match the one of the input stream */ - add_input_streams(ic); + add_input_streams(o, ic); /* dump the file content */ if (verbose >= 0) @@ -3059,24 +2986,21 @@ static int opt_input_file(const char *opt, const char *filename) input_files = grow_array(input_files, sizeof(*input_files), &nb_input_files, nb_input_files + 1); input_files[nb_input_files - 1].ctx = ic; input_files[nb_input_files - 1].ist_index = nb_input_streams - ic->nb_streams; - input_files[nb_input_files - 1].ts_offset = input_ts_offset - (copy_ts ? 0 : timestamp); + input_files[nb_input_files - 1].ts_offset = o->input_ts_offset - (copy_ts ? 0 : timestamp); + input_files[nb_input_files - 1].nb_streams = ic->nb_streams; frame_rate = (AVRational){0, 0}; frame_pix_fmt = PIX_FMT_NONE; frame_height = 0; frame_width = 0; audio_sample_rate = 0; - audio_channels = 0; audio_sample_fmt = AV_SAMPLE_FMT_NONE; - av_freep(&ts_scale); - nb_ts_scale = 0; for (i = 0; i < orig_nb_streams; i++) av_dict_free(&opts[i]); av_freep(&opts); - av_dict_free(&codec_names); - uninit_opts(); - init_opts(); + + reset_options(o); return 0; } @@ -3103,33 +3027,28 @@ static void parse_forced_key_frames(char *kf, OutputStream *ost, } } -static OutputStream *new_output_stream(AVFormatContext *oc, enum AVMediaType type) +static OutputStream *new_output_stream(OptionsContext *o, AVFormatContext *oc, enum AVMediaType type) { OutputStream *ost; AVStream *st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); int idx = oc->nb_streams - 1; + int64_t max_frames = INT64_MAX; + char *bsf = NULL, *next, *codec_tag = NULL; + AVBitStreamFilterContext *bsfc, *bsfc_prev = NULL; if (!st) { av_log(NULL, AV_LOG_ERROR, "Could not alloc stream.\n"); exit_program(1); } - output_streams_for_file[nb_output_files] = - grow_array(output_streams_for_file[nb_output_files], - sizeof(*output_streams_for_file[nb_output_files]), - &nb_output_streams_for_file[nb_output_files], - oc->nb_streams); - ost = output_streams_for_file[nb_output_files][idx] = - av_mallocz(sizeof(OutputStream)); - if (!ost) { - fprintf(stderr, "Could not alloc output stream\n"); - exit_program(1); - } + output_streams = grow_array(output_streams, sizeof(*output_streams), &nb_output_streams, + nb_output_streams + 1); + ost = &output_streams[nb_output_streams - 1]; ost->file_index = nb_output_files; ost->index = idx; ost->st = st; st->codec->codec_type = type; - ost->enc = choose_codec(oc, st, type, codec_names); + ost->enc = choose_codec(o, oc, st, type); if (ost->enc) { ost->opts = filter_codec_opts(codec_opts, ost->enc->id, oc, st); } @@ -3137,17 +3056,45 @@ static OutputStream *new_output_stream(AVFormatContext *oc, enum AVMediaType typ 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 + MATCH_PER_STREAM_OPT(max_frames, i64, max_frames, oc, st); + ost->max_frames = max_frames; + + MATCH_PER_STREAM_OPT(bitstream_filters, str, bsf, oc, st); + while (bsf) { + if (next = strchr(bsf, ',')) + *next++ = 0; + if (!(bsfc = av_bitstream_filter_init(bsf))) { + av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter %s\n", bsf); + exit_program(1); + } + if (bsfc_prev) + bsfc_prev->next = bsfc; + else + ost->bitstream_filters = bsfc; + + bsfc_prev = bsfc; + bsf = next; + } + + MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); + if (codec_tag) { + uint32_t tag = strtol(codec_tag, &next, 0); + if (*next) + tag = AV_RL32(codec_tag); + st->codec->codec_tag = tag; + } + ost->sws_flags = av_get_int(sws_opts, "sws_flags", NULL); return ost; } -static OutputStream *new_video_stream(AVFormatContext *oc) +static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc) { AVStream *st; OutputStream *ost; AVCodecContext *video_enc; - ost = new_output_stream(oc, AVMEDIA_TYPE_VIDEO); + ost = new_output_stream(o, oc, AVMEDIA_TYPE_VIDEO); st = ost->st; if (!st->stream_copy) { ost->frame_aspect_ratio = frame_aspect_ratio; @@ -3158,16 +3105,8 @@ static OutputStream *new_video_stream(AVFormatContext *oc) #endif } - ost->bitstream_filters = video_bitstream_filters; - video_bitstream_filters= NULL; - - st->codec->thread_count= thread_count; - video_enc = st->codec; - if(video_codec_tag) - video_enc->codec_tag= video_codec_tag; - if(oc->oformat->flags & AVFMT_GLOBALHEADER) { video_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } @@ -3187,8 +3126,6 @@ static OutputStream *new_video_stream(AVFormatContext *oc) video_enc->pix_fmt = frame_pix_fmt; st->sample_aspect_ratio = video_enc->sample_aspect_ratio; - if (intra_only) - video_enc->gop_size = 0; if (video_qscale || same_quant) { video_enc->flags |= CODEC_FLAG_QSCALE; video_enc->global_quality = FF_QP2LAMBDA * video_qscale; @@ -3244,38 +3181,25 @@ static OutputStream *new_video_stream(AVFormatContext *oc) if (forced_key_frames) parse_forced_key_frames(forced_key_frames, ost, video_enc); } - if (video_language) { - av_dict_set(&st->metadata, "language", video_language, 0); - av_freep(&video_language); - } /* reset some key parameters */ - video_disable = 0; av_freep(&forced_key_frames); frame_pix_fmt = PIX_FMT_NONE; return ost; } -static OutputStream *new_audio_stream(AVFormatContext *oc) +static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc) { AVStream *st; OutputStream *ost; AVCodecContext *audio_enc; - ost = new_output_stream(oc, AVMEDIA_TYPE_AUDIO); + ost = new_output_stream(o, oc, AVMEDIA_TYPE_AUDIO); st = ost->st; - ost->bitstream_filters = audio_bitstream_filters; - audio_bitstream_filters= NULL; - - st->codec->thread_count= thread_count; - audio_enc = st->codec; audio_enc->codec_type = AVMEDIA_TYPE_AUDIO; - if(audio_codec_tag) - audio_enc->codec_tag= audio_codec_tag; - if (oc->oformat->flags & AVFMT_GLOBALHEADER) { audio_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } @@ -3284,31 +3208,24 @@ static OutputStream *new_audio_stream(AVFormatContext *oc) audio_enc->flags |= CODEC_FLAG_QSCALE; audio_enc->global_quality = FF_QP2LAMBDA * audio_qscale; } - if (audio_channels) - audio_enc->channels = audio_channels; + MATCH_PER_STREAM_OPT(audio_channels, i, audio_enc->channels, oc, st); + if (audio_sample_fmt != AV_SAMPLE_FMT_NONE) audio_enc->sample_fmt = audio_sample_fmt; if (audio_sample_rate) audio_enc->sample_rate = audio_sample_rate; } - if (audio_language) { - av_dict_set(&st->metadata, "language", audio_language, 0); - av_freep(&audio_language); - } - - /* reset some key parameters */ - audio_disable = 0; return ost; } -static OutputStream *new_data_stream(AVFormatContext *oc) +static OutputStream *new_data_stream(OptionsContext *o, AVFormatContext *oc) { AVStream *st; OutputStream *ost; AVCodecContext *data_enc; - ost = new_output_stream(oc, AVMEDIA_TYPE_DATA); + ost = new_output_stream(o, oc, AVMEDIA_TYPE_DATA); st = ost->st; data_enc = st->codec; if (!st->stream_copy) { @@ -3316,45 +3233,29 @@ static OutputStream *new_data_stream(AVFormatContext *oc) exit_program(1); } - if (data_codec_tag) - data_enc->codec_tag= data_codec_tag; - if (oc->oformat->flags & AVFMT_GLOBALHEADER) { data_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } - data_disable = 0; return ost; } -static OutputStream *new_subtitle_stream(AVFormatContext *oc) +static OutputStream *new_subtitle_stream(OptionsContext *o, AVFormatContext *oc) { AVStream *st; OutputStream *ost; AVCodecContext *subtitle_enc; - ost = new_output_stream(oc, AVMEDIA_TYPE_SUBTITLE); + ost = new_output_stream(o, oc, AVMEDIA_TYPE_SUBTITLE); st = ost->st; subtitle_enc = st->codec; - ost->bitstream_filters = subtitle_bitstream_filters; - subtitle_bitstream_filters= NULL; - subtitle_enc->codec_type = AVMEDIA_TYPE_SUBTITLE; - if(subtitle_codec_tag) - subtitle_enc->codec_tag= subtitle_codec_tag; - if (oc->oformat->flags & AVFMT_GLOBALHEADER) { subtitle_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; } - if (subtitle_language) { - av_dict_set(&st->metadata, "language", subtitle_language, 0); - av_freep(&subtitle_language); - } - - subtitle_disable = 0; return ost; } @@ -3380,18 +3281,18 @@ static int opt_streamid(const char *opt, const char *arg) return 0; } -static int copy_chapters(int infile, int outfile) +static int copy_chapters(InputFile *ifile, OutputFile *ofile, int copy_metadata) { - AVFormatContext *is = input_files[infile].ctx; - AVFormatContext *os = output_files[outfile]; + AVFormatContext *is = ifile->ctx; + AVFormatContext *os = ofile->ctx; int i; for (i = 0; i < is->nb_chapters; i++) { AVChapter *in_ch = is->chapters[i], *out_ch; - int64_t ts_off = av_rescale_q(start_time - input_files[infile].ts_offset, + int64_t ts_off = av_rescale_q(ofile->start_time - ifile->ts_offset, AV_TIME_BASE_Q, in_ch->time_base); - int64_t rt = (recording_time == INT64_MAX) ? INT64_MAX : - av_rescale_q(recording_time, AV_TIME_BASE_Q, in_ch->time_base); + int64_t rt = (ofile->recording_time == INT64_MAX) ? INT64_MAX : + av_rescale_q(ofile->recording_time, AV_TIME_BASE_Q, in_ch->time_base); if (in_ch->end < ts_off) @@ -3408,7 +3309,7 @@ static int copy_chapters(int infile, int outfile) out_ch->start = FFMAX(0, in_ch->start - ts_off); out_ch->end = FFMIN(rt, in_ch->end - ts_off); - if (metadata_chapters_autocopy) + if (copy_metadata) av_dict_copy(&out_ch->metadata, in_ch->metadata, 0); os->nb_chapters++; @@ -3420,7 +3321,7 @@ static int copy_chapters(int infile, int outfile) return 0; } -static int read_avserver_streams(AVFormatContext *s, const char *filename) +static int read_avserver_streams(OptionsContext *o, AVFormatContext *s, const char *filename) { int i, err; AVFormatContext *ic = NULL; @@ -3435,7 +3336,7 @@ static int read_avserver_streams(AVFormatContext *s, const char *filename) AVCodec *codec; codec = avcodec_find_encoder(ic->streams[i]->codec->codec_id); - ost = new_output_stream(s, codec->type); + ost = new_output_stream(o, s, codec->type); st = ost->st; // FIXME: a more elegant solution is needed @@ -3453,8 +3354,9 @@ static int read_avserver_streams(AVFormatContext *s, const char *filename) return 0; } -static void opt_output_file(const char *filename) +static void opt_output_file(void *optctx, const char *filename) { + OptionsContext *o = optctx; AVFormatContext *oc; int i, err; AVOutputFormat *file_oformat; @@ -3470,13 +3372,12 @@ static void opt_output_file(const char *filename) exit_program(1); } - if (last_asked_format) { - file_oformat = av_guess_format(last_asked_format, NULL, NULL); + if (o->format) { + file_oformat = av_guess_format(o->format, NULL, NULL); if (!file_oformat) { - fprintf(stderr, "Requested output format '%s' is not a suitable output format\n", last_asked_format); + fprintf(stderr, "Requested output format '%s' is not a suitable output format\n", o->format); exit_program(1); } - last_asked_format = NULL; } else { file_oformat = av_guess_format(NULL, filename, NULL); if (!file_oformat) { @@ -3493,23 +3394,23 @@ static void opt_output_file(const char *filename) av_strstart(filename, "http:", NULL)) { /* special case for files sent to avserver: we get the stream parameters from avserver */ - int err = read_avserver_streams(oc, filename); + int err = read_avserver_streams(o, oc, filename); if (err < 0) { print_error(filename, err); exit_program(1); } - } else if (!nb_stream_maps) { + } else if (!o->nb_stream_maps) { /* pick the "best" stream of each type */ #define NEW_STREAM(type, index)\ if (index >= 0) {\ - ost = new_ ## type ## _stream(oc);\ + ost = new_ ## type ## _stream(o, oc);\ ost->source_index = index;\ ost->sync_ist = &input_streams[index];\ input_streams[index].discard = 0;\ } /* video: highest resolution */ - if (!video_disable && oc->oformat->video_codec != CODEC_ID_NONE) { + if (!o->video_disable && oc->oformat->video_codec != CODEC_ID_NONE) { int area = 0, idx = -1; for (i = 0; i < nb_input_streams; i++) { ist = &input_streams[i]; @@ -3523,7 +3424,7 @@ static void opt_output_file(const char *filename) } /* audio: most channels */ - if (!audio_disable && oc->oformat->audio_codec != CODEC_ID_NONE) { + if (!o->audio_disable && oc->oformat->audio_codec != CODEC_ID_NONE) { int channels = 0, idx = -1; for (i = 0; i < nb_input_streams; i++) { ist = &input_streams[i]; @@ -3537,7 +3438,7 @@ static void opt_output_file(const char *filename) } /* subtitles: pick first */ - if (!subtitle_disable && oc->oformat->subtitle_codec != CODEC_ID_NONE) { + if (!o->subtitle_disable && oc->oformat->subtitle_codec != CODEC_ID_NONE) { for (i = 0; i < nb_input_streams; i++) if (input_streams[i].st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { NEW_STREAM(subtitle, i); @@ -3546,18 +3447,18 @@ static void opt_output_file(const char *filename) } /* do something with data? */ } else { - for (i = 0; i < nb_stream_maps; i++) { - StreamMap *map = &stream_maps[i]; + for (i = 0; i < o->nb_stream_maps; i++) { + StreamMap *map = &o->stream_maps[i]; if (map->disabled) continue; ist = &input_streams[input_files[map->file_index].ist_index + map->stream_index]; switch (ist->st->codec->codec_type) { - case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(oc); break; - case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(oc); break; - case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(oc); break; - case AVMEDIA_TYPE_DATA: ost = new_data_stream(oc); break; + case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(o, oc); break; + case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(o, oc); break; + case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(o, oc); break; + case AVMEDIA_TYPE_DATA: ost = new_data_stream(o, oc); break; default: av_log(NULL, AV_LOG_ERROR, "Cannot map stream #%d.%d - unsupported type.\n", map->file_index, map->stream_index); @@ -3571,11 +3472,13 @@ static void opt_output_file(const char *filename) } } - av_dict_copy(&oc->metadata, metadata, 0); - av_dict_free(&metadata); - - av_dict_copy(&output_opts[nb_output_files], format_opts, 0); - output_files[nb_output_files++] = oc; + output_files = grow_array(output_files, sizeof(*output_files), &nb_output_files, nb_output_files + 1); + output_files[nb_output_files - 1].ctx = oc; + output_files[nb_output_files - 1].ost_index = nb_output_streams - oc->nb_streams; + output_files[nb_output_files - 1].recording_time = o->recording_time; + output_files[nb_output_files - 1].start_time = o->start_time; + output_files[nb_output_files - 1].limit_filesize = o->limit_filesize; + av_dict_copy(&output_files[nb_output_files - 1].opts, format_opts, 0); /* check filename in case of an image number is expected */ if (oc->oformat->flags & AVFMT_NEEDNUMBER) { @@ -3614,31 +3517,32 @@ static void opt_output_file(const char *filename) } } - oc->preload= (int)(mux_preload*AV_TIME_BASE); - oc->max_delay= (int)(mux_max_delay*AV_TIME_BASE); + oc->preload = (int)(o->mux_preload * AV_TIME_BASE); + oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE); oc->flags |= AVFMT_FLAG_NONBLOCK; /* copy chapters */ - if (chapters_input_file >= nb_input_files) { - if (chapters_input_file == INT_MAX) { + if (o->chapters_input_file >= nb_input_files) { + if (o->chapters_input_file == INT_MAX) { /* copy chapters from the first input file that has them*/ - chapters_input_file = -1; + o->chapters_input_file = -1; for (i = 0; i < nb_input_files; i++) if (input_files[i].ctx->nb_chapters) { - chapters_input_file = i; + o->chapters_input_file = i; break; } } else { av_log(NULL, AV_LOG_ERROR, "Invalid input file index %d in chapter mapping.\n", - chapters_input_file); + o->chapters_input_file); exit_program(1); } } - if (chapters_input_file >= 0) - copy_chapters(chapters_input_file, nb_output_files - 1); + if (o->chapters_input_file >= 0) + copy_chapters(&input_files[o->chapters_input_file], &output_files[nb_output_files - 1], + o->metadata_chapters_manual); /* copy metadata */ - for (i = 0; i < nb_meta_data_maps; i++) { + for (i = 0; i < o->nb_meta_data_maps; i++) { AVFormatContext *files[2]; AVDictionary **meta[2]; int j; @@ -3650,7 +3554,7 @@ static void opt_output_file(const char *filename) exit_program(1);\ } - int in_file_index = meta_data_maps[i][1].file; + int in_file_index = o->meta_data_maps[i][1].file; if (in_file_index < 0) continue; METADATA_CHECK_INDEX(in_file_index, nb_input_files, "input file") @@ -3659,7 +3563,7 @@ static void opt_output_file(const char *filename) files[1] = input_files[in_file_index].ctx; for (j = 0; j < 2; j++) { - MetadataMap *map = &meta_data_maps[i][j]; + MetadataMap *map = &o->meta_data_maps[i][j]; switch (map->type) { case 'g': @@ -3684,36 +3588,67 @@ static void opt_output_file(const char *filename) } /* copy global metadata by default */ - if (metadata_global_autocopy && nb_input_files) + if (!o->metadata_global_manual && nb_input_files) av_dict_copy(&oc->metadata, input_files[0].ctx->metadata, AV_DICT_DONT_OVERWRITE); - if (metadata_streams_autocopy) - for (i = 0; i < oc->nb_streams; i++) { - InputStream *ist = &input_streams[output_streams_for_file[nb_output_files-1][i]->source_index]; - av_dict_copy(&oc->streams[i]->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE); + if (!o->metadata_streams_manual) + for (i = output_files[nb_output_files - 1].ost_index; i < nb_output_streams; i++) { + InputStream *ist = &input_streams[output_streams[i].source_index]; + av_dict_copy(&output_streams[i].st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE); } + /* process manually set metadata */ + for (i = 0; i < o->nb_metadata; i++) { + AVDictionary **m; + char type, *val; + int index = 0; + + val = strchr(o->metadata[i].u.str, '='); + if (!val) { + av_log(NULL, AV_LOG_ERROR, "No '=' character in metadata string %s.\n", + o->metadata[i].u.str); + exit_program(1); + } + *val++ = 0; + + parse_meta_type(o->metadata[i].specifier, &type, &index); + switch (type) { + case 'g': + m = &oc->metadata; + break; + case 's': + if (index < 0 || index >= oc->nb_streams) { + av_log(NULL, AV_LOG_ERROR, "Invalid stream index %d in metadata specifier.\n", index); + exit_program(1); + } + m = &oc->streams[i]->metadata; + break; + case 'c': + if (index < 0 || index >= oc->nb_chapters) { + av_log(NULL, AV_LOG_ERROR, "Invalid chapter index %d in metadata specifier.\n", index); + exit_program(1); + } + m = &oc->chapters[i]->metadata; + break; + default: + av_log(NULL, AV_LOG_ERROR, "Invalid metadata specifier %s.\n", o->metadata[i].specifier); + exit_program(1); + } + + av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0); + } + frame_rate = (AVRational){0, 0}; frame_width = 0; frame_height = 0; audio_sample_rate = 0; - audio_channels = 0; audio_sample_fmt = AV_SAMPLE_FMT_NONE; - chapters_input_file = INT_MAX; - - av_freep(&meta_data_maps); - nb_meta_data_maps = 0; - metadata_global_autocopy = 1; - metadata_streams_autocopy = 1; - metadata_chapters_autocopy = 1; - av_freep(&stream_maps); - nb_stream_maps = 0; - av_dict_free(&codec_names); + av_freep(&streamid_map); + nb_streamid_map = 0; av_freep(&forced_key_frames); - uninit_opts(); - init_opts(); + reset_options(o); } /* same option as mencoder */ @@ -3800,6 +3735,7 @@ static void show_help(void) AVCodec *c; AVOutputFormat *oformat = NULL; AVInputFormat *iformat = NULL; + const AVClass *class; av_log_set_callback(log_callback_help); show_usage(); @@ -3827,7 +3763,8 @@ static void show_help(void) OPT_GRAB, OPT_GRAB); printf("\n"); - av_opt_show2(avcodec_opts[0], NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); + class = avcodec_get_class(); + av_opt_show2(&class, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); printf("\n"); /* individual codec options */ @@ -3839,7 +3776,8 @@ static void show_help(void) } } - av_opt_show2(avformat_opts, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); + class = avformat_get_class(); + av_opt_show2(&class, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); printf("\n"); /* individual muxer options */ @@ -3858,10 +3796,11 @@ static void show_help(void) } } - av_opt_show2(sws_opts, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); + class = sws_get_class(); + av_opt_show2(&class, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); } -static int opt_target(const char *opt, const char *arg) +static int opt_target(OptionsContext *o, const char *opt, const char *arg) { enum { PAL, NTSC, FILM, UNKNOWN } norm = UNKNOWN; static const char *const frame_rates[] = {"25", "30000/1001", "24000/1001"}; @@ -3888,7 +3827,7 @@ static int opt_target(const char *opt, const char *arg) if(nb_input_files) { int i, j; for (j = 0; j < nb_input_files; j++) { - for (i = 0; i < input_files[j].ctx->nb_streams; i++) { + for (i = 0; i < input_files[j].nb_streams; i++) { AVCodecContext *c = input_files[j].ctx->streams[i]->codec; if(c->codec_type != AVMEDIA_TYPE_VIDEO) continue; @@ -3918,9 +3857,9 @@ static int opt_target(const char *opt, const char *arg) } if(!strcmp(arg, "vcd")) { - opt_codec("c:v", "mpeg1video"); - opt_codec("c:a", "mp2"); - opt_format("f", "vcd"); + opt_video_codec(o, "c:v", "mpeg1video"); + opt_audio_codec(o, "c:a", "mp2"); + parse_option(o, "f", "vcd", options); opt_frame_size("s", norm == PAL ? "352x288" : "352x240"); opt_frame_rate("r", frame_rates[norm]); @@ -3933,7 +3872,7 @@ static int opt_target(const char *opt, const char *arg) opt_default("b:a", "224000"); audio_sample_rate = 44100; - audio_channels = 2; + parse_option(o, "ac", "2", options); opt_default("packetsize", "2324"); opt_default("muxrate", "1411200"); // 2352 * 75 * 8; @@ -3943,12 +3882,12 @@ static int opt_target(const char *opt, const char *arg) and the first pack from the other stream, respectively, may also have been written before. So the real data starts at SCR 36000+3*1200. */ - mux_preload= (36000+3*1200) / 90000.0; //0.44 + o->mux_preload = (36000+3*1200) / 90000.0; //0.44 } else if(!strcmp(arg, "svcd")) { - opt_codec("c:v", "mpeg2video"); - opt_codec("c:a", "mp2"); - opt_format("f", "svcd"); + opt_video_codec(o, "c:v", "mpeg2video"); + opt_audio_codec(o, "c:a", "mp2"); + parse_option(o, "f", "svcd", options); opt_frame_size("s", norm == PAL ? "480x576" : "480x480"); opt_frame_rate("r", frame_rates[norm]); @@ -3968,9 +3907,9 @@ static int opt_target(const char *opt, const char *arg) } else if(!strcmp(arg, "dvd")) { - opt_codec("c:v", "mpeg2video"); - opt_codec("c:a", "ac3"); - opt_format("f", "dvd"); + opt_video_codec(o, "c:v", "mpeg2video"); + opt_audio_codec(o, "c:a", "ac3"); + parse_option(o, "f", "dvd", options); opt_frame_size("vcodec", norm == PAL ? "720x576" : "720x480"); opt_frame_rate("r", frame_rates[norm]); @@ -3989,7 +3928,7 @@ static int opt_target(const char *opt, const char *arg) } else if(!strncmp(arg, "dv", 2)) { - opt_format("f", "dv"); + parse_option(o, "f", "dv", options); opt_frame_size("s", norm == PAL ? "720x576" : "720x480"); opt_frame_pix_fmt("pix_fmt", !strncmp(arg, "dv50", 4) ? "yuv422p" : @@ -3997,7 +3936,7 @@ static int opt_target(const char *opt, const char *arg) opt_frame_rate("r", frame_rates[norm]); audio_sample_rate = 48000; - audio_channels = 2; + parse_option(o, "ac", "2", options); } else { fprintf(stderr, "Unknown target: %s\n", arg); @@ -4024,46 +3963,56 @@ static int opt_vstats(const char *opt, const char *arg) return opt_vstats_file(opt, filename); } -static int opt_bsf(const char *opt, const char *arg) +static int opt_video_frames(OptionsContext *o, const char *opt, const char *arg) { - AVBitStreamFilterContext *bsfc= av_bitstream_filter_init(arg); //FIXME split name and args for filter at '=' - AVBitStreamFilterContext **bsfp; + return parse_option(o, "frames:v", arg, options); +} - if(!bsfc){ - fprintf(stderr, "Unknown bitstream filter %s\n", arg); - exit_program(1); - } +static int opt_audio_frames(OptionsContext *o, const char *opt, const char *arg) +{ + return parse_option(o, "frames:a", arg, options); +} - bsfp= *opt == 'v' ? &video_bitstream_filters : - *opt == 'a' ? &audio_bitstream_filters : - &subtitle_bitstream_filters; - while(*bsfp) - bsfp= &(*bsfp)->next; +static int opt_data_frames(OptionsContext *o, const char *opt, const char *arg) +{ + return parse_option(o, "frames:d", arg, options); +} - *bsfp= bsfc; +static int opt_video_tag(OptionsContext *o, const char *opt, const char *arg) +{ + return parse_option(o, "tag:v", arg, options); +} - return 0; +static int opt_audio_tag(OptionsContext *o, const char *opt, const char *arg) +{ + return parse_option(o, "tag:a", arg, options); } +static int opt_subtitle_tag(OptionsContext *o, const char *opt, const char *arg) +{ + return parse_option(o, "tag:s", arg, options); +} + +#define OFFSET(x) offsetof(OptionsContext, x) static const OptionDef options[] = { /* main options */ #include "cmdutils_common_opts.h" - { "f", HAS_ARG, {(void*)opt_format}, "force format", "fmt" }, - { "i", HAS_ARG, {(void*)opt_input_file}, "input file name", "filename" }, + { "f", HAS_ARG | OPT_STRING | OPT_OFFSET, {.off = OFFSET(format)}, "force format", "fmt" }, + { "i", HAS_ARG | OPT_FUNC2, {(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_metadata", HAS_ARG | OPT_EXPERT, {(void*)opt_map_metadata}, "set metadata information of outfile from infile", + { "c", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" }, + { "codec", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(codec_names)}, "codec name", "codec" }, + { "map", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map}, "set input stream mapping", "file.stream[:syncfile.syncstream]" }, + { "map_metadata", HAS_ARG | OPT_EXPERT | OPT_FUNC2, {(void*)opt_map_metadata}, "set metadata information of outfile from infile", "outfile[,metadata]:infile[,metadata]" }, - { "map_chapters", OPT_INT | HAS_ARG | OPT_EXPERT, {(void*)&chapters_input_file}, "set chapters mapping", "input_file_index" }, - { "t", HAS_ARG, {(void*)opt_recording_time}, "record or transcode \"duration\" seconds of audio/video", "duration" }, - { "fs", HAS_ARG | OPT_INT64, {(void*)&limit_filesize}, "set the limit file size in bytes", "limit_size" }, // - { "ss", HAS_ARG, {(void*)opt_start_time}, "set the start time offset", "time_off" }, - { "itsoffset", HAS_ARG, {(void*)opt_input_ts_offset}, "set the input ts offset", "time_off" }, - { "itsscale", HAS_ARG, {(void*)opt_input_ts_scale}, "set the input ts scale", "stream:scale" }, - { "metadata", HAS_ARG, {(void*)opt_metadata}, "add metadata", "string=string" }, - { "dframes", OPT_INT | HAS_ARG, {(void*)&max_frames[AVMEDIA_TYPE_DATA]}, "set the number of data frames to record", "number" }, + { "map_chapters", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_OFFSET, {.off = OFFSET(chapters_input_file)}, "set chapters mapping", "input_file_index" }, + { "t", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(recording_time)}, "record or transcode \"duration\" seconds of audio/video", "duration" }, + { "fs", HAS_ARG | OPT_INT64 | OPT_OFFSET, {.off = OFFSET(limit_filesize)}, "set the limit file size in bytes", "limit_size" }, // + { "ss", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(start_time)}, "set the start time offset", "time_off" }, + { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET, {.off = OFFSET(input_ts_offset)}, "set the input ts offset", "time_off" }, + { "itsscale", HAS_ARG | OPT_DOUBLE | OPT_SPEC, {.off = OFFSET(ts_scale)}, "set the input ts scale", "scale" }, + { "metadata", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(metadata)}, "add metadata", "string=string" }, + { "dframes", HAS_ARG | OPT_FUNC2, {(void*)opt_data_frames}, "set the number of data frames to record", "number" }, { "benchmark", OPT_BOOL | OPT_EXPERT, {(void*)&do_benchmark}, "add timings for benchmarking" }, { "timelimit", HAS_ARG, {(void*)opt_timelimit}, "set max runtime in seconds", "limit" }, @@ -4071,10 +4020,9 @@ static const OptionDef options[] = { "dump each input packet" }, { "hex", OPT_BOOL | OPT_EXPERT, {(void*)&do_hex_dump}, "when dumping packets, also dump the payload" }, - { "re", OPT_BOOL | OPT_EXPERT, {(void*)&rate_emu}, "read input at native frame rate", "" }, + { "re", OPT_BOOL | OPT_EXPERT | OPT_OFFSET, {.off = OFFSET(rate_emu)}, "read input at native frame rate", "" }, { "v", HAS_ARG, {(void*)opt_verbose}, "set the verbosity level", "number" }, - { "target", HAS_ARG, {(void*)opt_target}, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\", \"dv50\", \"pal-vcd\", \"ntsc-svcd\", ...)", "type" }, - { "threads", HAS_ARG | OPT_EXPERT, {(void*)opt_thread_count}, "thread count", "count" }, + { "target", HAS_ARG | OPT_FUNC2, {(void*)opt_target}, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\", \"dv50\", \"pal-vcd\", \"ntsc-svcd\", ...)", "type" }, { "vsync", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&video_sync_method}, "video sync method", "" }, { "async", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&audio_sync_method}, "audio sync method", "" }, { "adrift_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, {(void*)&audio_drift_threshold}, "audio drift threshold", "threshold" }, @@ -4085,29 +4033,21 @@ static const OptionDef options[] = { { "programid", HAS_ARG | OPT_INT | OPT_EXPERT, {(void*)&opt_programid}, "desired program number", "" }, { "xerror", OPT_BOOL, {(void*)&exit_on_error}, "exit on error", "error" }, { "copyinkf", OPT_BOOL | OPT_EXPERT, {(void*)©_initial_nonkeyframes}, "copy initial non-keyframes" }, + { "frames", OPT_INT64 | HAS_ARG | OPT_SPEC, {.off = OFFSET(max_frames)}, "set the number of frames to record", "number" }, + { "tag", OPT_STRING | HAS_ARG | OPT_SPEC, {.off = OFFSET(codec_tags)}, "force codec tag/fourcc", "fourcc/tag" }, /* video options */ - { "vframes", OPT_INT | HAS_ARG | OPT_VIDEO, {(void*)&max_frames[AVMEDIA_TYPE_VIDEO]}, "set the number of video frames to record", "number" }, + { "vframes", HAS_ARG | OPT_VIDEO | OPT_FUNC2, {(void*)opt_video_frames}, "set the number of video frames to record", "number" }, { "r", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_rate}, "set frame rate (Hz value, fraction or abbreviation)", "rate" }, { "s", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_size}, "set frame size (WxH or abbreviation)", "size" }, { "aspect", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_aspect_ratio}, "set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)", "aspect" }, { "pix_fmt", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_frame_pix_fmt}, "set pixel format, 'list' as argument shows all the pixel formats supported", "format" }, - { "croptop", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop}, "Removed, use the crop filter instead", "size" }, - { "cropbottom", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop}, "Removed, use the crop filter instead", "size" }, - { "cropleft", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop}, "Removed, use the crop filter instead", "size" }, - { "cropright", HAS_ARG | OPT_VIDEO, {(void*)opt_frame_crop}, "Removed, use the crop filter instead", "size" }, - { "padtop", HAS_ARG | OPT_VIDEO, {(void*)opt_pad}, "Removed, use the pad filter instead", "size" }, - { "padbottom", HAS_ARG | OPT_VIDEO, {(void*)opt_pad}, "Removed, use the pad filter instead", "size" }, - { "padleft", HAS_ARG | OPT_VIDEO, {(void*)opt_pad}, "Removed, use the pad filter instead", "size" }, - { "padright", HAS_ARG | OPT_VIDEO, {(void*)opt_pad}, "Removed, use the pad filter instead", "size" }, - { "padcolor", HAS_ARG | OPT_VIDEO, {(void*)opt_pad}, "Removed, use the pad filter instead", "color" }, - { "intra", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, {(void*)&intra_only}, "use only intra frames"}, - { "vn", OPT_BOOL | OPT_VIDEO, {(void*)&video_disable}, "disable video" }, + { "vn", OPT_BOOL | OPT_VIDEO | OPT_OFFSET, {.off = OFFSET(video_disable)}, "disable video" }, { "vdt", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)&video_discard}, "discard threshold", "n" }, { "qscale", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_qscale}, "use fixed video quantizer scale (VBR)", "q" }, { "rc_override", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_video_rc_override_string}, "rate control override for specific intervals", "override" }, - { "vcodec", HAS_ARG | OPT_VIDEO, {(void*)opt_video_codec}, "force video codec ('copy' to copy stream)", "codec" }, - { "me_threshold", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_me_threshold}, "motion estimaton threshold", "threshold" }, + { "vcodec", HAS_ARG | OPT_VIDEO | OPT_FUNC2, {(void*)opt_video_codec}, "force video codec ('copy' to copy stream)", "codec" }, + { "me_threshold", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_me_threshold}, "motion estimation threshold", "threshold" }, { "same_quant", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant}, "use same quantizer as source (implies VBR)" }, { "pass", HAS_ARG | OPT_VIDEO, {(void*)opt_pass}, "select the pass number (1 or 2)", "n" }, @@ -4124,44 +4064,39 @@ static const OptionDef options[] = { { "inter_matrix", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_inter_matrix}, "specify inter matrix coeffs", "matrix" }, { "top", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_top_field_first}, "top=1/bottom=0/auto=-1 field first", "" }, { "dc", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)&intra_dc_precision}, "intra_dc_precision", "precision" }, - { "vtag", HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_codec_tag}, "force video tag/fourcc", "fourcc/tag" }, - { "vlang", HAS_ARG | OPT_STRING | OPT_VIDEO, {(void *)&video_language}, "set the ISO 639 language code (3 letters) of the current video stream" , "code" }, + { "vtag", HAS_ARG | OPT_EXPERT | OPT_VIDEO | OPT_FUNC2, {(void*)opt_video_tag}, "force video tag/fourcc", "fourcc/tag" }, { "qphist", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, { (void *)&qp_hist }, "show QP histogram" }, { "force_fps", OPT_BOOL | OPT_EXPERT | OPT_VIDEO, {(void*)&force_fps}, "force the selected framerate, disable the best supported framerate selection" }, { "streamid", HAS_ARG | OPT_EXPERT, {(void*)opt_streamid}, "set the value of an outfile streamid", "streamIndex:value" }, { "force_key_frames", OPT_STRING | HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void *)&forced_key_frames}, "force key frames at specified timestamps", "timestamps" }, /* audio options */ - { "aframes", OPT_INT | HAS_ARG | OPT_AUDIO, {(void*)&max_frames[AVMEDIA_TYPE_AUDIO]}, "set the number of audio frames to record", "number" }, + { "aframes", HAS_ARG | OPT_AUDIO | OPT_FUNC2, {(void*)opt_audio_frames}, "set the number of audio frames to record", "number" }, { "aq", OPT_FLOAT | HAS_ARG | OPT_AUDIO, {(void*)&audio_qscale}, "set audio quality (codec-specific)", "quality", }, { "ar", HAS_ARG | OPT_AUDIO, {(void*)opt_audio_rate}, "set audio sampling rate (in Hz)", "rate" }, - { "ac", HAS_ARG | OPT_AUDIO, {(void*)opt_audio_channels}, "set number of audio channels", "channels" }, - { "an", OPT_BOOL | OPT_AUDIO, {(void*)&audio_disable}, "disable audio" }, - { "acodec", HAS_ARG | OPT_AUDIO, {(void*)opt_audio_codec}, "force audio codec ('copy' to copy stream)", "codec" }, - { "atag", HAS_ARG | OPT_EXPERT | OPT_AUDIO, {(void*)opt_codec_tag}, "force audio tag/fourcc", "fourcc/tag" }, + { "ac", HAS_ARG | OPT_AUDIO | OPT_INT | OPT_SPEC, {.off = OFFSET(audio_channels)}, "set number of audio channels", "channels" }, + { "an", OPT_BOOL | OPT_AUDIO | OPT_OFFSET, {.off = OFFSET(audio_disable)}, "disable audio" }, + { "acodec", HAS_ARG | OPT_AUDIO | OPT_FUNC2, {(void*)opt_audio_codec}, "force audio codec ('copy' to copy stream)", "codec" }, + { "atag", HAS_ARG | OPT_EXPERT | OPT_AUDIO | OPT_FUNC2, {(void*)opt_audio_tag}, "force audio tag/fourcc", "fourcc/tag" }, { "vol", OPT_INT | HAS_ARG | OPT_AUDIO, {(void*)&audio_volume}, "change audio volume (256=normal)" , "volume" }, // - { "alang", HAS_ARG | OPT_STRING | OPT_AUDIO, {(void *)&audio_language}, "set the ISO 639 language code (3 letters) of the current audio stream" , "code" }, { "sample_fmt", HAS_ARG | OPT_EXPERT | OPT_AUDIO, {(void*)opt_audio_sample_fmt}, "set sample format, 'list' as argument shows all the sample formats supported", "format" }, /* subtitle options */ - { "sn", OPT_BOOL | OPT_SUBTITLE, {(void*)&subtitle_disable}, "disable subtitle" }, - { "scodec", HAS_ARG | OPT_SUBTITLE, {(void*)opt_subtitle_codec}, "force subtitle codec ('copy' to copy stream)", "codec" }, - { "slang", HAS_ARG | OPT_STRING | OPT_SUBTITLE, {(void *)&subtitle_language}, "set the ISO 639 language code (3 letters) of the current subtitle stream" , "code" }, - { "stag", HAS_ARG | OPT_EXPERT | OPT_SUBTITLE, {(void*)opt_codec_tag}, "force subtitle tag/fourcc", "fourcc/tag" }, + { "sn", OPT_BOOL | OPT_SUBTITLE | OPT_OFFSET, {.off = OFFSET(subtitle_disable)}, "disable subtitle" }, + { "scodec", HAS_ARG | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_subtitle_codec}, "force subtitle codec ('copy' to copy stream)", "codec" }, + { "stag", HAS_ARG | OPT_EXPERT | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_subtitle_tag}, "force subtitle tag/fourcc", "fourcc/tag" }, /* grab options */ { "isync", OPT_BOOL | OPT_EXPERT | OPT_GRAB, {(void*)&input_sync}, "sync read on input", "" }, /* muxer options */ - { "muxdelay", OPT_FLOAT | HAS_ARG | OPT_EXPERT, {(void*)&mux_max_delay}, "set the maximum demux-decode delay", "seconds" }, - { "muxpreload", OPT_FLOAT | HAS_ARG | OPT_EXPERT, {(void*)&mux_preload}, "set the initial demux-decode delay", "seconds" }, + { "muxdelay", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET, {.off = OFFSET(mux_max_delay)}, "set the maximum demux-decode delay", "seconds" }, + { "muxpreload", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET, {.off = OFFSET(mux_preload)}, "set the initial demux-decode delay", "seconds" }, - { "absf", HAS_ARG | OPT_AUDIO | OPT_EXPERT, {(void*)opt_bsf}, "", "bitstream_filter" }, - { "vbsf", HAS_ARG | OPT_VIDEO | OPT_EXPERT, {(void*)opt_bsf}, "", "bitstream_filter" }, - { "sbsf", HAS_ARG | OPT_SUBTITLE | OPT_EXPERT, {(void*)opt_bsf}, "", "bitstream_filter" }, + { "bsf", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(bitstream_filters)}, "A comma-separated list of bitstream filters", "bitstream_filters" }, /* data codec support */ - { "dcodec", HAS_ARG | OPT_DATA, {(void*)opt_data_codec}, "force data codec ('copy' to copy stream)", "codec" }, + { "dcodec", HAS_ARG | OPT_DATA | OPT_FUNC2, {(void*)opt_data_codec}, "force data codec ('copy' to copy stream)", "codec" }, { "default", HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" }, { NULL, }, @@ -4169,8 +4104,11 @@ static const OptionDef options[] = { int main(int argc, char **argv) { + OptionsContext o = { 0 }; int64_t ti; + reset_options(&o); + av_log_set_flags(AV_LOG_SKIP_REPEATED); avcodec_register_all(); @@ -4184,12 +4122,10 @@ int main(int argc, char **argv) avio_set_interrupt_cb(decode_interrupt_cb); - init_opts(); - show_banner(); /* parse options */ - parse_options(argc, argv, options, opt_output_file); + parse_options(&o, argc, argv, options, opt_output_file); if(nb_output_files <= 0 && nb_input_files == 0) { show_usage(); @@ -4217,5 +4153,6 @@ int main(int argc, char **argv) printf("bench: utime=%0.3fs maxrss=%ikB\n", ti / 1000000.0, maxrss); } - return exit_program(0); + exit_program(0); + return 0; }