You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-15 14:13:16 +02:00
ffprobe: add flat output format.
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
|
161
ffprobe.c
161
ffprobe.c
@@ -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)
|
|||||||
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[] = {
|
|||||||
{ "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" },
|
||||||
|
Reference in New Issue
Block a user