[FFmpeg-devel] [PATCH] ffprobe: add show_entries option

Clément Bœsch ubitux at gmail.com
Tue Nov 20 21:07:55 CET 2012


On Tue, Nov 20, 2012 at 12:21:02AM +0100, Stefano Sabatini wrote:
> On date Thursday 2012-09-13 22:53:56 +0200, Stefano Sabatini encoded:
[...]
> Simplified with the use of unique names.
> -- 
> FFmpeg = Free & Friendly Moronic Power Elegant God

> From 2654af4fd2a971af9dec1e5e3824b05a2f75a852 Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab at gmail.com>
> Date: Mon, 17 Sep 2012 21:08:09 +0200
> Subject: [PATCH] ffprobe: implement subsection field selection
> 
> ---
>  doc/ffprobe.texi |   42 ++++++++++
>  ffprobe.c        |  227 +++++++++++++++++++++++++++++++++++++++++++-----------
>  2 files changed, 224 insertions(+), 45 deletions(-)
> 
> diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi
> index 7b47fba..39322cd 100644
> --- a/doc/ffprobe.texi
> +++ b/doc/ffprobe.texi
> @@ -133,6 +133,48 @@ Like @option{-show_format}, but only prints the specified entry of the
>  container format information, rather than all. This option may be given more
>  than once, then all specified entries will be shown.
>  
> +This option is deprecated, use @code{show_entries} instead.
> +
> + at item -show_entries @var{section_entries}
> +Set list of entries to show.
> +
> +Entries are specified according to the following
> +syntax. @var{section_entries} contains a list of section entries
> +separated by @code{:}. Each section entry is composed by a section
> +name (or unique name), optionally followed by a list of entries local
> +to that section, separated by @code{,}.
> +
> +If section name is specified but is followed by no @code{=}, all
> +entries are printed to output, together with all the contained
> +sections. Otherwise only the entries specified in the local section
> +entries list are printed. In particular, if @code{=} is specified but
> +the list of local entries is empty, then no entries will be shown for
> +that section.
> +
> +Note that the order of specification of the local section entries is
> +not honored in the output, and the usual display order will be
> +reatained.
> +

retained?

> +The formal syntax is given by:
> + at example
> + at var{LOCAL_SECTION_ENTRIES} ::= @var{SECTION_ENTRY_NAME}[, at var{LOCAL_SECTION_ENTRIES}]
> + at var{SECTION_ENTRY}         ::= @var{SECTION_NAME}[=[@var{LOCAL_SECTION_ENTRIES}]]
> + at var{SECTION_ENTRIES}       ::= @var{SECTION_ENTRY}[:@var{SECTION_ENTRIES}]
> + at end example
> +
> +For example, to show only the index and type of each stream, and the PTS
> +time, duration time, and stream index of the packets, you can specify
> +the argument:
> + at example
> +"packet=pts_time,duration_time,stream_index : stream=index,codec_type"
> + at end example
> +
> +To show all the entries in the section "format", but only the codec
> +type in the section "stream", specify the argument:
> + at example
> +"format : stream=codec_type"
> + at end example
> +

Can we use that for tags/metadata?

>  @item -show_packets
>  Show information about each packet contained in the input multimedia
>  stream.
> diff --git a/ffprobe.c b/ffprobe.c
> index 748a4b9..5c3a47c 100644
> --- a/ffprobe.c
> +++ b/ffprobe.c
> @@ -55,7 +55,6 @@ static int do_read_packets = 0;
>  static int do_show_error   = 0;
>  static int do_show_format  = 0;
>  static int do_show_frames  = 0;
> -static AVDictionary *fmt_entries_to_show = NULL;
>  static int do_show_packets = 0;
>  static int do_show_streams = 0;
>  static int do_show_data    = 0;
> @@ -73,16 +72,22 @@ static char *stream_specifier;
>  
>  /* section structure definition */
>  
> +#define SECTION_MAX_NB_PARENTS   2
> +#define SECTION_MAX_NB_CHILDREN 10
> +
>  struct section {
>      int id;             ///< unique id indentifying a section
>      const char *name;
> -
>  #define SECTION_FLAG_IS_WRAPPER      1 ///< the section only contains other sections, but has no data at its own level
>  #define SECTION_FLAG_IS_ARRAY        2 ///< the section contains an array of elements of the same type
>  #define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
>                                             ///  For these sections the element_name field is mandatory.
>      int flags;
> +    int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
>      const char *element_name; ///< name of the contained element, if provided
> +    const char *unique_name;  ///< unique section name, in case the name is ambiguous
> +    AVDictionary *entries_to_show;
> +    int show_all_entries;
>  };
>  
>  typedef enum {
> @@ -103,27 +108,29 @@ typedef enum {
>      SECTION_ID_STREAM,
>      SECTION_ID_STREAM_DISPOSITION,
>      SECTION_ID_STREAMS,
> -    SECTION_ID_STREAM_TAGS
> +    SECTION_ID_STREAM_TAGS,
>  } SectionID;
>  
> -static const struct section sections[] = {
> -    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR,              "error" },
> -    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT,             "format" },
> -    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS,        "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, .element_name = "tag" },
> -    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME,              "frame" },
> -    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES,             "frames", SECTION_FLAG_IS_ARRAY },
> -    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS,         "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, .element_name = "tag" },
> -    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION,    "library_version" },
> -    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS,   "library_versions", SECTION_FLAG_IS_ARRAY },
> -    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET,             "packet" },
> -    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS,            "packets", SECTION_FLAG_IS_ARRAY },
> -    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY },
> -    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION,    "program_version" },
> -    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT,               "root", SECTION_FLAG_IS_WRAPPER },
> -    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM,             "stream" },
> -    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition" },
> -    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS,            "streams", SECTION_FLAG_IS_ARRAY },
> -    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS,        "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, .element_name = "tag" },
> +static struct section sections[] = {
> +    [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_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", 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" },
>  };
>  
>  static const OptionDef *options;
> @@ -146,7 +153,6 @@ static int *selected_streams;
>  
>  static void exit_program(void)
>  {
> -    av_dict_free(&fmt_entries_to_show);
>  }
>  
>  struct unit_value {
> @@ -237,7 +243,7 @@ struct WriterContext {
>      char *name;                     ///< name of this writer instance
>      void *priv;                     ///< private data for use by the filter
>  
> -    const struct section *sections; ///< array containing all sections
> +    struct section *sections;       ///< array containing all sections
>      int nb_sections;                ///< number of sections
>  
>      int level;                      ///< current level, starting from 0
> @@ -246,10 +252,9 @@ struct WriterContext {
>      unsigned int nb_item[SECTION_MAX_NB_LEVELS];
>  
>      /** section per each level */
> -    const struct section *section[SECTION_MAX_NB_LEVELS];
> +    struct section *section[SECTION_MAX_NB_LEVELS];
>      AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
>                                                    ///  used by various writers
> -
>      unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section
>      unsigned int nb_section_frame;  ///< number of the frame  section in case we are in "packets_and_frames" section
>      unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames
> @@ -286,7 +291,7 @@ static void writer_close(WriterContext **wctx)
>  }
>  
>  static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
> -                       const struct section *sections, int nb_sections)
> +                       struct section *sections, int nb_sections)
>  {
>      int i, ret = 0;
>  
> @@ -375,9 +380,9 @@ static inline void writer_print_section_footer(WriterContext *wctx)
>  static inline void writer_print_integer(WriterContext *wctx,
>                                          const char *key, long long int val)
>  {
> -    if ((wctx->section[wctx->level]->id != SECTION_ID_FORMAT
> -         && wctx->section[wctx->level]->id != SECTION_ID_FORMAT_TAGS) ||
> -        !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) {
> +    struct section *section = wctx->section[wctx->level];
> +

looks like it can be const

> +    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
>          wctx->writer->print_integer(wctx, key, val);
>          wctx->nb_item[wctx->level]++;
>      }
> @@ -386,11 +391,12 @@ static inline void writer_print_integer(WriterContext *wctx,
>  static inline void writer_print_string(WriterContext *wctx,
>                                         const char *key, const char *val, int opt)
>  {
> +    struct section *section = wctx->section[wctx->level];
> +

here as well

>      if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
>          return;
> -    if ((wctx->section[wctx->level]->id != SECTION_ID_FORMAT
> -         && wctx->section[wctx->level]->id != SECTION_ID_FORMAT_TAGS) ||
> -        !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) {
> +
> +    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
>          wctx->writer->print_string(wctx, key, val);
>          wctx->nb_item[wctx->level]++;
>      }
> @@ -1958,11 +1964,100 @@ static int opt_format(void *optctx, const char *opt, const char *arg)
>      return 0;
>  }
>  
> +static inline void mark_section_show_entries(SectionID section_id,
> +                                             int show_all_entries, AVDictionary *entries)
> +{
> +    struct section *section = &sections[section_id];
> +
> +    section->show_all_entries = show_all_entries;
> +    if (show_all_entries) {
> +        SectionID *id;
> +        for (id = section->children_ids; *id != -1; id++)
> +            mark_section_show_entries(*id, show_all_entries, entries);
> +    } else {
> +        av_dict_copy(&section->entries_to_show, entries, 0);
> +    }
> +}
> +
> +static int match_section(const char *section_name,
> +                         int show_all_entries, AVDictionary *entries)
> +{
> +    int i, ret = 0;
> +
> +    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) {
> +        struct section *section = &sections[i];

ditto

[...]

Anyway, LGTM, nice work, thanks.

-- 
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20121120/3c3649e8/attachment.asc>


More information about the ffmpeg-devel mailing list