Browse code

ffprobe: add INI writer

Liberally based on the work of Luca Barbato <lu_zero@gentoo.org>, done
for libav/avprobe.

Stefano Sabatini authored on 2012/05/27 08:24:19
Showing 3 changed files
... ...
@@ -2,6 +2,8 @@ Entries are sorted chronologically from oldest to youngest within each release,
2 2
 releases are sorted from youngest to oldest.
3 3
 
4 4
 version next:
5
+- INI output in ffprobe
6
+
5 7
 
6 8
 version 0.11:
7 9
 Fixes:CVE-2012-2772, CVE-2012-2774, CVE-2012-2775, CVE-2012-2776, CVE-2012-2777,
... ...
@@ -269,6 +269,45 @@ CSV format.
269 269
 This writer is equivalent to
270 270
 @code{compact=item_sep=,:nokey=1:escape=csv}.
271 271
 
272
+@section ini
273
+INI format output.
274
+
275
+Print output in an INI based format.
276
+
277
+The following conventions are adopted:
278
+
279
+@itemize
280
+@item
281
+all key and values are UTF-8
282
+@item
283
+'.' is the subgroup separator
284
+@item
285
+newline, '\t', '\f', '\b' and the following characters are escaped
286
+@item
287
+'\' is the escape character
288
+@item
289
+'#' is the comment indicator
290
+@item
291
+'=' is the key/value separator
292
+@item
293
+':' is not used but usually parsed as key/value separator
294
+@end itemize
295
+
296
+This writer accepts options as a list of @var{key}=@var{value} pairs,
297
+separated by ":".
298
+
299
+The description of the accepted options follows.
300
+
301
+@table @option
302
+@item hierarchical, h
303
+Specify if the section name specification should be hierarchical. If
304
+set to 1, and if there is more than one section in the current
305
+chapter, the section name will be prefixed by the name of the
306
+chapter. A value of 0 will disable this behavior.
307
+
308
+Default value is 1.
309
+@end table
310
+
272 311
 @section json
273 312
 JSON based format.
274 313
 
... ...
@@ -713,6 +713,174 @@ static const Writer csv_writer = {
713 713
     .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
714 714
 };
715 715
 
716
+/* INI format output */
717
+
718
+typedef struct {
719
+    const AVClass *class;
720
+    AVBPrint chapter_name, section_name;
721
+    int print_packets_and_frames;
722
+    int nb_frame;
723
+    int nb_packet;
724
+    int hierarchical;
725
+} INIContext;
726
+
727
+#undef OFFSET
728
+#define OFFSET(x) offsetof(INIContext, x)
729
+
730
+static const AVOption ini_options[] = {
731
+    {"hierachical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
732
+    {"h",           "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 },
733
+    {NULL},
734
+};
735
+
736
+static const char *ini_get_name(void *ctx)
737
+{
738
+    return "ini";
739
+}
740
+
741
+static const AVClass ini_class = {
742
+    "INIContext",
743
+    ini_get_name,
744
+    ini_options
745
+};
746
+
747
+static av_cold int ini_init(WriterContext *wctx, const char *args, void *opaque)
748
+{
749
+    INIContext *ini = wctx->priv;
750
+    int err;
751
+
752
+    av_bprint_init(&ini->chapter_name, 1, AV_BPRINT_SIZE_UNLIMITED);
753
+    av_bprint_init(&ini->section_name, 1, AV_BPRINT_SIZE_UNLIMITED);
754
+    ini->nb_frame = ini->nb_packet = 0;
755
+
756
+    ini->class = &ini_class;
757
+    av_opt_set_defaults(ini);
758
+
759
+    if (args && (err = av_set_options_string(ini, args, "=", ":")) < 0) {
760
+        av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
761
+        return err;
762
+    }
763
+
764
+    return 0;
765
+}
766
+
767
+static av_cold void ini_uninit(WriterContext *wctx)
768
+{
769
+    INIContext *ini = wctx->priv;
770
+    av_bprint_finalize(&ini->chapter_name, NULL);
771
+    av_bprint_finalize(&ini->section_name, NULL);
772
+}
773
+
774
+static void ini_print_header(WriterContext *wctx)
775
+{
776
+    printf("# ffprobe output\n\n");
777
+}
778
+
779
+static char *ini_escape_str(AVBPrint *dst, const char *src)
780
+{
781
+    int i = 0;
782
+    char c = 0;
783
+
784
+    while (c = src[i++]) {
785
+        switch (c) {
786
+        case '\b': av_bprintf(dst, "%s", "\\b"); break;
787
+        case '\f': av_bprintf(dst, "%s", "\\f"); break;
788
+        case '\n': av_bprintf(dst, "%s", "\\n"); break;
789
+        case '\r': av_bprintf(dst, "%s", "\\r"); break;
790
+        case '\t': av_bprintf(dst, "%s", "\\t"); break;
791
+        case '\\':
792
+        case '#' :
793
+        case '=' :
794
+        case ':' : av_bprint_chars(dst, '\\', 1);
795
+        default:
796
+            if ((unsigned char)c < 32)
797
+                av_bprintf(dst, "\\x00%02x", c & 0xff);
798
+            else
799
+                av_bprint_chars(dst, c, 1);
800
+            break;
801
+        }
802
+    }
803
+    return dst->str;
804
+}
805
+
806
+static void ini_print_chapter_header(WriterContext *wctx, const char *chapter)
807
+{
808
+    INIContext *ini = wctx->priv;
809
+
810
+    av_bprint_clear(&ini->chapter_name);
811
+    av_bprintf(&ini->chapter_name, "%s", chapter);
812
+
813
+    if (wctx->nb_chapter)
814
+        printf("\n");
815
+    ini->print_packets_and_frames = !strcmp("packets_and_frames", chapter);
816
+}
817
+
818
+static void ini_print_section_header(WriterContext *wctx, const char *section)
819
+{
820
+    INIContext *ini = wctx->priv;
821
+    int n;
822
+    if (wctx->nb_section)
823
+        printf("\n");
824
+    av_bprint_clear(&ini->section_name);
825
+
826
+    if (ini->hierarchical && wctx->multiple_sections)
827
+        av_bprintf(&ini->section_name, "%s.", ini->chapter_name.str);
828
+    av_bprintf(&ini->section_name, "%s", section);
829
+
830
+    if (ini->print_packets_and_frames)
831
+        n = !strcmp(section, "packet") ? ini->nb_packet++ : ini->nb_frame++;
832
+    else
833
+        n = wctx->nb_section;
834
+    if (wctx->multiple_sections)
835
+        av_bprintf(&ini->section_name, ".%d", n);
836
+    printf("[%s]\n", ini->section_name.str);
837
+}
838
+
839
+static void ini_print_str(WriterContext *wctx, const char *key, const char *value)
840
+{
841
+    AVBPrint buf;
842
+
843
+    av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
844
+    printf("%s=", ini_escape_str(&buf, key));
845
+    av_bprint_clear(&buf);
846
+    printf("%s\n", ini_escape_str(&buf, value));
847
+    av_bprint_finalize(&buf, NULL);
848
+}
849
+
850
+static void ini_print_int(WriterContext *wctx, const char *key, long long int value)
851
+{
852
+    printf("%s=%lld\n", key, value);
853
+}
854
+
855
+static void ini_show_tags(WriterContext *wctx, AVDictionary *dict)
856
+{
857
+    INIContext *ini = wctx->priv;
858
+    AVDictionaryEntry *tag = NULL;
859
+    int is_first = 1;
860
+
861
+    while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
862
+        if (is_first) {
863
+            printf("\n[%s.tags]\n", ini->section_name.str);
864
+            is_first = 0;
865
+        }
866
+        writer_print_string(wctx, tag->key, tag->value, 0);
867
+    }
868
+}
869
+
870
+static const Writer ini_writer = {
871
+    .name                  = "ini",
872
+    .priv_size             = sizeof(INIContext),
873
+    .init                  = ini_init,
874
+    .uninit                = ini_uninit,
875
+    .print_header          = ini_print_header,
876
+    .print_chapter_header  = ini_print_chapter_header,
877
+    .print_section_header  = ini_print_section_header,
878
+    .print_integer         = ini_print_int,
879
+    .print_string          = ini_print_str,
880
+    .show_tags             = ini_show_tags,
881
+    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
882
+};
883
+
716 884
 /* JSON output */
717 885
 
718 886
 typedef struct {
... ...
@@ -1168,6 +1336,7 @@ static void writer_register_all(void)
1168 1168
     writer_register(&default_writer);
1169 1169
     writer_register(&compact_writer);
1170 1170
     writer_register(&csv_writer);
1171
+    writer_register(&ini_writer);
1171 1172
     writer_register(&json_writer);
1172 1173
     writer_register(&xml_writer);
1173 1174
 }
... ...
@@ -1734,7 +1903,7 @@ static const OptionDef options[] = {
1734 1734
     { "pretty", 0, {(void*)&opt_pretty},
1735 1735
       "prettify the format of displayed values, make it more human readable" },
1736 1736
     { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format},
1737
-      "set the output printing format (available formats are: default, compact, csv, json, xml)", "format" },
1737
+      "set the output printing format (available formats are: default, compact, csv, ini, json, xml)", "format" },
1738 1738
     { "show_error",   OPT_BOOL, {(void*)&do_show_error} ,  "show probing error" },
1739 1739
     { "show_format",  OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
1740 1740
     { "show_frames",  OPT_BOOL, {(void*)&do_show_frames} , "show frames info" },