ffprobe: add compact writer

pull/2/head
Stefano Sabatini 13 years ago
parent 0491a2a07a
commit 1c43713e57
  1. 1
      Changelog
  2. 52
      doc/ffprobe.texi
  3. 229
      ffprobe.c

@ -121,6 +121,7 @@ easier to use. The changes are:
- Discworld II BMV decoding support
- VBLE Decoder
- OS X Video Decoder Acceleration (VDA) support
- compact output in ffprobe
version 0.8:

@ -147,6 +147,58 @@ keyN=valN
Metadata tags are printed as a line in the corresponding FORMAT or
STREAM section, and are prefixed by the string "TAG:".
@section compact
Compact format.
Each section is printed on a single line.
If no option is specifid, the output has the form:
@example
section|key1=val1| ... |keyN=valN
@end example
Metadata tags are printed in the corresponding "format" or "stream"
section. A metadata tag key, if printed, is prefixed by the string
"tag:".
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 item_sep, s
Specify the character to use for separating fields in the output line.
It must be a single printable character, it is "|" by default.
@item nokey, nk
If set to 1 specify not to print the key of each field. Its default
value is 0.
@item escape, e
Set the escape mode to use, default to "c".
It can assume one of the following values:
@table @option
@item c
Perform C-like escaping. Strings containing a newline ('\n') or
carriage return ('\r'), the escaping character ('\') or the item
separator character @var{SEP} are escaped using C-like fashioned
escaping, so that a newline is converted to the sequence "\n", a
carriage return to "\r", '\' to "\\" and the separator @var{SEP} is
converted to "\@var{SEP}".
@item csv
Perform CSV-like escaping, as described in RFC4180. Strings
containing a newline ('\n'), a carriage return ('\r'), a double quote
('"'), or @var{SEP} are enclosed in double-quotes.
@item none
Perform no escaping.
@end table
@end table
@section json
JSON based format.

@ -462,6 +462,232 @@ static Writer default_writer = {
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
};
/* Compact output */
/**
* Escape \n, \r, \\ and sep characters contained in s, and print the
* resulting string.
*/
static const char *c_escape_str(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx)
{
const char *p;
char *q;
size_t size = 1;
/* precompute size */
for (p = src; *p; p++, size++) {
ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-2);
if (*p == '\n' || *p == '\r' || *p == '\\')
size++;
}
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
q = *dst;
for (p = src; *p; p++) {
switch (*src) {
case '\n': *q++ = '\\'; *q++ = 'n'; break;
case '\r': *q++ = '\\'; *q++ = 'r'; break;
case '\\': *q++ = '\\'; *q++ = '\\'; break;
default:
if (*p == sep)
*q++ = '\\';
*q++ = *p;
}
}
*q = 0;
return *dst;
}
/**
* Quote fields containing special characters, check RFC4180.
*/
static const char *csv_escape_str(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx)
{
const char *p;
char *q;
size_t size = 1;
int quote = 0;
/* precompute size */
for (p = src; *p; p++, size++) {
ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-4);
if (*p == '"' || *p == sep || *p == '\n' || *p == '\r')
if (!quote) {
quote = 1;
size += 2;
}
if (*p == '"')
size++;
}
ESCAPE_REALLOC_BUF(dst_size, dst, src, size);
q = *dst;
p = src;
if (quote)
*q++ = '\"';
while (*p) {
if (*p == '"')
*q++ = '\"';
*q++ = *p++;
}
if (quote)
*q++ = '\"';
*q = 0;
return *dst;
}
static const char *none_escape_str(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx)
{
return src;
}
typedef struct CompactContext {
const AVClass *class;
char *item_sep_str;
char item_sep;
int nokey;
char *buf;
size_t buf_size;
char *escape_mode_str;
const char * (*escape_str)(char **dst, size_t *dst_size,
const char *src, const char sep, void *log_ctx);
} CompactContext;
#define OFFSET(x) offsetof(CompactContext, x)
static const AVOption compact_options[]= {
{"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, CHAR_MIN, CHAR_MAX },
{"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, CHAR_MIN, CHAR_MAX },
{"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1 },
{"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, CHAR_MIN, CHAR_MAX },
{"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, CHAR_MIN, CHAR_MAX },
{NULL},
};
static const char *compact_get_name(void *ctx)
{
return "compact";
}
static const AVClass compact_class = {
"CompactContext",
compact_get_name,
compact_options
};
static av_cold int compact_init(WriterContext *wctx, const char *args, void *opaque)
{
CompactContext *compact = wctx->priv;
int err;
compact->class = &compact_class;
av_opt_set_defaults(compact);
if (args &&
(err = (av_set_options_string(compact, args, "=", ":"))) < 0) {
av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
return err;
}
if (strlen(compact->item_sep_str) != 1) {
av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
compact->item_sep_str);
return AVERROR(EINVAL);
}
compact->item_sep = compact->item_sep_str[0];
compact->buf_size = ESCAPE_INIT_BUF_SIZE;
if (!(compact->buf = av_malloc(compact->buf_size)))
return AVERROR(ENOMEM);
if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str;
else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
else {
av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
return AVERROR(EINVAL);
}
return 0;
}
static av_cold void compact_uninit(WriterContext *wctx)
{
CompactContext *compact = wctx->priv;
av_freep(&compact->item_sep_str);
av_freep(&compact->buf);
av_freep(&compact->escape_mode_str);
}
static void compact_print_section_header(WriterContext *wctx, const char *section)
{
CompactContext *compact = wctx->priv;
printf("%s%c", section, compact->item_sep);
}
static void compact_print_section_footer(WriterContext *wctx, const char *section)
{
printf("\n");
}
static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
{
CompactContext *compact = wctx->priv;
if (wctx->nb_item) printf("%c", compact->item_sep);
if (!compact->nokey)
printf("%s=", key);
printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
value, compact->item_sep, wctx));
}
static void compact_print_int(WriterContext *wctx, const char *key, int value)
{
CompactContext *compact = wctx->priv;
if (wctx->nb_item) printf("%c", compact->item_sep);
if (!compact->nokey)
printf("%s=", key);
printf("%d", value);
}
static void compact_show_tags(WriterContext *wctx, AVDictionary *dict)
{
CompactContext *compact = wctx->priv;
AVDictionaryEntry *tag = NULL;
while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) {
if (wctx->nb_item) printf("%c", compact->item_sep);
if (!compact->nokey)
printf("tag:%s=", compact->escape_str(&compact->buf, &compact->buf_size,
tag->key, compact->item_sep, wctx));
printf("%s", compact->escape_str(&compact->buf, &compact->buf_size,
tag->value, compact->item_sep, wctx));
}
}
static Writer compact_writer = {
.name = "compact",
.priv_size = sizeof(CompactContext),
.init = compact_init,
.uninit = compact_uninit,
.print_section_header = compact_print_section_header,
.print_section_footer = compact_print_section_footer,
.print_integer = compact_print_int,
.print_string = compact_print_str,
.show_tags = compact_show_tags,
.flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS
};
/* JSON output */
typedef struct {
@ -630,6 +856,7 @@ static void writer_register_all(void)
initialized = 1;
writer_register(&default_writer);
writer_register(&compact_writer);
writer_register(&json_writer);
}
@ -976,7 +1203,7 @@ static const OptionDef options[] = {
"use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" },
{ "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, json)", "format" },
{ "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format}, "set the output printing format (available formats are: default, compact, json)", "format" },
{ "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
{ "show_packets", OPT_BOOL, {(void*)&do_show_packets}, "show packets info" },
{ "show_streams", OPT_BOOL, {(void*)&do_show_streams}, "show streams info" },

Loading…
Cancel
Save