From fd0c83c6667de5b1ebe40f115a746fddea558168 Mon Sep 17 00:00:00 2001 From: Clément Bœsch Date: Tue, 29 May 2012 23:22:39 +0200 Subject: ffprobe: add flat output format. --- ffprobe.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) (limited to 'ffprobe.c') diff --git a/ffprobe.c b/ffprobe.c index e2a59bddff..295b815c20 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_char", "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 { @@ -1335,6 +1493,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); @@ -1902,7 +2061,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" }, -- cgit v1.2.3