[FFmpeg-devel] [PATCH 4/4] ffprobe: add flat output format.
Stefano Sabatini
stefasab at gmail.com
Thu May 31 01:14:17 CEST 2012
On date Tuesday 2012-05-29 23:24:42 +0200, Clément Bœsch encoded:
> ---
> doc/ffprobe.texi | 24 ++++++++
> ffprobe.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 186 insertions(+), 1 deletion(-)
>
> diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi
> index a725c37..e1c64ae 100644
> --- a/doc/ffprobe.texi
> +++ b/doc/ffprobe.texi
> @@ -269,6 +269,30 @@ CSV format.
> This writer is equivalent to
> @code{compact=item_sep=,:nokey=1:escape=csv}.
>
> + at section flat
> +Flat format.
> +
> +A free-form output with each line contains an explicit key=value, such as
> +"streams.stream.3.tags.foo=bar"
Mention that the output is sh-escaped, so can be directly embedded in
sh scripts (if using a valid separator).
> +
> +This writer accepts options as a list of @var{key}=@var{value} pairs,
> +separated by ":".
Reminder: we should make the interface consistent with all writers,
and maybe factorize some init code.
> +
> +The description of the accepted options follows.
> +
> + at table @option
> + at item chr_sep, c
Nit: sep_char, s? (also, consistent with compact writer)
Random abbreviations are usually confusing.
> +Separator used between chapters/section/etc. Default is '.'.
"chapter"
Also maybe more clear:
"Separator character used to separate the chapter and the section name
in the printed field key."
> +
> + at 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.
> + at end table
> +
> @section ini
> INI format output.
>
> diff --git a/ffprobe.c b/ffprobe.c
> index 27ab62e..c64a687 100644
> --- a/ffprobe.c
> +++ b/ffprobe.c
> @@ -727,6 +727,166 @@ 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;
> + char chr_sep;
nit: sep_str, sep, should be more consistent (e.g. check
CompactContext), and I wonder if you can directly use the string.
> + int hierarchical;
> +} FlatContext;
> +
> +#undef OFFSET
> +#define OFFSET(x) offsetof(FlatContext, x)
> +
> +static const AVOption flat_options[]= {
> + {"chr_sep", "set separator", OFFSET(sep), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX },
> + {"c", "set separator", OFFSET(sep), 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) != 1) {
> + av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
> + flat->sep);
> + return AVERROR(EINVAL);
> + }
> + flat->chr_sep = flat->sep[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') ||
> + *p == sep))
> + 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;
> + case '\'': av_bprintf(dst, "%s", "\\'"); break;
> + default: av_bprint_chars(dst, *p, 1); break;
> + }
> + }
> + return dst->str;
> +}
> +
> +static void flat_print_section(WriterContext *wctx)
> +{
> + int n = wctx->is_packets_and_frames ? wctx->nb_section_packet_frame
> + : wctx->nb_section;
> + FlatContext *flat = wctx->priv;
nit+++: private context is usually the first item in the declaration
section
> +
> + if (flat->hierarchical && wctx->multiple_sections)
> + printf("%s%c", flat->chapter, flat->chr_sep);
> + printf("%s%c", flat->section, flat->chr_sep);
> + if (wctx->multiple_sections)
> + printf("%d%c", n, flat->chr_sep);
> +}
Would it make sense to use '_' like default separator character? This
way you have sh code output by default.
Also: you could cache the section name in the private context, like it
is done in the INI writer.
> +
> +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;
> +}
nit: please declare these in order of printing, seems more
logical/easier to read
[...]
--
FFmpeg = Fancy & Frightening Maxi Pure Earthshaking Geek
More information about the ffmpeg-devel
mailing list