X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffmpeg.c;h=94919814bc2ea7d048534658b236f7fd09e71eef;hb=b3f5c21a9122fd587f251a17bc6d011112dd5267;hp=14aea7474a578b850c195e780a7f36bd2b402114;hpb=16b2691346fa9c3d85af3162deef3db50b1eecc5;p=ffmpeg diff --git a/ffmpeg.c b/ffmpeg.c index 14aea7474a5..94919814bc2 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -36,9 +36,12 @@ #include "libswscale/swscale.h" #include "libavcodec/opt.h" #include "libavcodec/audioconvert.h" +#include "libavcore/audioconvert.h" #include "libavcore/parseutils.h" +#include "libavcore/samplefmt.h" #include "libavutil/colorspace.h" #include "libavutil/fifo.h" +#include "libavutil/intreadwrite.h" #include "libavutil/pixdesc.h" #include "libavutil/avstring.h" #include "libavutil/libm.h" @@ -47,7 +50,6 @@ #if CONFIG_AVFILTER # include "libavfilter/avfilter.h" # include "libavfilter/avfiltergraph.h" -# include "libavfilter/graphparser.h" # include "libavfilter/vsrc_buffer.h" #endif @@ -96,10 +98,16 @@ typedef struct AVStreamMap { * select an input file for an output file */ typedef struct AVMetaDataMap { - int out_file; - int in_file; + 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 } AVMetaDataMap; +typedef struct AVChapterMap { + int in_file; + int out_file; +} AVChapterMap; + static const OptionDef options[]; #define MAX_FILES 100 @@ -124,8 +132,15 @@ static int nb_output_codecs = 0; static AVStreamMap *stream_maps = NULL; static int nb_stream_maps; -static AVMetaDataMap meta_data_maps[MAX_FILES]; +/* first item specifies output metadata, second is input */ +static AVMetaDataMap (*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 AVChapterMap *chapter_maps = NULL; +static int nb_chapter_maps; /* indexed by output file stream index */ static int *streamid_map = NULL; @@ -135,7 +150,7 @@ static int frame_width = 0; static int frame_height = 0; static float frame_aspect_ratio = 0; static enum PixelFormat frame_pix_fmt = PIX_FMT_NONE; -static enum SampleFormat audio_sample_fmt = SAMPLE_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; @@ -225,8 +240,8 @@ 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 int pgmyuv_compatibility_hack=0; static float dts_delta_threshold = 10; static unsigned int sws_flags = SWS_BICUBIC; @@ -272,9 +287,17 @@ typedef struct AVOutputStream { int original_height; int original_width; + /* forced key frames */ + int64_t *forced_kf_pts; + int forced_kf_count; + int forced_kf_index; + /* audio only */ int audio_resample; ReSampleContext *resample; /* for audio resampling */ + int resample_sample_fmt; + int resample_channels; + int resample_sample_rate; int reformat_pair; AVAudioConvert *reformat_ctx; AVFifoBuffer *fifo; /* for compression: one audio fifo per codec */ @@ -324,29 +347,6 @@ static struct termios oldtty; #if CONFIG_AVFILTER -static int get_filtered_video_pic(AVFilterContext *ctx, - AVFilterBufferRef **picref, AVFrame *pic2, - uint64_t *pts) -{ - AVFilterBufferRef *pic; - - if(avfilter_request_frame(ctx->inputs[0])) - return -1; - if(!(pic = ctx->inputs[0]->cur_buf)) - return -1; - *picref = pic; - ctx->inputs[0]->cur_buf = NULL; - - *pts = pic->pts; - - memcpy(pic2->data, pic->data, sizeof(pic->data)); - memcpy(pic2->linesize, pic->linesize, sizeof(pic->linesize)); - pic2->interlaced_frame = pic->video->interlaced; - pic2->top_field_first = pic->video->top_field_first; - - return 1; -} - static int configure_filters(AVInputStream *ist, AVOutputStream *ost) { AVFilterContext *last_filter, *filter; @@ -357,25 +357,18 @@ static int configure_filters(AVInputStream *ist, AVOutputStream *ost) char args[255]; int ret; - graph = av_mallocz(sizeof(AVFilterGraph)); - - if ((ret = avfilter_open(&ist->input_video_filter, avfilter_get_by_name("buffer"), "src")) < 0) - return ret; - if ((ret = avfilter_open(&ist->output_video_filter, &ffsink, "out")) < 0) - return ret; + graph = avfilter_graph_alloc(); snprintf(args, 255, "%d:%d:%d:%d:%d", ist->st->codec->width, - ist->st->codec->height, ist->st->codec->pix_fmt, - ist->st->time_base.num, ist->st->time_base.den); - if ((ret = avfilter_init_filter(ist->input_video_filter, args, NULL)) < 0) + ist->st->codec->height, ist->st->codec->pix_fmt, 1, AV_TIME_BASE); + ret = avfilter_graph_create_filter(&ist->input_video_filter, avfilter_get_by_name("buffer"), + "src", args, NULL, graph); + if (ret < 0) return ret; - if ((ret = avfilter_init_filter(ist->output_video_filter, NULL, &ffsink_ctx)) < 0) + ret = avfilter_graph_create_filter(&ist->output_video_filter, &ffsink, + "out", NULL, &ffsink_ctx, graph); + if (ret < 0) return ret; - - /* add input and output filters to the overall graph */ - avfilter_graph_add_filter(graph, ist->input_video_filter); - avfilter_graph_add_filter(graph, ist->output_video_filter); - last_filter = ist->input_video_filter; if (codec->width != icodec->width || codec->height != icodec->height) { @@ -383,14 +376,12 @@ static int configure_filters(AVInputStream *ist, AVOutputStream *ost) codec->width, codec->height, (int)av_get_int(sws_opts, "sws_flags", NULL)); - if ((ret = avfilter_open(&filter, avfilter_get_by_name("scale"), NULL)) < 0) - return ret; - if ((ret = avfilter_init_filter(filter, args, NULL)) < 0) + if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), + NULL, args, NULL, graph)) < 0) return ret; if ((ret = avfilter_link(last_filter, 0, filter, 0)) < 0) return ret; last_filter = filter; - avfilter_graph_add_filter(graph, last_filter); } snprintf(args, sizeof(args), "flags=0x%X", (int)av_get_int(sws_opts, "sws_flags", NULL)); @@ -401,12 +392,12 @@ static int configure_filters(AVInputStream *ist, AVOutputStream *ost) AVFilterInOut *inputs = av_malloc(sizeof(AVFilterInOut)); outputs->name = av_strdup("in"); - outputs->filter = last_filter; + outputs->filter_ctx = last_filter; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); - inputs->filter = ist->output_video_filter; + inputs->filter_ctx = ist->output_video_filter; inputs->pad_idx = 0; inputs->next = NULL; @@ -551,6 +542,7 @@ static int ffmpeg_exit(int ret) av_free(input_codecs); av_free(output_codecs); av_free(stream_maps); + av_free(meta_data_maps); av_free(video_codec_name); av_free(audio_codec_name); @@ -602,7 +594,7 @@ static void *grow_array(void *array, int elem_size, int *size, int new_size) static void choose_sample_fmt(AVStream *st, AVCodec *codec) { if(codec && codec->sample_fmts){ - const enum SampleFormat *p= codec->sample_fmts; + const enum AVSampleFormat *p= codec->sample_fmts; for(; *p!=-1; p++){ if(*p == st->codec->sample_fmt) break; @@ -649,6 +641,27 @@ static void choose_pixel_fmt(AVStream *st, AVCodec *codec) } } +static AVOutputStream *new_output_stream(AVFormatContext *oc, int file_idx) +{ + int idx = oc->nb_streams - 1; + AVOutputStream *ost; + + output_streams_for_file[file_idx] = + grow_array(output_streams_for_file[file_idx], + sizeof(*output_streams_for_file[file_idx]), + &nb_output_streams_for_file[file_idx], + oc->nb_streams); + ost = output_streams_for_file[file_idx][idx] = + av_mallocz(sizeof(AVOutputStream)); + if (!ost) { + fprintf(stderr, "Could not alloc output stream\n"); + ffmpeg_exit(1); + } + ost->file_index = file_idx; + ost->index = idx; + return ost; +} + static int read_ffserver_streams(AVFormatContext *s, const char *filename) { int i, err; @@ -659,11 +672,13 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) if (err < 0) return err; /* copy stream format */ - s->nb_streams = ic->nb_streams; + s->nb_streams = 0; for(i=0;inb_streams;i++) { AVStream *st; AVCodec *codec; + s->nb_streams++; + // FIXME: a more elegant solution is needed st = av_mallocz(sizeof(AVStream)); memcpy(st, ic->streams[i], sizeof(AVStream)); @@ -695,6 +710,8 @@ static int read_ffserver_streams(AVFormatContext *s, const char *filename) if(st->codec->flags & CODEC_FLAG_BITEXACT) nopts = 1; + + new_output_stream(s, nb_output_files); } if (!nopts) @@ -754,11 +771,11 @@ static void do_audio_out(AVFormatContext *s, int64_t audio_out_size, audio_buf_size; int64_t allocated_for_size= size; - int size_out, frame_bytes, ret; + int size_out, frame_bytes, ret, resample_changed; AVCodecContext *enc= ost->st->codec; AVCodecContext *dec= ist->st->codec; - int osize= av_get_bits_per_sample_format(enc->sample_fmt)/8; - int isize= av_get_bits_per_sample_format(dec->sample_fmt)/8; + int osize= av_get_bits_per_sample_fmt(enc->sample_fmt)/8; + int isize= av_get_bits_per_sample_fmt(dec->sample_fmt)/8; const int coded_bps = av_get_bits_per_sample(enc->codec->id); need_realloc: @@ -788,22 +805,44 @@ need_realloc: if (enc->channels != dec->channels) ost->audio_resample = 1; - if (ost->audio_resample && !ost->resample) { - if (dec->sample_fmt != SAMPLE_FMT_S16) - fprintf(stderr, "Warning, using s16 intermediate sample format for resampling\n"); - ost->resample = av_audio_resample_init(enc->channels, dec->channels, - enc->sample_rate, dec->sample_rate, - enc->sample_fmt, dec->sample_fmt, - 16, 10, 0, 0.8); - if (!ost->resample) { - fprintf(stderr, "Can not resample %d channels @ %d Hz to %d channels @ %d Hz\n", - dec->channels, dec->sample_rate, - enc->channels, enc->sample_rate); - ffmpeg_exit(1); + resample_changed = ost->resample_sample_fmt != dec->sample_fmt || + ost->resample_channels != dec->channels || + ost->resample_sample_rate != dec->sample_rate; + + if ((ost->audio_resample && !ost->resample) || resample_changed) { + if (resample_changed) { + av_log(NULL, AV_LOG_INFO, "Input stream #%d.%d frame changed from rate:%d fmt:%s ch:%d to rate:%d fmt:%s ch:%d\n", + ist->file_index, ist->index, + ost->resample_sample_rate, av_get_sample_fmt_name(ost->resample_sample_fmt), ost->resample_channels, + dec->sample_rate, av_get_sample_fmt_name(dec->sample_fmt), dec->channels); + ost->resample_sample_fmt = dec->sample_fmt; + ost->resample_channels = dec->channels; + ost->resample_sample_rate = dec->sample_rate; + if (ost->resample) + audio_resample_close(ost->resample); + } + if (ost->resample_sample_fmt == enc->sample_fmt && + ost->resample_channels == enc->channels && + ost->resample_sample_rate == enc->sample_rate) { + ost->resample = NULL; + ost->audio_resample = 0; + } else { + if (dec->sample_fmt != AV_SAMPLE_FMT_S16) + fprintf(stderr, "Warning, using s16 intermediate sample format for resampling\n"); + ost->resample = av_audio_resample_init(enc->channels, dec->channels, + enc->sample_rate, dec->sample_rate, + enc->sample_fmt, dec->sample_fmt, + 16, 10, 0, 0.8); + if (!ost->resample) { + fprintf(stderr, "Can not resample %d channels @ %d Hz to %d channels @ %d Hz\n", + dec->channels, dec->sample_rate, + enc->channels, enc->sample_rate); + ffmpeg_exit(1); + } } } -#define MAKE_SFMT_PAIR(a,b) ((a)+SAMPLE_FMT_NB*(b)) +#define MAKE_SFMT_PAIR(a,b) ((a)+AV_SAMPLE_FMT_NB*(b)) if (!ost->audio_resample && dec->sample_fmt!=enc->sample_fmt && MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt)!=ost->reformat_pair) { if (ost->reformat_ctx) @@ -812,8 +851,8 @@ need_realloc: dec->sample_fmt, 1, NULL, 0); if (!ost->reformat_ctx) { fprintf(stderr, "Cannot convert %s sample format to %s sample format\n", - avcodec_get_sample_fmt_name(dec->sample_fmt), - avcodec_get_sample_fmt_name(enc->sample_fmt)); + av_get_sample_fmt_name(dec->sample_fmt), + av_get_sample_fmt_name(enc->sample_fmt)); ffmpeg_exit(1); } ost->reformat_pair=MAKE_SFMT_PAIR(enc->sample_fmt,dec->sample_fmt); @@ -1081,13 +1120,9 @@ static void do_video_out(AVFormatContext *s, { int nb_frames, i, ret; AVFrame *final_picture, *formatted_picture, *resampling_dst, *padding_src; - AVFrame picture_crop_temp, picture_pad_temp; AVCodecContext *enc, *dec; double sync_ipts; - avcodec_get_frame_defaults(&picture_crop_temp); - avcodec_get_frame_defaults(&picture_pad_temp); - enc = ost->st->codec; dec = ist->st->codec; @@ -1107,7 +1142,7 @@ static void do_video_out(AVFormatContext *s, if(vdelta<=-0.6){ nb_frames=0; }else if(vdelta>0.6) - ost->sync_opts= lrintf(sync_ipts); + ost->sync_opts= lrintf(sync_ipts); }else if (vdelta > 1.1) nb_frames = lrintf(vdelta); //fprintf(stderr, "vdelta:%f, ost->sync_opts:%"PRId64", ost->sync_ipts:%f nb_frames:%d\n", vdelta, ost->sync_opts, get_sync_ipts(ost), nb_frames); @@ -1212,6 +1247,11 @@ static void do_video_out(AVFormatContext *s, big_picture.pts= ost->sync_opts; // big_picture.pts= av_rescale(ost->sync_opts, AV_TIME_BASE*(int64_t)enc->time_base.num, enc->time_base.den); //av_log(NULL, AV_LOG_DEBUG, "%"PRId64" -> encoder\n", ost->sync_opts); + if (ost->forced_kf_index < ost->forced_kf_count && + big_picture.pts >= ost->forced_kf_pts[ost->forced_kf_index]) { + big_picture.pict_type = FF_I_TYPE; + ost->forced_kf_index++; + } ret = avcodec_encode_video(enc, bit_buffer, bit_buffer_size, &big_picture); @@ -1430,7 +1470,7 @@ static int output_packet(AVInputStream *ist, int ist_index, #endif AVPacket avpkt; - int bps = av_get_bits_per_sample_format(ist->st->codec->sample_fmt)>>3; + int bps = av_get_bits_per_sample_fmt(ist->st->codec->sample_fmt)>>3; if(ist->next_pts == AV_NOPTS_VALUE) ist->next_pts= ist->pts; @@ -1600,8 +1640,11 @@ static int output_packet(AVInputStream *ist, int ist_index, if (start_time == 0 || ist->pts >= start_time) #if CONFIG_AVFILTER while (frame_available) { + AVRational ist_pts_tb; if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO && ist->output_video_filter) - get_filtered_video_pic(ist->output_video_filter, &ist->picref, &picture, &ist->pts); + get_filtered_video_frame(ist->output_video_filter, &picture, &ist->picref, &ist_pts_tb); + if (ist->picref) + ist->pts = av_rescale_q(ist->picref->pts, ist_pts_tb, AV_TIME_BASE_Q); #endif for(i=0;i 0) { - int osize = av_get_bits_per_sample_format(enc->sample_fmt) >> 3; + int osize = av_get_bits_per_sample_fmt(enc->sample_fmt) >> 3; int fs_tmp = enc->frame_size; av_fifo_generic_read(ost->fifo, audio_buf, fifo_bytes, NULL); @@ -1845,8 +1888,9 @@ 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); - while ((t = av_metadata_get(in_ch->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) - av_metadata_set2(&out_ch->metadata, t->key, t->value, 0); + if (metadata_chapters_autocopy) + while ((t = av_metadata_get(in_ch->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) + av_metadata_set2(&out_ch->metadata, t->key, t->value, 0); os->nb_chapters++; os->chapters = av_realloc(os->chapters, sizeof(AVChapter)*os->nb_chapters); @@ -1857,6 +1901,29 @@ static int copy_chapters(int infile, int outfile) return 0; } +static void parse_forced_key_frames(char *kf, AVOutputStream *ost, + AVCodecContext *avctx) +{ + char *p; + int n = 1, i; + int64_t t; + + for (p = kf; *p; p++) + if (*p == ',') + n++; + ost->forced_kf_count = n; + ost->forced_kf_pts = av_malloc(sizeof(*ost->forced_kf_pts) * n); + if (!ost->forced_kf_pts) { + av_log(NULL, AV_LOG_FATAL, "Could not allocate forced key frames array.\n"); + ffmpeg_exit(1); + } + for (i = 0; i < n; i++) { + p = i ? strchr(p, ',') + 1 : kf; + t = parse_time_or_die("force_key_frames", p, 1); + ost->forced_kf_pts[i] = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base); + } +} + /* * The following code is the main loop of the file converter */ @@ -1923,7 +1990,7 @@ static int transcode(AVFormatContext **output_files, nb_ostreams = 0; for(i=0;inb_streams) { + if (!os->nb_streams && !(os->oformat->flags & AVFMT_NOSTREAMS)) { dump_format(output_files[i], i, output_files[i]->filename, 1); fprintf(stderr, "Output file #%d does not contain any stream\n", i); ret = AVERROR(EINVAL); @@ -2051,9 +2118,10 @@ static int transcode(AVFormatContext **output_files, codec = ost->st->codec; icodec = ist->st->codec; - while ((t = av_metadata_get(ist->st->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) { - av_metadata_set2(&ost->st->metadata, t->key, t->value, AV_METADATA_DONT_OVERWRITE); - } + if (metadata_streams_autocopy) + while ((t = av_metadata_get(ist->st->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) { + av_metadata_set2(&ost->st->metadata, t->key, t->value, AV_METADATA_DONT_OVERWRITE); + } ost->st->disposition = ist->st->disposition; codec->bits_per_raw_sample= icodec->bits_per_raw_sample; @@ -2126,11 +2194,14 @@ static int transcode(AVFormatContext **output_files, ost->fifo= av_fifo_alloc(1024); if(!ost->fifo) goto fail; - ost->reformat_pair = MAKE_SFMT_PAIR(SAMPLE_FMT_NONE,SAMPLE_FMT_NONE); + ost->reformat_pair = MAKE_SFMT_PAIR(AV_SAMPLE_FMT_NONE,AV_SAMPLE_FMT_NONE); ost->audio_resample = codec->sample_rate != icodec->sample_rate || audio_sync_method > 1; icodec->request_channels = codec->channels; ist->decoding_needed = 1; ost->encoding_needed = 1; + ost->resample_sample_fmt = icodec->sample_fmt; + ost->resample_sample_rate = icodec->sample_rate; + ost->resample_channels = icodec->channels; break; case AVMEDIA_TYPE_VIDEO: if (ost->st->codec->pix_fmt == PIX_FMT_NONE) { @@ -2235,6 +2306,7 @@ static int transcode(AVFormatContext **output_files, ost = ost_table[i]; if (ost->encoding_needed) { AVCodec *codec = i < nb_output_codecs ? output_codecs[i] : NULL; + AVCodecContext *dec = ist_table[ost->source_index]->st->codec; if (!codec) codec = avcodec_find_encoder(ost->st->codec->codec_id); if (!codec) { @@ -2243,6 +2315,15 @@ static int transcode(AVFormatContext **output_files, ret = AVERROR(EINVAL); goto dump_format; } + if (dec->subtitle_header) { + ost->st->codec->subtitle_header = av_malloc(dec->subtitle_header_size); + if (!ost->st->codec->subtitle_header) { + ret = AVERROR(ENOMEM); + goto dump_format; + } + memcpy(ost->st->codec->subtitle_header, dec->subtitle_header, dec->subtitle_header_size); + ost->st->codec->subtitle_header_size = dec->subtitle_header_size; + } if (avcodec_open(ost->st->codec, codec) < 0) { snprintf(error, sizeof(error), "Error while opening encoder for output stream #%d.%d - maybe incorrect parameters such as bit_rate, rate, width or height", ost->file_index, ost->index); @@ -2290,43 +2371,96 @@ static int transcode(AVFormatContext **output_files, /* set meta data information from input file if required */ for (i=0;i= (nb_elems)) {\ + snprintf(error, sizeof(error), "Invalid %s index %d while processing metadata maps\n",\ + (desc), (index));\ + ret = AVERROR(EINVAL);\ + goto dump_format;\ + } + + int out_file_index = meta_data_maps[i][0].file; + int in_file_index = meta_data_maps[i][1].file; + if (in_file_index < 0 || out_file_index < 0) + continue; + METADATA_CHECK_INDEX(out_file_index, nb_output_files, "output file") + METADATA_CHECK_INDEX(in_file_index, nb_input_files, "input file") + + files[0] = output_files[out_file_index]; + files[1] = input_files[in_file_index]; + + for (j = 0; j < 2; j++) { + AVMetaDataMap *map = &meta_data_maps[i][j]; + + switch (map->type) { + case 'g': + meta[j] = &files[j]->metadata; + break; + case 's': + METADATA_CHECK_INDEX(map->index, files[j]->nb_streams, "stream") + meta[j] = &files[j]->streams[map->index]->metadata; + break; + case 'c': + METADATA_CHECK_INDEX(map->index, files[j]->nb_chapters, "chapter") + meta[j] = &files[j]->chapters[map->index]->metadata; + break; + case 'p': + METADATA_CHECK_INDEX(map->index, files[j]->nb_programs, "program") + meta[j] = &files[j]->programs[map->index]->metadata; + break; + } + } + + mtag=NULL; + while((mtag=av_metadata_get(*meta[1], "", mtag, AV_METADATA_IGNORE_SUFFIX))) + av_metadata_set2(meta[0], mtag->key, mtag->value, AV_METADATA_DONT_OVERWRITE); + } + + /* copy global metadata by default */ + if (metadata_global_autocopy) { + AVMetadataTag *t = NULL; + + while ((t = av_metadata_get(input_files[0]->metadata, "", t, AV_METADATA_IGNORE_SUFFIX))) + for (i = 0; i < nb_output_files; i++) + av_metadata_set2(&output_files[i]->metadata, t->key, t->value, AV_METADATA_DONT_OVERWRITE); + } + + /* copy chapters according to chapter maps */ + for (i = 0; i < nb_chapter_maps; i++) { + int infile = chapter_maps[i].in_file; + int outfile = chapter_maps[i].out_file; - int out_file_index = meta_data_maps[i].out_file; - int in_file_index = meta_data_maps[i].in_file; - if (out_file_index < 0 || out_file_index >= nb_output_files) { - snprintf(error, sizeof(error), "Invalid output file index %d map_meta_data(%d,%d)", - out_file_index, out_file_index, in_file_index); + if (infile < 0 || outfile < 0) + continue; + if (infile >= nb_input_files) { + snprintf(error, sizeof(error), "Invalid input file index %d in chapter mapping.\n", infile); ret = AVERROR(EINVAL); goto dump_format; } - if (in_file_index < 0 || in_file_index >= nb_input_files) { - snprintf(error, sizeof(error), "Invalid input file index %d map_meta_data(%d,%d)", - in_file_index, out_file_index, in_file_index); + if (outfile >= nb_output_files) { + snprintf(error, sizeof(error), "Invalid output file index %d in chapter mapping.\n",outfile); ret = AVERROR(EINVAL); goto dump_format; } - - out_file = output_files[out_file_index]; - in_file = input_files[in_file_index]; - - - mtag=NULL; - while((mtag=av_metadata_get(in_file->metadata, "", mtag, AV_METADATA_IGNORE_SUFFIX))) - av_metadata_set2(&out_file->metadata, mtag->key, mtag->value, AV_METADATA_DONT_OVERWRITE); + copy_chapters(infile, outfile); } /* copy chapters from the first input file that has them*/ - for (i = 0; i < nb_input_files; i++) { - if (!input_files[i]->nb_chapters) - continue; + if (!nb_chapter_maps) + for (i = 0; i < nb_input_files; i++) { + if (!input_files[i]->nb_chapters) + continue; - for (j = 0; j < nb_output_files; j++) - if ((ret = copy_chapters(i, j)) < 0) - goto dump_format; - } + for (j = 0; j < nb_output_files; j++) + if ((ret = copy_chapters(i, j)) < 0) + goto dump_format; + break; + } /* open files and write file headers */ for(i=0;ififo); /* works even if fifo is not initialized but set to zero */ + av_freep(&ost->st->codec->subtitle_header); av_free(ost->pict_tmp.data[0]); + av_free(ost->forced_kf_pts); if (ost->video_resample) sws_freeContext(ost->img_resample_ctx); if (ost->resample) @@ -2614,14 +2750,6 @@ static int transcode(AVFormatContext **output_files, static void opt_format(const char *arg) { - /* compatibility stuff for pgmyuv */ - if (!strcmp(arg, "pgmyuv")) { - pgmyuv_compatibility_hack=1; -// opt_image_format(arg); - arg = "image2"; - fprintf(stderr, "pgmyuv format is deprecated, use image2\n"); - } - last_asked_format = arg; } @@ -2763,9 +2891,9 @@ static int opt_thread_count(const char *opt, const char *arg) static void opt_audio_sample_fmt(const char *arg) { if (strcmp(arg, "list")) - audio_sample_fmt = avcodec_get_sample_fmt(arg); + audio_sample_fmt = av_get_sample_fmt(arg); else { - list_fmts(avcodec_sample_fmt_string, SAMPLE_FMT_NB); + list_fmts(av_get_sample_fmt_string, AV_SAMPLE_FMT_NB); ffmpeg_exit(0); } } @@ -2808,24 +2936,6 @@ static void opt_audio_codec(const char *arg) opt_codec(&audio_stream_copy, &audio_codec_name, AVMEDIA_TYPE_AUDIO, arg); } -static void opt_audio_tag(const char *arg) -{ - char *tail; - audio_codec_tag= strtol(arg, &tail, 0); - - if(!tail || *tail) - audio_codec_tag= arg[0] + (arg[1]<<8) + (arg[2]<<16) + (arg[3]<<24); -} - -static void opt_video_tag(const char *arg) -{ - char *tail; - video_codec_tag= strtol(arg, &tail, 0); - - if(!tail || *tail) - video_codec_tag= arg[0] + (arg[1]<<8) + (arg[2]<<16) + (arg[3]<<24); -} - static void opt_video_codec(const char *arg) { opt_codec(&video_stream_copy, &video_codec_name, AVMEDIA_TYPE_VIDEO, arg); @@ -2836,13 +2946,22 @@ static void opt_subtitle_codec(const char *arg) opt_codec(&subtitle_stream_copy, &subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, arg); } -static void opt_subtitle_tag(const char *arg) +static int opt_codec_tag(const char *opt, const char *arg) { char *tail; - subtitle_codec_tag= strtol(arg, &tail, 0); + 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; - if(!tail || *tail) - subtitle_codec_tag= arg[0] + (arg[1]<<8) + (arg[2]<<16) + (arg[3]<<24); + *codec_tag = strtol(arg, &tail, 0); + if (!tail || *tail) + *codec_tag = AV_RL32(arg); + + return 0; } static void opt_map(const char *arg) @@ -2870,18 +2989,66 @@ static void opt_map(const char *arg) } } +static void parse_meta_type(const char *arg, char *type, int *index, char **endptr) +{ + *endptr = arg; + if (*arg == ',') { + *type = *(++arg); + switch (*arg) { + case 'g': + break; + case 's': + case 'c': + case 'p': + *index = strtol(++arg, endptr, 0); + break; + default: + fprintf(stderr, "Invalid metadata type %c.\n", *arg); + ffmpeg_exit(1); + } + } else + *type = 'g'; +} + static void opt_map_meta_data(const char *arg) { - AVMetaDataMap *m; + AVMetaDataMap *m, *m1; char *p; - m = &meta_data_maps[nb_meta_data_maps++]; + meta_data_maps = grow_array(meta_data_maps, sizeof(*meta_data_maps), + &nb_meta_data_maps, nb_meta_data_maps + 1); - m->out_file = strtol(arg, &p, 0); + m = &meta_data_maps[nb_meta_data_maps - 1][0]; + m->file = strtol(arg, &p, 0); + parse_meta_type(p, &m->type, &m->index, &p); if (*p) p++; - m->in_file = strtol(p, &p, 0); + m1 = &meta_data_maps[nb_meta_data_maps - 1][1]; + m1->file = strtol(p, &p, 0); + parse_meta_type(p, &m1->type, &m1->index, &p); + + if (m->type == 'g' || m1->type == 'g') + metadata_global_autocopy = 0; + if (m->type == 's' || m1->type == 's') + metadata_streams_autocopy = 0; + if (m->type == 'c' || m1->type == 'c') + metadata_chapters_autocopy = 0; +} + +static void opt_map_chapters(const char *arg) +{ + AVChapterMap *c; + char *p; + + chapter_maps = grow_array(chapter_maps, sizeof(*chapter_maps), &nb_chapter_maps, + nb_chapter_maps + 1); + c = &chapter_maps[nb_chapter_maps - 1]; + c->out_file = strtol(arg, &p, 0); + if (*p) + p++; + + c->in_file = strtol(p, &p, 0); } static void opt_input_ts_scale(const char *arg) @@ -3015,9 +3182,6 @@ static void opt_input_file(const char *filename) avcodec_opts[AVMEDIA_TYPE_SUBTITLE]->strict_std_compliance); ic->flags |= AVFMT_FLAG_NONBLOCK; - if(pgmyuv_compatibility_hack) - ic->video_codec_id= CODEC_ID_PGMYUV; - /* open the input file with generic libav function */ err = av_open_input_file(&ic, filename, file_iformat, 0, ap); if (err < 0) { @@ -3203,27 +3367,6 @@ static void check_audio_video_sub_inputs(int *has_video_ptr, int *has_audio_ptr, *has_subtitle_ptr = has_subtitle; } -static AVOutputStream *new_output_stream(AVFormatContext *oc, int file_idx) -{ - int idx = oc->nb_streams - 1; - AVOutputStream *ost; - - output_streams_for_file[file_idx] = - grow_array(output_streams_for_file[file_idx], - sizeof(*output_streams_for_file[file_idx]), - &nb_output_streams_for_file[file_idx], - oc->nb_streams); - ost = output_streams_for_file[file_idx][idx] = - av_mallocz(sizeof(AVOutputStream)); - if (!ost) { - fprintf(stderr, "Could not alloc output stream\n"); - ffmpeg_exit(1); - } - ost->file_index = file_idx; - ost->index = idx; - return ost; -} - static void new_video_stream(AVFormatContext *oc, int file_idx) { AVStream *st; @@ -3353,6 +3496,9 @@ static void new_video_stream(AVFormatContext *oc, int file_idx) video_enc->flags |= CODEC_FLAG_PASS2; } } + + if (forced_key_frames) + parse_forced_key_frames(forced_key_frames, ost, video_enc); } if (video_language) { av_metadata_set2(&st->metadata, "language", video_language, 0); @@ -3362,6 +3508,7 @@ static void new_video_stream(AVFormatContext *oc, int file_idx) /* reset some key parameters */ video_disable = 0; av_freep(&video_codec_name); + av_freep(&forced_key_frames); video_stream_copy = 0; frame_pix_fmt = PIX_FMT_NONE; } @@ -3427,7 +3574,7 @@ static void new_audio_stream(AVFormatContext *oc, int file_idx) audio_enc->sample_fmt = audio_sample_fmt; audio_enc->sample_rate = audio_sample_rate; audio_enc->channel_layout = channel_layout; - if (avcodec_channel_layout_num_channels(channel_layout) != audio_channels) + if (av_get_channel_layout_nb_channels(channel_layout) != audio_channels) audio_enc->channel_layout = 0; choose_sample_fmt(st, codec); choose_sample_rate(st, codec); @@ -3450,6 +3597,7 @@ static void new_subtitle_stream(AVFormatContext *oc, int file_idx) AVOutputStream *ost; AVCodec *codec=NULL; AVCodecContext *subtitle_enc; + enum CodecID codec_id; st = av_new_stream(oc, oc->nb_streams < nb_streamid_map ? streamid_map[oc->nb_streams] : 0); if (!st) { @@ -3460,9 +3608,14 @@ static void new_subtitle_stream(AVFormatContext *oc, int file_idx) subtitle_enc = st->codec; output_codecs = grow_array(output_codecs, sizeof(*output_codecs), &nb_output_codecs, nb_output_codecs + 1); if(!subtitle_stream_copy){ - subtitle_enc->codec_id = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 1, - avcodec_opts[AVMEDIA_TYPE_SUBTITLE]->strict_std_compliance); - codec= output_codecs[nb_output_codecs-1] = avcodec_find_encoder_by_name(subtitle_codec_name); + if (subtitle_codec_name) { + codec_id = find_codec_or_die(subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 1, + avcodec_opts[AVMEDIA_TYPE_SUBTITLE]->strict_std_compliance); + codec= output_codecs[nb_output_codecs-1] = avcodec_find_encoder_by_name(subtitle_codec_name); + } else { + codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_SUBTITLE); + codec = avcodec_find_encoder(codec_id); + } } avcodec_get_context_defaults3(st->codec, codec); @@ -3474,9 +3627,14 @@ static void new_subtitle_stream(AVFormatContext *oc, int file_idx) if(subtitle_codec_tag) subtitle_enc->codec_tag= subtitle_codec_tag; + if (oc->oformat->flags & AVFMT_GLOBALHEADER) { + subtitle_enc->flags |= CODEC_FLAG_GLOBAL_HEADER; + avcodec_opts[AVMEDIA_TYPE_SUBTITLE]->flags |= CODEC_FLAG_GLOBAL_HEADER; + } if (subtitle_stream_copy) { st->stream_copy = 1; } else { + subtitle_enc->codec_id = codec_id; set_context_opts(avcodec_opts[AVMEDIA_TYPE_SUBTITLE], subtitle_enc, AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_ENCODING_PARAM, codec); } @@ -3664,6 +3822,7 @@ static void opt_output_file(const char *filename) set_context_opts(oc, avformat_opts, AV_OPT_FLAG_ENCODING_PARAM, NULL); nb_streamid_map = 0; + av_freep(&forced_key_frames); } /* same option as mencoder */ @@ -3752,6 +3911,8 @@ static void show_usage(void) static void show_help(void) { + AVCodec *c; + av_log_set_callback(log_callback_help); show_usage(); show_help_options(options, "Main options:\n", @@ -3780,6 +3941,16 @@ static void show_help(void) printf("\n"); av_opt_show2(avcodec_opts[0], NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); printf("\n"); + + /* individual codec options */ + c = NULL; + while ((c = av_codec_next(c))) { + if (c->priv_class) { + av_opt_show2(&c->priv_class, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); + printf("\n"); + } + } + av_opt_show2(avformat_opts, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); printf("\n"); av_opt_show2(sws_opts, NULL, AV_OPT_FLAG_ENCODING_PARAM|AV_OPT_FLAG_DECODING_PARAM, 0); @@ -3972,32 +4143,11 @@ static int opt_preset(const char *opt, const char *arg) { FILE *f=NULL; char filename[1000], tmp[1000], tmp2[1000], line[1000]; - int i; - const char *base[3]= { getenv("FFMPEG_DATADIR"), - getenv("HOME"), - FFMPEG_DATADIR, - }; - - if (*opt != 'f') { - for(i=0; i<3 && !f; i++){ - if(!base[i]) - continue; - snprintf(filename, sizeof(filename), "%s%s/%s.ffpreset", base[i], i != 1 ? "" : "/.ffmpeg", arg); - f= fopen(filename, "r"); - if(!f){ - char *codec_name= *opt == 'v' ? video_codec_name : - *opt == 'a' ? audio_codec_name : - subtitle_codec_name; - snprintf(filename, sizeof(filename), "%s%s/%s-%s.ffpreset", base[i], i != 1 ? "" : "/.ffmpeg", codec_name, arg); - f= fopen(filename, "r"); - } - } - } else { - av_strlcpy(filename, arg, sizeof(filename)); - f= fopen(filename, "r"); - } + char *codec_name = *opt == 'v' ? video_codec_name : + *opt == 'a' ? audio_codec_name : + subtitle_codec_name; - if(!f){ + if (!(f = get_preset_file(filename, sizeof(filename), arg, *opt == 'f', codec_name))) { fprintf(stderr, "File for preset '%s' not found\n", arg); ffmpeg_exit(1); } @@ -4035,7 +4185,8 @@ static const OptionDef options[] = { { "i", HAS_ARG, {(void*)opt_input_file}, "input file name", "filename" }, { "y", OPT_BOOL, {(void*)&file_overwrite}, "overwrite output files" }, { "map", HAS_ARG | OPT_EXPERT, {(void*)opt_map}, "set input stream mapping", "file:stream[:syncfile:syncstream]" }, - { "map_meta_data", HAS_ARG | OPT_EXPERT, {(void*)opt_map_meta_data}, "set meta data information of outfile from infile", "outfile:infile" }, + { "map_meta_data", HAS_ARG | OPT_EXPERT, {(void*)opt_map_meta_data}, "set meta data information of outfile from infile", "outfile[,metadata]:infile[,metadata]" }, + { "map_chapters", HAS_ARG | OPT_EXPERT, {(void*)opt_map_chapters}, "set chapters mapping", "outfile:infile" }, { "t", OPT_FUNC2 | 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", OPT_FUNC2 | HAS_ARG, {(void*)opt_start_time}, "set the start time offset", "time_off" }, @@ -4108,12 +4259,13 @@ 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_video_tag}, "force video tag/fourcc", "fourcc/tag" }, + { "vtag", OPT_FUNC2 | HAS_ARG | OPT_EXPERT | OPT_VIDEO, {(void*)opt_codec_tag}, "force video tag/fourcc", "fourcc/tag" }, { "newvideo", OPT_VIDEO | OPT_FUNC2, {(void*)opt_new_stream}, "add a new video stream to the current output stream" }, { "vlang", HAS_ARG | OPT_STRING | OPT_VIDEO, {(void *)&video_language}, "set the ISO 639 language code (3 letters) of the current video stream" , "code" }, { "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", OPT_FUNC2 | 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 */ { "ab", OPT_FUNC2 | HAS_ARG | OPT_AUDIO, {(void*)opt_bitrate}, "set bitrate (in bits/s)", "bitrate" }, @@ -4123,7 +4275,7 @@ static const OptionDef options[] = { { "ac", HAS_ARG | OPT_FUNC2 | 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_audio_tag}, "force audio tag/fourcc", "fourcc/tag" }, + { "atag", OPT_FUNC2 | HAS_ARG | OPT_EXPERT | OPT_AUDIO, {(void*)opt_codec_tag}, "force audio tag/fourcc", "fourcc/tag" }, { "vol", OPT_INT | HAS_ARG | OPT_AUDIO, {(void*)&audio_volume}, "change audio volume (256=normal)" , "volume" }, // { "newaudio", OPT_AUDIO | OPT_FUNC2, {(void*)opt_new_stream}, "add a new audio stream to the current output stream" }, { "alang", HAS_ARG | OPT_STRING | OPT_AUDIO, {(void *)&audio_language}, "set the ISO 639 language code (3 letters) of the current audio stream" , "code" }, @@ -4134,7 +4286,7 @@ static const OptionDef options[] = { { "scodec", HAS_ARG | OPT_SUBTITLE, {(void*)opt_subtitle_codec}, "force subtitle codec ('copy' to copy stream)", "codec" }, { "newsubtitle", OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_new_stream}, "add a new subtitle stream to the current output stream" }, { "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_subtitle_tag}, "force subtitle tag/fourcc", "fourcc/tag" }, + { "stag", OPT_FUNC2 | HAS_ARG | OPT_EXPERT | OPT_SUBTITLE, {(void*)opt_codec_tag}, "force subtitle tag/fourcc", "fourcc/tag" }, /* grab options */ { "vc", HAS_ARG | OPT_EXPERT | OPT_VIDEO | OPT_GRAB, {(void*)opt_video_channel}, "set video grab channel (DV1394 only)", "channel" },