avtools: reinitialise filter chain when input video stream changes dimensions
[ffmpeg.git] / avconv.c
index b72b26d..18c5f07 100644 (file)
--- a/avconv.c
+++ b/avconv.c
@@ -79,6 +79,7 @@ const int program_birth_year = 2000;
 
 /* select an input stream for an output stream */
 typedef struct StreamMap {
+    int disabled;           /** 1 is this mapping is disabled by a negative map */
     int file_index;
     int stream_index;
     int sync_file_index;
@@ -464,7 +465,6 @@ static int exit_program(int ret)
     av_free(vstats_filename);
 
     av_free(streamid_map);
-    av_free(stream_maps);
     av_free(meta_data_maps);
 
     av_freep(&input_streams);
@@ -674,8 +674,10 @@ static OutputStream *new_output_stream(AVFormatContext *oc, int file_idx, AVCode
     ost->index = idx;
     ost->st    = st;
     ost->enc   = codec;
-    if (codec)
-        ost->opts  = filter_codec_opts(codec_opts, codec->id, 1);
+    if (codec) {
+        st->codec->codec_type = codec->type;
+        ost->opts  = filter_codec_opts(codec_opts, codec->id, oc, st);
+    }
 
     avcodec_get_context_defaults3(st->codec, codec);
 
@@ -1209,7 +1211,20 @@ static void do_video_out(AVFormatContext *s,
         sws_scale(ost->img_resample_ctx, formatted_picture->data, formatted_picture->linesize,
               0, ost->resample_height, final_picture->data, final_picture->linesize);
     }
+#else
+    if (resample_changed) {
+        avfilter_graph_free(&ost->graph);
+        if (configure_video_filters(ist, ost)) {
+            fprintf(stderr, "Error reinitialising filters!\n");
+            exit_program(1);
+        }
+    }
 #endif
+    if (resample_changed) {
+        ost->resample_width   = dec->width;
+        ost->resample_height  = dec->height;
+        ost->resample_pix_fmt = dec->pix_fmt;
+    }
 
     /* duplicates frame if needed */
     for(i=0;i<nb_frames;i++) {
@@ -1944,8 +1959,7 @@ static void parse_forced_key_frames(char *kf, OutputStream *ost,
 static int transcode(AVFormatContext **output_files,
                      int nb_output_files,
                      InputFile *input_files,
-                     int nb_input_files,
-                     StreamMap *stream_maps, int nb_stream_maps)
+                     int nb_input_files)
 {
     int ret = 0, i, j, k, n, nb_ostreams = 0;
     AVFormatContext *is, *os;
@@ -1973,32 +1987,6 @@ static int transcode(AVFormatContext **output_files,
         }
         nb_ostreams += os->nb_streams;
     }
-    if (nb_stream_maps > 0 && nb_stream_maps != nb_ostreams) {
-        fprintf(stderr, "Number of stream maps must match number of output streams\n");
-        ret = AVERROR(EINVAL);
-        goto fail;
-    }
-
-    /* Sanity check the mapping args -- do the input files & streams exist? */
-    for(i=0;i<nb_stream_maps;i++) {
-        int fi = stream_maps[i].file_index;
-        int si = stream_maps[i].stream_index;
-
-        if (fi < 0 || fi > nb_input_files - 1 ||
-            si < 0 || si > input_files[fi].ctx->nb_streams - 1) {
-            fprintf(stderr,"Could not find input stream #%d.%d\n", fi, si);
-            ret = AVERROR(EINVAL);
-            goto fail;
-        }
-        fi = stream_maps[i].sync_file_index;
-        si = stream_maps[i].sync_stream_index;
-        if (fi < 0 || fi > nb_input_files - 1 ||
-            si < 0 || si > input_files[fi].ctx->nb_streams - 1) {
-            fprintf(stderr,"Could not find sync stream #%d.%d\n", fi, si);
-            ret = AVERROR(EINVAL);
-            goto fail;
-        }
-    }
 
     ost_table = av_mallocz(sizeof(OutputStream *) * nb_ostreams);
     if (!ost_table)
@@ -2006,80 +1994,8 @@ static int transcode(AVFormatContext **output_files,
     n = 0;
     for(k=0;k<nb_output_files;k++) {
         os = output_files[k];
-        for(i=0;i<os->nb_streams;i++,n++) {
-            int found;
-            ost = ost_table[n] = output_streams_for_file[k][i];
-            if (nb_stream_maps > 0) {
-                ost->source_index = input_files[stream_maps[n].file_index].ist_index +
-                    stream_maps[n].stream_index;
-
-                /* Sanity check that the stream types match */
-                if (input_streams[ost->source_index].st->codec->codec_type != ost->st->codec->codec_type) {
-                    int i= ost->file_index;
-                    av_dump_format(output_files[i], i, output_files[i]->filename, 1);
-                    fprintf(stderr, "Codec type mismatch for mapping #%d.%d -> #%d.%d\n",
-                        stream_maps[n].file_index, stream_maps[n].stream_index,
-                        ost->file_index, ost->index);
-                    exit_program(1);
-                }
-
-            } else {
-                int best_nb_frames=-1;
-                /* get corresponding input stream index : we select the first one with the right type */
-                found = 0;
-                for (j = 0; j < nb_input_streams; j++) {
-                    int skip=0;
-                    ist = &input_streams[j];
-                    if(opt_programid){
-                        int pi,si;
-                        AVFormatContext *f = input_files[ist->file_index].ctx;
-                        skip=1;
-                        for(pi=0; pi<f->nb_programs; pi++){
-                            AVProgram *p= f->programs[pi];
-                            if(p->id == opt_programid)
-                                for(si=0; si<p->nb_stream_indexes; si++){
-                                    if(f->streams[ p->stream_index[si] ] == ist->st)
-                                        skip=0;
-                                }
-                        }
-                    }
-                    if (ist->discard && ist->st->discard != AVDISCARD_ALL && !skip &&
-                        ist->st->codec->codec_type == ost->st->codec->codec_type) {
-                        if(best_nb_frames < ist->st->codec_info_nb_frames){
-                            best_nb_frames= ist->st->codec_info_nb_frames;
-                            ost->source_index = j;
-                            found = 1;
-                        }
-                    }
-                }
-
-                if (!found) {
-                    if(! opt_programid) {
-                        /* try again and reuse existing stream */
-                        for (j = 0; j < nb_input_streams; j++) {
-                            ist = &input_streams[j];
-                            if (   ist->st->codec->codec_type == ost->st->codec->codec_type
-                                && ist->st->discard != AVDISCARD_ALL) {
-                                ost->source_index = j;
-                                found = 1;
-                            }
-                        }
-                    }
-                    if (!found) {
-                        int i= ost->file_index;
-                        av_dump_format(output_files[i], i, output_files[i]->filename, 1);
-                        fprintf(stderr, "Could not find input stream matching output stream #%d.%d\n",
-                                ost->file_index, ost->index);
-                        exit_program(1);
-                    }
-                }
-            }
-            ist = &input_streams[ost->source_index];
-            ist->discard = 0;
-            ost->sync_ist = (nb_stream_maps > 0) ?
-                &input_streams[input_files[stream_maps[n].sync_file_index].ist_index +
-                         stream_maps[n].sync_stream_index] : ist;
-        }
+        for (i = 0; i < os->nb_streams; i++, n++)
+            ost_table[n] = output_streams_for_file[k][i];
     }
 
     /* for each output stream, we compute the right encoding parameters */
@@ -2091,10 +2007,6 @@ static int transcode(AVFormatContext **output_files,
         codec = ost->st->codec;
         icodec = ist->st->codec;
 
-        if (metadata_streams_autocopy)
-            av_dict_copy(&ost->st->metadata, ist->st->metadata,
-                         AV_DICT_DONT_OVERWRITE);
-
         ost->st->disposition = ist->st->disposition;
         codec->bits_per_raw_sample= icodec->bits_per_raw_sample;
         codec->chroma_sample_location = icodec->chroma_sample_location;
@@ -2399,63 +2311,6 @@ static int transcode(AVFormatContext **output_files,
         ist->is_start = 1;
     }
 
-    /* set meta data information from input file if required */
-    for (i=0;i<nb_meta_data_maps;i++) {
-        AVFormatContext *files[2];
-        AVDictionary    **meta[2];
-        int j;
-
-#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
-        if ((index) < 0 || (index) >= (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].ctx;
-
-        for (j = 0; j < 2; j++) {
-            MetadataMap *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;
-            }
-        }
-
-        av_dict_copy(meta[0], *meta[1], AV_DICT_DONT_OVERWRITE);
-    }
-
-    /* copy global metadata by default */
-    if (metadata_global_autocopy) {
-
-        for (i = 0; i < nb_output_files; i++)
-            av_dict_copy(&output_files[i]->metadata, input_files[0].ctx->metadata,
-                         AV_DICT_DONT_OVERWRITE);
-    }
-
     /* open files and write file headers */
     for(i=0;i<nb_output_files;i++) {
         os = output_files[i];
@@ -2939,34 +2794,88 @@ static int opt_codec_tag(const char *opt, const char *arg)
 
 static int opt_map(const char *opt, const char *arg)
 {
-    StreamMap *m;
-    char *p;
+    StreamMap *m = NULL;
+    int i, negative = 0, file_idx;
+    int sync_file_idx = -1, sync_stream_idx;
+    char *p, *sync;
+    char *map;
+
+    if (*arg == '-') {
+        negative = 1;
+        arg++;
+    }
+    map = av_strdup(arg);
+
+    /* parse sync stream first, just pick first matching stream */
+    if (sync = strchr(map, ',')) {
+        *sync = 0;
+        sync_file_idx = strtol(sync + 1, &sync, 0);
+        if (sync_file_idx >= nb_input_files || sync_file_idx < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Invalid sync file index: %d.\n", sync_file_idx);
+            exit_program(1);
+        }
+        if (*sync)
+            sync++;
+        for (i = 0; i < input_files[sync_file_idx].ctx->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) {
+            av_log(NULL, AV_LOG_ERROR, "Sync stream specification in map %s does not "
+                                       "match any streams.\n", arg);
+            exit_program(1);
+        }
+    }
 
-    stream_maps = grow_array(stream_maps, sizeof(*stream_maps), &nb_stream_maps, nb_stream_maps + 1);
-    m = &stream_maps[nb_stream_maps-1];
 
-    m->file_index = strtol(arg, &p, 0);
-    if (*p)
-        p++;
+    file_idx = strtol(map, &p, 0);
+    if (file_idx >= nb_input_files || file_idx < 0) {
+        av_log(NULL, AV_LOG_ERROR, "Invalid input file index: %d.\n", file_idx);
+        exit_program(1);
+    }
+    if (negative)
+        /* disable some already defined maps */
+        for (i = 0; i < nb_stream_maps; i++) {
+            m = &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++) {
+            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];
 
-    m->stream_index = strtol(p, &p, 0);
-    if (*p) {
-        p++;
-        m->sync_file_index = strtol(p, &p, 0);
-        if (*p)
-            p++;
-        m->sync_stream_index = strtol(p, &p, 0);
-    } else {
-        m->sync_file_index = m->file_index;
-        m->sync_stream_index = m->stream_index;
+            m->file_index   = file_idx;
+            m->stream_index = i;
+
+            if (sync_file_idx >= 0) {
+                m->sync_file_index   = sync_file_idx;
+                m->sync_stream_index = sync_stream_idx;
+            } else {
+                m->sync_file_index   = file_idx;
+                m->sync_stream_index = i;
+            }
+        }
+
+    if (!m) {
+        av_log(NULL, AV_LOG_ERROR, "Stream map '%s' matches no streams.\n", arg);
+        exit_program(1);
     }
+
+    av_freep(&map);
     return 0;
 }
 
-static void parse_meta_type(char *arg, char *type, int *index, char **endptr)
+static void parse_meta_type(char *arg, char *type, int *index)
 {
-    *endptr = arg;
-    if (*arg == ',') {
+    if (*arg == ':') {
         *type = *(++arg);
         switch (*arg) {
         case 'g':
@@ -2974,7 +2883,8 @@ static void parse_meta_type(char *arg, char *type, int *index, char **endptr)
         case 's':
         case 'c':
         case 'p':
-            *index = strtol(++arg, endptr, 0);
+            if (*(++arg) == ':')
+                *index = strtol(++arg, NULL, 0);
             break;
         default:
             fprintf(stderr, "Invalid metadata type %c.\n", *arg);
@@ -2992,15 +2902,15 @@ static int opt_map_metadata(const char *opt, const char *arg)
     meta_data_maps = grow_array(meta_data_maps, sizeof(*meta_data_maps),
                                 &nb_meta_data_maps, nb_meta_data_maps + 1);
 
-    m = &meta_data_maps[nb_meta_data_maps - 1][0];
+    m = &meta_data_maps[nb_meta_data_maps - 1][1];
     m->file = strtol(arg, &p, 0);
-    parse_meta_type(p, &m->type, &m->index, &p);
-    if (*p)
-        p++;
+    parse_meta_type(p, &m->type, &m->index);
 
-    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);
+    m1 = &meta_data_maps[nb_meta_data_maps - 1][0];
+    if (p = strchr(opt, ':'))
+        parse_meta_type(p, &m1->type, &m1->index);
+    else
+        m1->type = 'g';
 
     if (m->type == 'g' || m1->type == 'g')
         metadata_global_autocopy = 0;
@@ -3198,7 +3108,7 @@ static int opt_input_file(const char *opt, const char *filename)
         ist->st = st;
         ist->file_index = nb_input_files;
         ist->discard = 1;
-        ist->opts = filter_codec_opts(codec_opts, ist->st->codec->codec_id, 0);
+        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];
@@ -3280,50 +3190,7 @@ static int opt_input_file(const char *opt, const char *filename)
     return 0;
 }
 
-static void check_inputs(int *has_video_ptr,
-                         int *has_audio_ptr,
-                         int *has_subtitle_ptr,
-                         int *has_data_ptr)
-{
-    int has_video, has_audio, has_subtitle, has_data, i, j;
-    AVFormatContext *ic;
-
-    has_video = 0;
-    has_audio = 0;
-    has_subtitle = 0;
-    has_data = 0;
-
-    for(j=0;j<nb_input_files;j++) {
-        ic = input_files[j].ctx;
-        for(i=0;i<ic->nb_streams;i++) {
-            AVCodecContext *enc = ic->streams[i]->codec;
-            switch(enc->codec_type) {
-            case AVMEDIA_TYPE_AUDIO:
-                has_audio = 1;
-                break;
-            case AVMEDIA_TYPE_VIDEO:
-                has_video = 1;
-                break;
-            case AVMEDIA_TYPE_SUBTITLE:
-                has_subtitle = 1;
-                break;
-            case AVMEDIA_TYPE_DATA:
-            case AVMEDIA_TYPE_ATTACHMENT:
-            case AVMEDIA_TYPE_UNKNOWN:
-                has_data = 1;
-                break;
-            default:
-                abort();
-            }
-        }
-    }
-    *has_video_ptr = has_video;
-    *has_audio_ptr = has_audio;
-    *has_subtitle_ptr = has_subtitle;
-    *has_data_ptr = has_data;
-}
-
-static void new_video_stream(AVFormatContext *oc, int file_idx)
+static OutputStream *new_video_stream(AVFormatContext *oc, int file_idx)
 {
     AVStream *st;
     OutputStream *ost;
@@ -3452,9 +3319,10 @@ static void new_video_stream(AVFormatContext *oc, int file_idx)
     av_freep(&forced_key_frames);
     video_stream_copy = 0;
     frame_pix_fmt = PIX_FMT_NONE;
+    return ost;
 }
 
-static void new_audio_stream(AVFormatContext *oc, int file_idx)
+static OutputStream *new_audio_stream(AVFormatContext *oc, int file_idx)
 {
     AVStream *st;
     OutputStream *ost;
@@ -3513,9 +3381,11 @@ static void new_audio_stream(AVFormatContext *oc, int file_idx)
     audio_disable = 0;
     av_freep(&audio_codec_name);
     audio_stream_copy = 0;
+
+    return ost;
 }
 
-static void new_data_stream(AVFormatContext *oc, int file_idx)
+static OutputStream *new_data_stream(AVFormatContext *oc, int file_idx)
 {
     AVStream *st;
     OutputStream *ost;
@@ -3544,9 +3414,10 @@ static void new_data_stream(AVFormatContext *oc, int file_idx)
     data_disable = 0;
     av_freep(&data_codec_name);
     data_stream_copy = 0;
+    return ost;
 }
 
-static void new_subtitle_stream(AVFormatContext *oc, int file_idx)
+static OutputStream *new_subtitle_stream(AVFormatContext *oc, int file_idx)
 {
     AVStream *st;
     OutputStream *ost;
@@ -3592,24 +3463,7 @@ static void new_subtitle_stream(AVFormatContext *oc, int file_idx)
     subtitle_disable = 0;
     av_freep(&subtitle_codec_name);
     subtitle_stream_copy = 0;
-}
-
-static int opt_new_stream(const char *opt, const char *arg)
-{
-    AVFormatContext *oc;
-    int file_idx = nb_output_files - 1;
-    if (nb_output_files <= 0) {
-        fprintf(stderr, "At least one output file must be specified\n");
-        exit_program(1);
-    }
-    oc = output_files[file_idx];
-
-    if      (!strcmp(opt, "newvideo"   )) new_video_stream   (oc, file_idx);
-    else if (!strcmp(opt, "newaudio"   )) new_audio_stream   (oc, file_idx);
-    else if (!strcmp(opt, "newsubtitle")) new_subtitle_stream(oc, file_idx);
-    else if (!strcmp(opt, "newdata"    )) new_data_stream    (oc, file_idx);
-    else av_assert0(0);
-    return 0;
+    return ost;
 }
 
 /* arg format is "output-stream-index:streamid-value". */
@@ -3637,9 +3491,10 @@ static int opt_streamid(const char *opt, const char *arg)
 static void opt_output_file(const char *filename)
 {
     AVFormatContext *oc;
-    int i, err, use_video, use_audio, use_subtitle, use_data;
-    int input_has_video, input_has_audio, input_has_subtitle, input_has_data;
+    int i, err;
     AVOutputFormat *file_oformat;
+    OutputStream *ost;
+    InputStream  *ist;
 
     if (!strcmp(filename, "-"))
         filename = "pipe:";
@@ -3678,42 +3533,82 @@ static void opt_output_file(const char *filename)
             print_error(filename, err);
             exit_program(1);
         }
+    } else if (!nb_stream_maps) {
+        /* pick the "best" stream of each type */
+#define NEW_STREAM(type, index)\
+        if (index >= 0) {\
+            ost = new_ ## type ## _stream(oc, nb_output_files);\
+            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) {
+            int area = 0, idx = -1;
+            for (i = 0; i < nb_input_streams; i++) {
+                ist = &input_streams[i];
+                if (ist->st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+                    ist->st->codec->width * ist->st->codec->height > area) {
+                    area = ist->st->codec->width * ist->st->codec->height;
+                    idx = i;
+                }
+            }
+            NEW_STREAM(video, idx);
+        }
+
+        /* audio: most channels */
+        if (!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];
+                if (ist->st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
+                    ist->st->codec->channels > channels) {
+                    channels = ist->st->codec->channels;
+                    idx = i;
+                }
+            }
+            NEW_STREAM(audio, idx);
+        }
+
+        /* subtitles: pick first */
+        if (!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);
+                    break;
+                }
+        }
+        /* do something with data? */
     } else {
-        use_video = file_oformat->video_codec != CODEC_ID_NONE || video_stream_copy || video_codec_name;
-        use_audio = file_oformat->audio_codec != CODEC_ID_NONE || audio_stream_copy || audio_codec_name;
-        use_subtitle = file_oformat->subtitle_codec != CODEC_ID_NONE || subtitle_stream_copy || subtitle_codec_name;
-        use_data = data_stream_copy ||  data_codec_name; /* XXX once generic data codec will be available add a ->data_codec reference and use it here */
-
-        /* disable if no corresponding type found */
-        check_inputs(&input_has_video,
-                     &input_has_audio,
-                     &input_has_subtitle,
-                     &input_has_data);
-
-        if (!input_has_video)
-            use_video = 0;
-        if (!input_has_audio)
-            use_audio = 0;
-        if (!input_has_subtitle)
-            use_subtitle = 0;
-        if (!input_has_data)
-            use_data = 0;
-
-        /* manual disable */
-        if (audio_disable)    use_audio    = 0;
-        if (video_disable)    use_video    = 0;
-        if (subtitle_disable) use_subtitle = 0;
-        if (data_disable)     use_data     = 0;
-
-        if (use_video)    new_video_stream(oc, nb_output_files);
-        if (use_audio)    new_audio_stream(oc, nb_output_files);
-        if (use_subtitle) new_subtitle_stream(oc, nb_output_files);
-        if (use_data)     new_data_stream(oc, nb_output_files);
-
-        av_dict_copy(&oc->metadata, metadata, 0);
-        av_dict_free(&metadata);
+        for (i = 0; i < nb_stream_maps; i++) {
+            StreamMap *map = &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, nb_output_files);    break;
+            case AVMEDIA_TYPE_AUDIO:    ost = new_audio_stream(oc, nb_output_files);    break;
+            case AVMEDIA_TYPE_SUBTITLE: ost = new_subtitle_stream(oc, nb_output_files); break;
+            case AVMEDIA_TYPE_DATA:     ost = new_data_stream(oc, nb_output_files);     break;
+            default:
+                av_log(NULL, AV_LOG_ERROR, "Cannot map stream #%d.%d - unsupported type.\n",
+                       map->file_index, map->stream_index);
+                exit_program(1);
+            }
+
+            ost->source_index = input_files[map->file_index].ist_index + map->stream_index;
+            ost->sync_ist = &input_streams[input_files[map->sync_file_index].ist_index +
+                                           map->sync_stream_index];
+            ist->discard = 0;
+        }
     }
 
+    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;
 
@@ -3777,6 +3672,62 @@ static void opt_output_file(const char *filename)
     if (chapters_input_file >= 0)
         copy_chapters(chapters_input_file, nb_output_files - 1);
 
+    /* copy metadata */
+    for (i = 0; i < nb_meta_data_maps; i++) {
+        AVFormatContext *files[2];
+        AVDictionary    **meta[2];
+        int j;
+
+#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
+        if ((index) < 0 || (index) >= (nb_elems)) {\
+            av_log(NULL, AV_LOG_ERROR, "Invalid %s index %d while processing metadata maps\n",\
+                     (desc), (index));\
+            exit_program(1);\
+        }
+
+        int in_file_index = meta_data_maps[i][1].file;
+        if (in_file_index < 0)
+            continue;
+        METADATA_CHECK_INDEX(in_file_index, nb_input_files, "input file")
+
+        files[0] = oc;
+        files[1] = input_files[in_file_index].ctx;
+
+        for (j = 0; j < 2; j++) {
+            MetadataMap *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;
+            }
+        }
+
+        av_dict_copy(meta[0], *meta[1], AV_DICT_DONT_OVERWRITE);
+    }
+
+    /* copy global metadata by default */
+    if (metadata_global_autocopy)
+        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);
+        }
+
     frame_rate    = (AVRational){0, 0};
     frame_width   = 0;
     frame_height  = 0;
@@ -3785,6 +3736,14 @@ static void opt_output_file(const char *filename)
     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_freep(&forced_key_frames);
     uninit_opts();
     init_opts();
@@ -4238,7 +4197,6 @@ static const OptionDef options[] = {
     { "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" },
-    { "newvideo", OPT_VIDEO, {(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" },
@@ -4254,14 +4212,12 @@ static const OptionDef options[] = {
     { "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" },
     { "vol", OPT_INT | HAS_ARG | OPT_AUDIO, {(void*)&audio_volume}, "change audio volume (256=normal)" , "volume" }, //
-    { "newaudio", OPT_AUDIO, {(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" },
     { "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" },
-    { "newsubtitle", OPT_SUBTITLE, {(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_codec_tag}, "force subtitle tag/fourcc", "fourcc/tag" },
 
@@ -4329,8 +4285,7 @@ int main(int argc, char **argv)
     }
 
     ti = getutime();
-    if (transcode(output_files, nb_output_files, input_files, nb_input_files,
-                  stream_maps, nb_stream_maps) < 0)
+    if (transcode(output_files, nb_output_files, input_files, nb_input_files) < 0)
         exit_program(1);
     ti = getutime() - ti;
     if (do_benchmark) {