Commit fd0c83c6 authored by Clément Bœsch's avatar Clément Bœsch

ffprobe: add flat output format.

parent db839544
...@@ -269,6 +269,35 @@ CSV format. ...@@ -269,6 +269,35 @@ CSV format.
This writer is equivalent to This writer is equivalent to
@code{compact=item_sep=,:nokey=1:escape=csv}. @code{compact=item_sep=,:nokey=1:escape=csv}.
@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 the separator character is an
alphanumeric character or an underscore (see @var{sep_char} option).
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 sep_char, s
Separator character used to separate the chapter, the section name, IDs and
potential tags in the printed field key.
Default value is '.'.
@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 ini @section ini
INI format output. INI format output.
......
...@@ -727,6 +727,164 @@ static const Writer csv_writer = { ...@@ -727,6 +727,164 @@ static const Writer csv_writer = {
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS, .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 */ /* INI format output */
typedef struct { typedef struct {
...@@ -1335,6 +1493,7 @@ static void writer_register_all(void) ...@@ -1335,6 +1493,7 @@ static void writer_register_all(void)
writer_register(&default_writer); writer_register(&default_writer);
writer_register(&compact_writer); writer_register(&compact_writer);
writer_register(&csv_writer); writer_register(&csv_writer);
writer_register(&flat_writer);
writer_register(&ini_writer); writer_register(&ini_writer);
writer_register(&json_writer); writer_register(&json_writer);
writer_register(&xml_writer); writer_register(&xml_writer);
...@@ -1902,7 +2061,7 @@ static const OptionDef options[] = { ...@@ -1902,7 +2061,7 @@ static const OptionDef options[] = {
{ "pretty", 0, {(void*)&opt_pretty}, { "pretty", 0, {(void*)&opt_pretty},
"prettify the format of displayed values, make it more human readable" }, "prettify the format of displayed values, make it more human readable" },
{ "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format}, { "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" }, { "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_error", OPT_BOOL, {(void*)&do_show_error} , "show probing error" },
{ "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" }, { "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment