[FFmpeg-devel] [PATCH] Add programs to ffprobe

Stefano Sabatini stefasab at gmail.com
Fri Jul 19 19:02:13 CEST 2013


On date Friday 2013-07-19 15:24:50 +0200, Florent Tribouilloy encoded:
> Option -show_programs for ffprobe will sort the output
> 
> Signed-off-by: Florent Tribouilloy <florent.tribouilloy at smartjog.com>
> ---
>  doc/ffprobe.texi |   14 +++++--
>  doc/ffprobe.xsd  |   26 +++++++++++++
>  ffprobe.c        |  112 ++++++++++++++++++++++++++++++++++++++++--------------
>  3 files changed, 121 insertions(+), 31 deletions(-)
> 
> diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi
> index 38517a8..02cffbb 100644
> --- a/doc/ffprobe.texi
> +++ b/doc/ffprobe.texi
> @@ -44,7 +44,8 @@ name (which may be shared by other sections), and an unique
>  name. See the output of @option{sections}.
>  
>  Metadata tags stored in the container or in the streams are recognized
> -and printed in the corresponding "FORMAT" or "STREAM" section.
> +and printed in the corresponding "FORMAT", "STREAM" or "PROGRAM_STREAM"
> +section.
>  
>  @c man end
>  
> @@ -209,6 +210,13 @@ multimedia stream.
>  Each media stream information is printed within a dedicated section
>  with name "STREAM".
>  
> + at item -show_programs
> +Show information about programs and their streams contained in the input
> +multimedia stream.
> +
> +Each media stream information is printed within a dedicated section
> +with name "PROGRAM_STREAM".
> +
>  @item -show_chapters
>  Show information about chapters stored in the format.
>  
> @@ -279,8 +287,8 @@ keyN=valN
>  [/SECTION]
>  @end example
>  
> -Metadata tags are printed as a line in the corresponding FORMAT or
> -STREAM section, and are prefixed by the string "TAG:".
> +Metadata tags are printed as a line in the corresponding FORMAT, STREAM or
> +PROGRAM_STREAM section, and are prefixed by the string "TAG:".
>  
>  A description of the accepted options follows.
>  
> diff --git a/doc/ffprobe.xsd b/doc/ffprobe.xsd
> index bd890b1..5716c7f 100644
> --- a/doc/ffprobe.xsd
> +++ b/doc/ffprobe.xsd
> @@ -11,6 +11,7 @@
>              <xsd:element name="packets"  type="ffprobe:packetsType" minOccurs="0" maxOccurs="1" />
>              <xsd:element name="frames"   type="ffprobe:framesType"  minOccurs="0" maxOccurs="1" />
>              <xsd:element name="streams"  type="ffprobe:streamsType" minOccurs="0" maxOccurs="1" />
> +            <xsd:element name="programs" type="ffprobe:programsType" minOccurs="0" maxOccurs="1" />
>              <xsd:element name="chapters" type="ffprobe:chaptersType" minOccurs="0" maxOccurs="1" />
>              <xsd:element name="format"   type="ffprobe:formatType"  minOccurs="0" maxOccurs="1" />
>              <xsd:element name="error"    type="ffprobe:errorType"   minOccurs="0" maxOccurs="1" />
> @@ -87,6 +88,12 @@
>          </xsd:sequence>
>      </xsd:complexType>
>  
> +    <xsd:complexType name="programsType">
> +        <xsd:sequence>
> +            <xsd:element name="programs" type="ffprobe:programType" minOccurs="0" maxOccurs="unbounded"/>
> +        </xsd:sequence>
> +    </xsd:complexType>
> +
>      <xsd:complexType name="streamDispositionType">
>        <xsd:attribute name="default"          type="xsd:int" use="required" />
>        <xsd:attribute name="dub"              type="xsd:int" use="required" />
> @@ -147,6 +154,25 @@
>        <xsd:attribute name="nb_read_packets"  type="xsd:int"/>
>      </xsd:complexType>
>  
> +    <xsd:complexType name="programType">
> +      <xsd:sequence>
> +        <xsd:element name="tag" type="ffprobe:tagType" minOccurs="0" maxOccurs="unbounded"/>
> +      </xsd:sequence>
> +
> +      <xsd:attribute name="program_id"           type="xsd:int" use="required"/>
> +      <xsd:attribute name="program_num"          type="xsd:int" use="required"/>
> +      <xsd:attribute name="nb_stream"            type="xsd:int" use="required"/>
> +      <xsd:attribute name="start_time"           type="xsd:int" use="required"/>
> +      <xsd:attribute name="end_time"             type="xsd:int" use="required"/>
> +      <xsd:attribute name="pmt_pid"              type="xsd:int" use="required"/>
> +      <xsd:attribute name="pcr_pid"              type="xsd:int" use="required"/>
> +      <xsd:attribute name="pts_wrap_reference"   type="xsd:int" use="required"/>
> +      <xsd:attribute name="pts_wrap_behavior"    type="xsd:int" use="required"/>
> +        <xsd:sequence>
> +            <xsd:element name="program_stream" type="ffprobe:streamType" minOccurs="0" maxOccurs="unbounded"/>
> +        </xsd:sequence>
> +    </xsd:complexType>
> +
>      <xsd:complexType name="formatType">
>        <xsd:sequence>
>          <xsd:element name="tag" type="ffprobe:tagType" minOccurs="0" maxOccurs="unbounded"/>
> diff --git a/ffprobe.c b/ffprobe.c
> index d4adde0..d617592 100644
> --- a/ffprobe.c
> +++ b/ffprobe.c
> @@ -57,6 +57,7 @@ static int do_show_error   = 0;
>  static int do_show_format  = 0;
>  static int do_show_frames  = 0;
>  static int do_show_packets = 0;
> +static int do_show_programs = 0;
>  static int do_show_streams = 0;
>  static int do_show_stream_disposition = 0;
>  static int do_show_data    = 0;
> @@ -108,7 +109,13 @@ typedef enum {
>      SECTION_ID_PACKET,
>      SECTION_ID_PACKETS,
>      SECTION_ID_PACKETS_AND_FRAMES,
> +    SECTION_ID_PROGRAM_STREAM_DISPOSITION,
> +    SECTION_ID_PROGRAM_STREAM_TAGS,
> +    SECTION_ID_PROGRAM,
> +    SECTION_ID_PROGRAM_STREAM,
> +    SECTION_ID_PROGRAM_TAGS,
>      SECTION_ID_PROGRAM_VERSION,
> +    SECTION_ID_PROGRAMS,
>      SECTION_ID_ROOT,
>      SECTION_ID_STREAM,
>      SECTION_ID_STREAM_DISPOSITION,
> @@ -117,28 +124,34 @@ typedef enum {
>  } SectionID;
>  

>  static struct section sections[] = {
> -    [SECTION_ID_CHAPTERS] =           { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
> -    [SECTION_ID_CHAPTER] =            { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } },
> -    [SECTION_ID_CHAPTER_TAGS] =       { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
> -    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR, "error", 0, { -1 } },
> -    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
> -    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
> -    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, -1 } },
> -    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, -1 } },
> -    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
> -    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
> -    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
> -    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
> -    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
> -    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET, "packet", 0, { -1 } },
> -    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
> -    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
> -                                        { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_STREAMS, SECTION_ID_PACKETS,
> -                                          SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, -1} },
> -    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
> -    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, -1 } },
> -    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
> -    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
> +    [SECTION_ID_CHAPTERS] =                   { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
> +    [SECTION_ID_CHAPTER] =                    { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } },
> +    [SECTION_ID_CHAPTER_TAGS] =               { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
> +    [SECTION_ID_ERROR] =                      { SECTION_ID_ERROR, "error", 0, { -1 } },
> +    [SECTION_ID_FORMAT] =                     { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
> +    [SECTION_ID_FORMAT_TAGS] =                { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
> +    [SECTION_ID_FRAMES] =                     { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, -1 } },
> +    [SECTION_ID_FRAME] =                      { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, -1 } },
> +    [SECTION_ID_FRAME_TAGS] =                 { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
> +    [SECTION_ID_LIBRARY_VERSIONS] =           { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
> +    [SECTION_ID_LIBRARY_VERSION] =            { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
> +    [SECTION_ID_PACKETS] =                    { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
> +    [SECTION_ID_PACKETS_AND_FRAMES] =         { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
> +    [SECTION_ID_PACKET] =                     { SECTION_ID_PACKET, "packet", 0, { -1 } },
> +    [SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" },
> +    [SECTION_ID_PROGRAM_STREAM_TAGS] =        { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" },
> +    [SECTION_ID_PROGRAM] =                    { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAM, -1 } },
> +    [SECTION_ID_PROGRAM_STREAM] =             { SECTION_ID_PROGRAM_STREAM, "program_stream", 0, { SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, -1 } },
> +    [SECTION_ID_PROGRAM_TAGS] =               { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
> +    [SECTION_ID_PROGRAM_VERSION] =            { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
> +    [SECTION_ID_PROGRAMS] =                   { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
> +    [SECTION_ID_ROOT] =                       { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
> +                                                { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS,
> +                                                  SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, -1} },
> +    [SECTION_ID_STREAMS] =                    { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
> +    [SECTION_ID_STREAM] =                     { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, -1 } },
> +    [SECTION_ID_STREAM_DISPOSITION] =         { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
> +    [SECTION_ID_STREAM_TAGS] =                { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
>  };

please reindent the other entries in a separate patch, so the patch
it's easier to read

>  
>  static const OptionDef *options;
> @@ -1598,7 +1611,7 @@ static void read_packets(WriterContext *w, AVFormatContext *fmt_ctx)
>      }
>  }
>  
> -static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx)
> +static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, int in_program)
>  {
>      AVStream *stream = fmt_ctx->streams[stream_idx];
>      AVCodecContext *dec_ctx;
> @@ -1610,7 +1623,7 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i
>  
>      av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
>  
> -    writer_print_section_header(w, SECTION_ID_STREAM);
> +    writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM);
>  
>      print_int("index", stream->index);
>  
> @@ -1726,7 +1739,7 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i
>      } while (0)
>  
>      if (do_show_stream_disposition) {
> -    writer_print_section_header(w, SECTION_ID_STREAM_DISPOSITION);
> +    writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION : SECTION_ID_STREAM_DISPOSITION);
>      PRINT_DISPOSITION(DEFAULT,          "default");
>      PRINT_DISPOSITION(DUB,              "dub");
>      PRINT_DISPOSITION(ORIGINAL,         "original");
> @@ -1741,7 +1754,7 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i
>      writer_print_section_footer(w);
>      }
>  
> -    show_tags(w, stream->metadata, SECTION_ID_STREAM_TAGS);
> +    show_tags(w, stream->metadata, in_program ? SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS);
>  
>      writer_print_section_footer(w);
>      av_bprint_finalize(&pbuf, NULL);
> @@ -1754,7 +1767,43 @@ static void show_streams(WriterContext *w, AVFormatContext *fmt_ctx)
>      writer_print_section_header(w, SECTION_ID_STREAMS);
>      for (i = 0; i < fmt_ctx->nb_streams; i++)
>          if (selected_streams[i])
> -            show_stream(w, fmt_ctx, i);
> +            show_stream(w, fmt_ctx, i, 0);
> +    writer_print_section_footer(w);
> +}
> +
> +static void show_program(WriterContext *w, AVFormatContext *fmt_ctx, AVProgram *program)
> +{
> +    int i;
> +
> +    writer_print_section_header(w, SECTION_ID_PROGRAM);
> +    print_int("program_id", program->id);
> +    print_int("program_num", program->program_num);

> +    print_int("nb_stream", program->nb_stream_indexes);

nb_streams?

> +    print_int("start_time", program->start_time);
> +    print_int("end_time", program->end_time);

is this a pts or a time? It could be useful to give a representation
of the time as well.

Then this could be:
start_time_pts
start_time
...

Unfortunately the field name "_time" for denoting a timestamp was
poorly choosen.

> +    print_int("pmt_pid", program->pmt_pid);
> +    print_int("pcr_pid", program->pcr_pid);

> +    print_int("pts_wrap_reference", program->pts_wrap_reference);
> +    print_int("pts_wrap_behavior", program->pts_wrap_behavior);

I have no idea what these are for, and I'm not sure they should be
exposed.

> +    show_tags(w, program->metadata, SECTION_ID_PROGRAM_TAGS);
> +    for (i = 0; i < program->nb_stream_indexes; i++) {
> +        if (selected_streams[program->stream_index[i]])
> +            show_stream(w, fmt_ctx, program->stream_index[i], 1);
> +    }
> +    writer_print_section_footer(w);
> +}
> +
> +static void show_programs(WriterContext *w, AVFormatContext *fmt_ctx)
> +{
> +    int i;
> +
> +    writer_print_section_header(w, SECTION_ID_PROGRAMS);
> +    for (i = 0; i < fmt_ctx->nb_programs; i++) {
> +        AVProgram *program = fmt_ctx->programs[i];
> +        if (!program)
> +            continue;
> +        show_program(w, fmt_ctx, program);
> +    }
>      writer_print_section_footer(w);
>  }
>  
> @@ -1787,6 +1836,7 @@ static void show_format(WriterContext *w, AVFormatContext *fmt_ctx)
>      writer_print_section_header(w, SECTION_ID_FORMAT);
>      print_str("filename",         fmt_ctx->filename);
>      print_int("nb_streams",       fmt_ctx->nb_streams);
> +    print_int("nb_programs",      fmt_ctx->nb_programs);
>      print_str("format_name",      fmt_ctx->iformat->name);
>      if (!do_bitexact) {
>          if (fmt_ctx->iformat->long_name) print_str    ("format_long_name", fmt_ctx->iformat->long_name);
> @@ -1940,6 +1990,8 @@ static int probe_file(WriterContext *wctx, const char *filename)
>          if (do_show_frames || do_show_packets)
>              writer_print_section_footer(wctx);
>      }
> +    if (do_show_programs)
> +        show_programs(wctx, fmt_ctx);
>      if (do_show_streams)
>          show_streams(wctx, fmt_ctx);
>      if (do_show_chapters)
> @@ -2206,6 +2258,7 @@ DEFINE_OPT_SHOW_SECTION(library_versions, LIBRARY_VERSIONS);
>  DEFINE_OPT_SHOW_SECTION(packets,          PACKETS);
>  DEFINE_OPT_SHOW_SECTION(program_version,  PROGRAM_VERSION);
>  DEFINE_OPT_SHOW_SECTION(streams,          STREAMS);
> +DEFINE_OPT_SHOW_SECTION(programs,         PROGRAMS);
>  
>  static const OptionDef real_options[] = {
>  #include "cmdutils_common_opts.h"
> @@ -2232,6 +2285,7 @@ static const OptionDef real_options[] = {
>      { "show_entries", HAS_ARG, {.func_arg = opt_show_entries},
>        "show a set of specified entries", "entry_list" },
>      { "show_packets", 0, {(void*)&opt_show_packets}, "show packets info" },
> +    { "show_programs", 0, {(void*)&opt_show_programs}, "show programs info" },
>      { "show_streams", 0, {(void*)&opt_show_streams}, "show streams info" },
>      { "show_chapters", 0, {(void*)&opt_show_chapters}, "show chapters info" },
>      { "count_frames", OPT_BOOL, {(void*)&do_count_frames}, "count the number of frames per stream" },
> @@ -2295,8 +2349,10 @@ int main(int argc, char **argv)
>      SET_DO_SHOW(LIBRARY_VERSIONS, library_versions);
>      SET_DO_SHOW(PACKETS, packets);
>      SET_DO_SHOW(PROGRAM_VERSION, program_version);
> +    SET_DO_SHOW(PROGRAMS, programs);
>      SET_DO_SHOW(STREAMS, streams);
>      SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition);
> +    SET_DO_SHOW(PROGRAM_STREAM_DISPOSITION, stream_disposition);
>  
>      if (do_bitexact && (do_show_program_version || do_show_library_versions)) {
>          av_log(NULL, AV_LOG_ERROR,
> @@ -2334,7 +2390,7 @@ int main(int argc, char **argv)
>              ffprobe_show_library_versions(wctx);
>  
>          if (!input_filename &&
> -            ((do_show_format || do_show_streams || do_show_chapters || do_show_packets || do_show_error) ||
> +            ((do_show_format || do_show_programs || do_show_streams || do_show_chapters || do_show_packets || do_show_error) ||
>               (!do_show_program_version && !do_show_library_versions))) {
>              show_usage();
>              av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n");

Looks pretty sane otherwise, while at it check that the fate ffprobe
tests still pass (make fate-list | grep ffprobe), ask if you don't
know how to update the references.
-- 
FFmpeg = Free Foolish Mystic Proud Elastic God


More information about the ffmpeg-devel mailing list