[FFmpeg-devel] [PATCH 4/4] ffprobe: add flat output format.

Clément Bœsch ubitux at gmail.com
Thu May 31 20:56:50 CEST 2012


On Thu, May 31, 2012 at 01:14:17AM +0200, Stefano Sabatini wrote:
> On date Tuesday 2012-05-29 23:24:42 +0200, Clément Bœsch encoded:
> > ---
> >  doc/ffprobe.texi |   24 ++++++++
> >  ffprobe.c        |  163 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> >  2 files changed, 186 insertions(+), 1 deletion(-)
> > 
> > diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi
> > index a725c37..e1c64ae 100644
> > --- a/doc/ffprobe.texi
> > +++ b/doc/ffprobe.texi
> > @@ -269,6 +269,30 @@ CSV format.
> >  This writer is equivalent to
> >  @code{compact=item_sep=,:nokey=1:escape=csv}.
> >  
> > + at section flat
> > +Flat format.
> > +
> 
> > +A free-form output with each line contains an explicit key=value, such as
> > +"streams.stream.3.tags.foo=bar"
> 
> Mention that the output is sh-escaped, so can be directly embedded in
> sh scripts (if using a valid separator).
> 

Added.

> > +
> > +This writer accepts options as a list of @var{key}=@var{value} pairs,
> > +separated by ":".
> 
> Reminder: we should make the interface consistent with all writers,
> and maybe factorize some init code.
> 

I hope you will remember this, because I won't.

> > +
> > +The description of the accepted options follows.
> > +
> > + at table @option
> > + at item chr_sep, c
> 
> Nit: sep_char, s? (also, consistent with compact writer)
> Random abbreviations are usually confusing.
> 

I wanted to make a distinction with the item separator and this specific
separator. But it shouldn't hurt, renamed.

> > +Separator used between chapters/section/etc. Default is '.'.
> 
> "chapter"
> 
> Also maybe more clear:
> "Separator character used to separate the chapter and the section name
> in the printed field key."
> 

OK

> > +
> > + at item hierarchical, h
> > +Specify if the section name specification should be hierarchical. If
> > +set to 1, and if there is more than one section in the current
> > +chapter, the section name will be prefixed by the name of the
> > +chapter. A value of 0 will disable this behavior.
> > +
> > +Default value is 1.
> > + at end table
> > +
> >  @section ini
> >  INI format output.
> >  
> > diff --git a/ffprobe.c b/ffprobe.c
> > index 27ab62e..c64a687 100644
> > --- a/ffprobe.c
> > +++ b/ffprobe.c
> > @@ -727,6 +727,166 @@ static const Writer csv_writer = {
> >      .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
> >  };
> >  
> > +/* Flat output */
> > +
> > +typedef struct FlatContext {
> > +    const AVClass *class;
> > +    const char *section, *chapter;
> 
> > +    const char *sep;
> > +    char chr_sep;
> 
> nit: sep_str, sep, should be more consistent (e.g. check
> CompactContext), and I wonder if you can directly use the string.
> 

Renamed

[...]
> > +static void flat_print_section(WriterContext *wctx)
> > +{
> > +    int n = wctx->is_packets_and_frames ? wctx->nb_section_packet_frame
> > +                                        : wctx->nb_section;
> > +    FlatContext *flat = wctx->priv;
> 
> nit+++: private context is usually the first item in the declaration
> section
> 

Toggled

> > +
> > +    if (flat->hierarchical && wctx->multiple_sections)
> > +        printf("%s%c", flat->chapter, flat->chr_sep);
> > +    printf("%s%c", flat->section, flat->chr_sep);
> > +    if (wctx->multiple_sections)
> > +        printf("%d%c", n, flat->chr_sep);
> > +}
> 
> Would it make sense to use '_' like default separator character? This
> way you have sh code output by default.
> 

A lot of keys have a '_' (codec_time_base, display_aspect_ratio,
bits_per_sample, …), so I'm a bit reluctant to provide an unreadable
output by default.

> Also: you could cache the section name in the private context, like it
> is done in the INI writer.
> 

I admit I didn't quite understood the point of the copy in the INI writer;
I keep a pointer to the current section and chapter names in the private
context already, what would be the point of the copy?

> > +
> > +static void flat_print_chapter_header(WriterContext *wctx, const char *chapter)
> > +{
> > +    FlatContext *flat = wctx->priv;
> > +    flat->chapter = chapter;
> > +}
> > +
> > +static void flat_print_section_header(WriterContext *wctx, const char *section)
> > +{
> > +    FlatContext *flat = wctx->priv;
> > +    flat->section = section;
> > +}
> 
> nit: please declare these in order of printing, seems more
> logical/easier to read
> 

Re-ordered, hopefully to something making more sense.

-- 
Clément B.
-------------- next part --------------
From 5deb33ad48c58409f844977be1823d9f87775805 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <ubitux at gmail.com>
Date: Tue, 29 May 2012 23:22:39 +0200
Subject: [PATCH 3/3] ffprobe: add flat output format.

---
 doc/ffprobe.texi |   29 ++++++++++
 ffprobe.c        |  161 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 189 insertions(+), 1 deletion(-)

diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi
index a725c37..1778dab 100644
--- a/doc/ffprobe.texi
+++ b/doc/ffprobe.texi
@@ -269,6 +269,35 @@ CSV format.
 This writer is equivalent to
 @code{compact=item_sep=,:nokey=1:escape=csv}.
 
+ at section flat
+Flat format.
+
+A free-form output where each line contains an explicit key=value, such as
+"streams.stream.3.tags.foo=bar". The output is shell escaped, so it can be
+directly embedded in sh scripts as long as a valid separator is selected (see
+option @var{sep_char}).
+
+This writer accepts options as a list of @var{key}=@var{value} pairs,
+separated by ":".
+
+The description of the accepted options follows.
+
+ at table @option
+ at item sep_char, s
+Separator character used to separate the chapter, the section name, IDs or
+potential tags in the printed field key.
+
+Default value is '.'.
+
+ at item hierarchical, h
+Specify if the section name specification should be hierarchical. If
+set to 1, and if there is more than one section in the current
+chapter, the section name will be prefixed by the name of the
+chapter. A value of 0 will disable this behavior.
+
+Default value is 1.
+ at end table
+
 @section ini
 INI format output.
 
diff --git a/ffprobe.c b/ffprobe.c
index 27ab62e..87c5cfb 100644
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -727,6 +727,164 @@ static const Writer csv_writer = {
     .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
 };
 
+/* Flat output */
+
+typedef struct FlatContext {
+    const AVClass *class;
+    const char *section, *chapter;
+    const char *sep_str;
+    char sep;
+    int hierarchical;
+} FlatContext;
+
+#undef OFFSET
+#define OFFSET(x) offsetof(FlatContext, x)
+
+static const AVOption flat_options[]= {
+    {"sep_str",  "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  CHAR_MIN, CHAR_MAX },
+    {"s",        "set separator",    OFFSET(sep_str),    AV_OPT_TYPE_STRING, {.str="."},  CHAR_MIN, CHAR_MAX },
+    {"hierachical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
+    {"h",           "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
+    {NULL},
+};
+
+static const char *flat_get_name(void *ctx)
+{
+    return "flat";
+}
+
+static const AVClass flat_class = {
+    "FlatContext",
+    flat_get_name,
+    flat_options
+};
+
+static av_cold int flat_init(WriterContext *wctx, const char *args, void *opaque)
+{
+    FlatContext *flat = wctx->priv;
+    int err;
+
+    flat->class = &flat_class;
+    av_opt_set_defaults(flat);
+
+    if (args &&
+        (err = (av_set_options_string(flat, args, "=", ":"))) < 0) {
+        av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
+        return err;
+    }
+    if (strlen(flat->sep_str) != 1) {
+        av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
+               flat->sep_str);
+        return AVERROR(EINVAL);
+    }
+    flat->sep = flat->sep_str[0];
+    return 0;
+}
+
+static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
+{
+    const char *p;
+
+    for (p = src; *p; p++) {
+        if (!((*p >= '0' && *p <= '9') ||
+              (*p >= 'a' && *p <= 'z') ||
+              (*p >= 'A' && *p <= 'Z')))
+            av_bprint_chars(dst, '_', 1);
+        else
+            av_bprint_chars(dst, *p, 1);
+    }
+    return dst->str;
+}
+
+static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
+{
+    const char *p;
+
+    for (p = src; *p; p++) {
+        switch (*p) {
+        case '\n': av_bprintf(dst, "%s", "\\n");  break;
+        case '\r': av_bprintf(dst, "%s", "\\r");  break;
+        case '\\': av_bprintf(dst, "%s", "\\\\"); break;
+        case '"':  av_bprintf(dst, "%s", "\\\""); break;
+        default:   av_bprint_chars(dst, *p, 1);   break;
+        }
+    }
+    return dst->str;
+}
+
+static void flat_print_chapter_header(WriterContext *wctx, const char *chapter)
+{
+    FlatContext *flat = wctx->priv;
+    flat->chapter = chapter;
+}
+
+static void flat_print_section_header(WriterContext *wctx, const char *section)
+{
+    FlatContext *flat = wctx->priv;
+    flat->section = section;
+}
+
+static void flat_print_section(WriterContext *wctx)
+{
+    FlatContext *flat = wctx->priv;
+    int n = wctx->is_packets_and_frames ? wctx->nb_section_packet_frame
+                                        : wctx->nb_section;
+
+    if (flat->hierarchical && wctx->multiple_sections)
+        printf("%s%c", flat->chapter, flat->sep);
+    printf("%s%c", flat->section, flat->sep);
+    if (wctx->multiple_sections)
+        printf("%d%c", n, flat->sep);
+}
+
+static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
+{
+    flat_print_section(wctx);
+    printf("%s=%lld\n", key, value);
+}
+
+static void flat_print_str(WriterContext *wctx, const char *key, const char *value)
+{
+    FlatContext *flat = wctx->priv;
+    AVBPrint buf;
+
+    flat_print_section(wctx);
+    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+    printf("%s=", flat_escape_key_str(&buf, key, flat->sep));
+    av_bprint_clear(&buf);
+    printf("\"%s\"\n", flat_escape_value_str(&buf, value));
+    av_bprint_finalize(&buf, NULL);
+}
+
+static void flat_show_tags(WriterContext *wctx, AVDictionary *dict)
+{
+    FlatContext *flat = wctx->priv;
+    AVBPrint buf;
+    AVDictionaryEntry *tag = NULL;
+
+    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+    while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
+        flat_print_section(wctx);
+        av_bprint_clear(&buf);
+        printf("tags%c%s=", flat->sep, flat_escape_key_str(&buf, tag->key, flat->sep));
+        av_bprint_clear(&buf);
+        printf("\"%s\"\n", flat_escape_value_str(&buf, tag->value));
+    }
+    av_bprint_finalize(&buf, NULL);
+}
+
+static const Writer flat_writer = {
+    .name                  = "flat",
+    .priv_size             = sizeof(FlatContext),
+    .init                  = flat_init,
+    .print_chapter_header  = flat_print_chapter_header,
+    .print_section_header  = flat_print_section_header,
+    .print_integer         = flat_print_int,
+    .print_string          = flat_print_str,
+    .show_tags             = flat_show_tags,
+    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
+};
+
 /* INI format output */
 
 typedef struct {
@@ -1338,6 +1496,7 @@ static void writer_register_all(void)
     writer_register(&default_writer);
     writer_register(&compact_writer);
     writer_register(&csv_writer);
+    writer_register(&flat_writer);
     writer_register(&ini_writer);
     writer_register(&json_writer);
     writer_register(&xml_writer);
@@ -1905,7 +2064,7 @@ static const OptionDef options[] = {
     { "pretty", 0, {(void*)&opt_pretty},
       "prettify the format of displayed values, make it more human readable" },
     { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
-      "set the output printing format (available formats are: default, compact, csv, ini, json, xml)", "format" },
+      "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
     { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" },
     { "show_error",   OPT_BOOL, {(void*)&do_show_error} ,  "show probing error" },
     { "show_format",  OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
-- 
1.7.10.2

-------------- 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/20120531/c2e651a0/attachment.asc>


More information about the ffmpeg-devel mailing list