ffprobe: add INI writer

Liberally based on the work of Luca Barbato <lu_zero@gentoo.org>, done
for libav/avprobe.
This commit is contained in:
Stefano Sabatini 2012-05-27 01:24:19 +02:00
parent 908c045f3a
commit 89d49acb3b
3 changed files with 211 additions and 1 deletions

View File

@ -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,

View File

@ -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.

171
ffprobe.c
View File

@ -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" },