On Thu, Dec 08, 2011 at 03:24:57PM +0100, Alexandra Khirnova wrote:
> ---
>  avconv.c        |  207 +++++++++++++++++++++++++++++++-----------------------
>  doc/avconv.texi |   38 ++++++++--
>  2 files changed, 149 insertions(+), 96 deletions(-)
> 
> diff --git a/avconv.c b/avconv.c
> index 54aa89b..95bf244 100644
> --- a/avconv.c
> +++ b/avconv.c
> @@ -332,6 +332,8 @@ typedef struct OptionsContext {
>      int        nb_inter_matrices;
>      SpecifierOpt *top_field_first;
>      int        nb_top_field_first;
> +    SpecifierOpt *metadata_map;
> +    int        nb_metadata_map;
>      SpecifierOpt *presets;
>      int        nb_presets;
>      SpecifierOpt *copy_initial_nonkeyframes;

unrelated: the structure needs vertical realignment

> @@ -2752,7 +2754,13 @@ static int opt_attach(OptionsContext *o, const char 
> *opt, const char *arg)
>      return 0;
>  }
>  
> -static void parse_meta_type(char *arg, char *type, int *index)
> +/**
> + * Parse a metadata specifier in arg.
> + * @param type metadata type is written here -- 
> g(lobal)/s(tream)/c(hapter)/p(rogram)
> + * @param index for type c/p, chapter/program index is written here
> + * @param stream_spec for type s, the stream specifier is written here
> + */

Doxygen complains when not all arguments are documented, and so does Diego.

> +static void parse_meta_type(char *arg, char *type, int *index, const char 
> **stream_spec)
>  {
>      if (*arg) {
>          *type = *arg;
> @@ -2760,6 +2768,12 @@ static void parse_meta_type(char *arg, char *type, int 
> *index)
>          case 'g':
>              break;
>          case 's':
> +            if (*(++arg) && *arg != ':') {
> +                av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier 
> %s.\n", arg);
> +                exit_program(1);
> +            }
> +            *stream_spec = *arg == ':' ? arg + 1 : "";
> +            break;
>          case 'c':
>          case 'p':
>              if (*(++arg) == ':')
> @@ -2773,31 +2787,76 @@ static void parse_meta_type(char *arg, char *type, 
> int *index)
>          *type = 'g';
>  }
>  
> -static int opt_map_metadata(OptionsContext *o, const char *opt, const char 
> *arg)
> +static int copy_metadata(char *outspec, char *inspec, AVFormatContext *oc, 
> AVFormatContext *ic, OptionsContext *o)
>  {
> -    MetadataMap *m, *m1;
> -    char *p;
> -
> -    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 = &o->meta_data_maps[o->nb_meta_data_maps - 1][1];
> -    m->file = strtol(arg, &p, 0);
> -    parse_meta_type(*p ? p + 1 : p, &m->type, &m->index);
> +    AVDictionary **meta_in = NULL;
> +    AVDictionary **meta_out;
> +    int i, ret = 0;
> +    char type_in, type_out;
> +    const char *istream_spec = NULL, *ostream_spec = NULL;
> +    int idx_in = 0, idx_out = 0;
>  
> -    m1 = &o->meta_data_maps[o->nb_meta_data_maps - 1][0];
> -    if (p = strchr(opt, ':'))
> -        parse_meta_type(p + 1, &m1->type, &m1->index);
> -    else
> -        m1->type = 'g';
> +    parse_meta_type(inspec,  &type_in,  &idx_in,  &istream_spec);
> +    parse_meta_type(outspec, &type_out, &idx_out, &ostream_spec);
>  
> -    if (m->type == 'g' || m1->type == 'g')
> +    if (type_in == 'g' || type_out == 'g')
>          o->metadata_global_manual = 1;
> -    if (m->type == 's' || m1->type == 's')
> +    if (type_in == 's' || type_out == 's')
>          o->metadata_streams_manual = 1;
> -    if (m->type == 'c' || m1->type == 'c')
> +    if (type_in == 'c' || type_out == 'c')
>          o->metadata_chapters_manual = 1;
>  
> +#define METADATA_CHECK_INDEX(index, nb_elems, desc)\
> +    if ((index) < 0 || (index) >= (nb_elems)) {\
> +        av_log(NULL, AV_LOG_FATAL, "Invalid %s index %d while processing 
> metadata maps.\n",\
> +                (desc), (index));\
> +        exit_program(1);\
> +    }
> +
> +#define SET_DICT(type, meta, context, index)\
> +        switch (type) {\
> +        case 'g':\
> +            meta = &context->metadata;\
> +            break;\
> +        case 'c':\
> +            METADATA_CHECK_INDEX(index, context->nb_chapters, "chapter")\
> +            meta = &context->chapters[index]->metadata;\
> +            break;\
> +        case 'p':\
> +            METADATA_CHECK_INDEX(index, context->nb_programs, "program")\
> +            meta = &context->programs[index]->metadata;\
> +            break;\
> +        }\
> +
> +    SET_DICT(type_in, meta_in, ic, idx_in);
> +    SET_DICT(type_out, meta_out, oc, idx_out);
> +
> +    /* for input streams choose first matching stream */
> +    if (type_in == 's') {
> +        for (i = 0; i < ic->nb_streams; i++) {
> +            if ((ret = check_stream_specifier(ic, ic->streams[i], 
> istream_spec)) > 0) {
> +                meta_in = &ic->streams[i]->metadata;
> +                break;
> +            } else if (ret < 0)
> +                exit_program(1);
> +        }
> +        if (!meta_in) {
> +            av_log(NULL, AV_LOG_FATAL, "Stream specifier %s does not match  
> any streams.\n", istream_spec);
> +            exit_program(1);
> +        }
> +    }
> +
> +    if (type_out == 's') {
> +        for (i = 0; i < oc->nb_streams; i++) {
> +            if ((ret = check_stream_specifier(oc, oc->streams[i], 
> ostream_spec)) > 0) {
> +                meta_out = &oc->streams[i]->metadata;
> +                av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
> +            } else if (ret < 0)
> +                exit_program(1);
> +        }
> +    } else
> +        av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE);
> +
>      return 0;
>  }
>  
> @@ -3745,6 +3804,20 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>      oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE);
>      oc->flags |= AVFMT_FLAG_NONBLOCK;
>  
> +    /* copy metadata */
> +    for (i = 0; i < o->nb_metadata_map; i++) {
> +        char *p;
> +        int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);
> +
> +        if (in_file_index < 0)
> +            continue;
> +        if (in_file_index >= nb_input_files) {
> +            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while 
> processing metadata maps\n", in_file_index);
> +            exit_program(1);
> +        }
> +        copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc, 
> input_files[in_file_index].ctx, o);
> +    }
> +
>      /* copy chapters */
>      if (o->chapters_input_file >= nb_input_files) {
>          if (o->chapters_input_file == INT_MAX) {
> @@ -3765,52 +3838,6 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>          copy_chapters(&input_files[o->chapters_input_file], 
> &output_files[nb_output_files - 1],
>                        !o->metadata_chapters_manual);
>  
> -    /* copy metadata */
> -    for (i = 0; i < o->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_FATAL, "Invalid %s index %d while processing 
> metadata maps\n",\
> -                     (desc), (index));\
> -            exit_program(1);\
> -        }
> -
> -        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")
> -
> -        files[0] = oc;
> -        files[1] = input_files[in_file_index].ctx;
> -
> -        for (j = 0; j < 2; j++) {
> -            MetadataMap *map = &o->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 (!o->metadata_global_manual && nb_input_files)
>          av_dict_copy(&oc->metadata, input_files[0].ctx->metadata,
> @@ -3828,7 +3855,8 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>      for (i = 0; i < o->nb_metadata; i++) {
>          AVDictionary **m;
>          char type, *val;
> -        int index = 0;
> +        const char *stream_spec;
> +        int index = 0, j, ret;
>  
>          val = strchr(o->metadata[i].u.str, '=');
>          if (!val) {
> @@ -3838,31 +3866,34 @@ static void opt_output_file(void *optctx, const char 
> *filename)
>          }
>          *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_FATAL, "Invalid stream index %d in 
> metadata specifier.\n", index);
> -                exit_program(1);
> +        parse_meta_type(o->metadata[i].specifier, &type, &index, 
> &stream_spec);
> +        if (type == 's') {
> +            for (j = 0; j < oc->nb_streams; j++) {
> +                if ((ret = check_stream_specifier(oc, oc->streams[j], 
> stream_spec)) > 0) {
> +                    av_dict_set(&oc->streams[j]->metadata, 
> o->metadata[i].u.str, *val ? val : NULL, 0);
> +                } else if (ret < 0)
> +                    exit_program(1);
>              }
> -            m = &oc->streams[index]->metadata;
> -            break;
> -        case 'c':
> -            if (index < 0 || index >= oc->nb_chapters) {
> -                av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in 
> metadata specifier.\n", index);
> +            printf("ret %d, stream_spec %s\n", ret, stream_spec);
> +        }
> +        else {
> +            switch (type) {
> +            case 'g':
> +                m = &oc->metadata;
> +                break;
> +            case 'c':
> +                if (index < 0 || index >= oc->nb_chapters) {
> +                    av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in 
> metadata specifier.\n", index);
> +                    exit_program(1);
> +                }
> +                m = &oc->chapters[index]->metadata;
> +                break;
> +            default:
> +                av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier 
> %s.\n", o->metadata[i].specifier);
>                  exit_program(1);
>              }
> -            m = &oc->chapters[index]->metadata;
> -            break;
> -        default:
> -            av_log(NULL, AV_LOG_FATAL, "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);
>          }
> -
> -        av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
>      }
>  
>      reset_options(o);
> @@ -4157,7 +4188,7 @@ static const OptionDef options[] = {
>      { "codec", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = 
> OFFSET(codec_names)}, "codec name", "codec" },
>      { "pre", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = OFFSET(presets)}, 
> "preset name", "preset" },
>      { "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",
> +    { "map_metadata", HAS_ARG | OPT_STRING | OPT_SPEC, {.off = 
> OFFSET(metadata_map)}, "set metadata information of outfile from infile",
>        "outfile[,metadata]:infile[,metadata]" },
>      { "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" },
> diff --git a/doc/avconv.texi b/doc/avconv.texi
> index 68266a9..ff414a8 100644
> --- a/doc/avconv.texi
> +++ b/doc/avconv.texi
> @@ -170,9 +170,9 @@ For example, for setting the title in the output file:
>  avconv -i in.avi -metadata title="my title" out.flv
>  @end example
>  
> -To set the language of the second stream:
> +To set the language of the first audio stream:
>  @example
> -avconv -i INPUT -metadata:s:1 language=eng OUTPUT
> +avconv -i INPUT -metadata:s:a:0 language=eng OUTPUT
>  @end example
>  
>  @item -target @var{type} (@emph{output})
> @@ -677,14 +677,28 @@ avconv -i INPUT -map 0 -map -0:a:1 OUTPUT
>  
>  Note that using this option disables the default mappings for this output 
> file.
>  
> -@item -map_metadata[:@var{metadata_type}][:@var{index}] 
> @var{infile}[:@var{metadata_type}][:@var{index}] (@emph{output,per-metadata})
> +@item -map_metadata[:@var{metadata_spec_out}] 
> @var{infile}[:@var{metadata_spec_in}] (@emph{output,per-metadata})
>  Set metadata information of the next output file from @var{infile}. Note that
>  those are file indices (zero-based), not filenames.
> -Optional @var{metadata_type} parameters specify, which metadata to copy - 
> (g)lobal
> -(i.e. metadata that applies to the whole file), per-(s)tream, per-(c)hapter 
> or
> -per-(p)rogram. All metadata specifiers other than global must be followed by 
> the
> -stream/chapter/program index. If metadata specifier is omitted, it defaults 
> to
> -global.
> +Optional @var{metadata_spec_in/out} parameters specify, which metadata to 
> copy.
> +A metadata specifier can have the following forms:
> +@table @option
> +@item @var{g}
> +global metadata, i.e. metadata that applies to the whole file
> +
> +@item @var{s}[:@var{stream_spec}]
> +per-stream metadata. @var{stream_spec} is a stream specifier as described
> +in the Stream specifiers chapter. In an input metadata specifier, the first

I suspect @ref{} should be used here.

> +matching stream is copied from. In an output metadata specifier, all matching
> +streams are copied to.
> +
> +@item @var{c}:@var{chapter_index}
> +per-chapter metadata. @var{chapter_index} is the zero-based chapter index.
> +
> +@item @var{p}:@var{program_index}
> +per-program metadata. @var{program_index} is the zero-based program index.
> +@end table
> +If metadata specifier is omitted, it defaults to global.
>  
>  By default, global metadata is copied from the first input file,
>  per-stream and per-chapter metadata is copied along with streams/chapters. 
> These
> @@ -696,6 +710,14 @@ of the output file:
>  @example
>  avconv -i in.ogg -map_metadata 0:s:0 out.mp3
>  @end example
> +
> +To do the reverse, i.e. copy global metadata to all audio streams:
> +@example
> +avconv -i in.mkv -map_metadata:s:a 0:g out.mkv
> +@end example
> +Note that simple @code{0} would work as well in this example, since global
> +metadata is assumed by default.
> +
>  @item -map_chapters @var{input_file_index} (@emph{output})
>  Copy chapters from input file with index @var{input_file_index} to the next
>  output file. If no chapter mapping is specified, then chapters are copied 
> from
> -- 

in general LGTM
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to