diff --git a/Changelog b/Changelog index fc9f482720..ece8d27568 100644 --- a/Changelog +++ b/Changelog @@ -2,6 +2,8 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. version next: +- INI output in ffprobe + version 0.11: Fixes:CVE-2012-2772, CVE-2012-2774, CVE-2012-2775, CVE-2012-2776, CVE-2012-2777, diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi index cde1d34211..1f6566802c 100644 --- a/doc/ffprobe.texi +++ b/doc/ffprobe.texi @@ -269,6 +269,45 @@ CSV format. This writer is equivalent to @code{compact=item_sep=,:nokey=1:escape=csv}. +@section ini +INI format output. + +Print output in an INI based format. + +The following conventions are adopted: + +@itemize +@item +all key and values are UTF-8 +@item +'.' is the subgroup separator +@item +newline, '\t', '\f', '\b' and the following characters are escaped +@item +'\' is the escape character +@item +'#' is the comment indicator +@item +'=' is the key/value separator +@item +':' is not used but usually parsed as key/value separator +@end itemize + +This writer accepts options as a list of @var{key}=@var{value} pairs, +separated by ":". + +The description of the accepted options follows. + +@table @option +@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. +@end table + @section json JSON based format. diff --git a/ffprobe.c b/ffprobe.c index daea512eb8..2727dd7498 100644 --- a/ffprobe.c +++ b/ffprobe.c @@ -713,6 +713,174 @@ static const Writer csv_writer = { .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS, }; +/* INI format output */ + +typedef struct { + const AVClass *class; + AVBPrint chapter_name, section_name; + int print_packets_and_frames; + int nb_frame; + int nb_packet; + int hierarchical; +} INIContext; + +#undef OFFSET +#define OFFSET(x) offsetof(INIContext, x) + +static const AVOption ini_options[] = { + {"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 *ini_get_name(void *ctx) +{ + return "ini"; +} + +static const AVClass ini_class = { + "INIContext", + ini_get_name, + ini_options +}; + +static av_cold int ini_init(WriterContext *wctx, const char *args, void *opaque) +{ + INIContext *ini = wctx->priv; + int err; + + av_bprint_init(&ini->chapter_name, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&ini->section_name, 1, AV_BPRINT_SIZE_UNLIMITED); + ini->nb_frame = ini->nb_packet = 0; + + ini->class = &ini_class; + av_opt_set_defaults(ini); + + if (args && (err = av_set_options_string(ini, args, "=", ":")) < 0) { + av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); + return err; + } + + return 0; +} + +static av_cold void ini_uninit(WriterContext *wctx) +{ + INIContext *ini = wctx->priv; + av_bprint_finalize(&ini->chapter_name, NULL); + av_bprint_finalize(&ini->section_name, NULL); +} + +static void ini_print_header(WriterContext *wctx) +{ + printf("# ffprobe output\n\n"); +} + +static char *ini_escape_str(AVBPrint *dst, const char *src) +{ + int i = 0; + char c = 0; + + while (c = src[i++]) { + switch (c) { + case '\b': av_bprintf(dst, "%s", "\\b"); break; + case '\f': av_bprintf(dst, "%s", "\\f"); break; + case '\n': av_bprintf(dst, "%s", "\\n"); break; + case '\r': av_bprintf(dst, "%s", "\\r"); break; + case '\t': av_bprintf(dst, "%s", "\\t"); break; + case '\\': + case '#' : + case '=' : + case ':' : av_bprint_chars(dst, '\\', 1); + default: + if ((unsigned char)c < 32) + av_bprintf(dst, "\\x00%02x", c & 0xff); + else + av_bprint_chars(dst, c, 1); + break; + } + } + return dst->str; +} + +static void ini_print_chapter_header(WriterContext *wctx, const char *chapter) +{ + INIContext *ini = wctx->priv; + + av_bprint_clear(&ini->chapter_name); + av_bprintf(&ini->chapter_name, "%s", chapter); + + if (wctx->nb_chapter) + printf("\n"); + ini->print_packets_and_frames = !strcmp("packets_and_frames", chapter); +} + +static void ini_print_section_header(WriterContext *wctx, const char *section) +{ + INIContext *ini = wctx->priv; + int n; + if (wctx->nb_section) + printf("\n"); + av_bprint_clear(&ini->section_name); + + if (ini->hierarchical && wctx->multiple_sections) + av_bprintf(&ini->section_name, "%s.", ini->chapter_name.str); + av_bprintf(&ini->section_name, "%s", section); + + if (ini->print_packets_and_frames) + n = !strcmp(section, "packet") ? ini->nb_packet++ : ini->nb_frame++; + else + n = wctx->nb_section; + if (wctx->multiple_sections) + av_bprintf(&ini->section_name, ".%d", n); + printf("[%s]\n", ini->section_name.str); +} + +static void ini_print_str(WriterContext *wctx, const char *key, const char *value) +{ + AVBPrint buf; + + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + printf("%s=", ini_escape_str(&buf, key)); + av_bprint_clear(&buf); + printf("%s\n", ini_escape_str(&buf, value)); + av_bprint_finalize(&buf, NULL); +} + +static void ini_print_int(WriterContext *wctx, const char *key, long long int value) +{ + printf("%s=%lld\n", key, value); +} + +static void ini_show_tags(WriterContext *wctx, AVDictionary *dict) +{ + INIContext *ini = wctx->priv; + AVDictionaryEntry *tag = NULL; + int is_first = 1; + + while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if (is_first) { + printf("\n[%s.tags]\n", ini->section_name.str); + is_first = 0; + } + writer_print_string(wctx, tag->key, tag->value, 0); + } +} + +static const Writer ini_writer = { + .name = "ini", + .priv_size = sizeof(INIContext), + .init = ini_init, + .uninit = ini_uninit, + .print_header = ini_print_header, + .print_chapter_header = ini_print_chapter_header, + .print_section_header = ini_print_section_header, + .print_integer = ini_print_int, + .print_string = ini_print_str, + .show_tags = ini_show_tags, + .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, +}; + /* JSON output */ typedef struct { @@ -1168,6 +1336,7 @@ static void writer_register_all(void) writer_register(&default_writer); writer_register(&compact_writer); writer_register(&csv_writer); + writer_register(&ini_writer); writer_register(&json_writer); writer_register(&xml_writer); } @@ -1734,7 +1903,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, json, xml)", "format" }, + "set the output printing format (available formats are: default, compact, csv, ini, json, xml)", "format" }, { "show_error", OPT_BOOL, {(void*)&do_show_error} , "show probing error" }, { "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" }, { "show_frames", OPT_BOOL, {(void*)&do_show_frames} , "show frames info" },