* [FFmpeg-devel] [PATCH v3 1/7] fftools/textformat: Extract and generalize textformat api from ffprobe.c
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
@ 2025-03-01 10:01 ` softworkz
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 2/7] fftools/ffprobe: Change to use textformat api softworkz
` (5 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:01 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
Signed-off-by: softworkz <softworkz@hotmail.com>
---
fftools/textformat/avtextformat.c | 671 +++++++++++++++++++++++++++++
fftools/textformat/avtextformat.h | 171 ++++++++
fftools/textformat/avtextwriters.h | 68 +++
fftools/textformat/tf_compact.c | 282 ++++++++++++
fftools/textformat/tf_default.c | 145 +++++++
fftools/textformat/tf_flat.c | 174 ++++++++
fftools/textformat/tf_ini.c | 160 +++++++
fftools/textformat/tf_json.c | 215 +++++++++
fftools/textformat/tf_xml.c | 221 ++++++++++
fftools/textformat/tw_avio.c | 129 ++++++
fftools/textformat/tw_buffer.c | 92 ++++
fftools/textformat/tw_stdout.c | 82 ++++
12 files changed, 2410 insertions(+)
create mode 100644 fftools/textformat/avtextformat.c
create mode 100644 fftools/textformat/avtextformat.h
create mode 100644 fftools/textformat/avtextwriters.h
create mode 100644 fftools/textformat/tf_compact.c
create mode 100644 fftools/textformat/tf_default.c
create mode 100644 fftools/textformat/tf_flat.c
create mode 100644 fftools/textformat/tf_ini.c
create mode 100644 fftools/textformat/tf_json.c
create mode 100644 fftools/textformat/tf_xml.c
create mode 100644 fftools/textformat/tw_avio.c
create mode 100644 fftools/textformat/tw_buffer.c
create mode 100644 fftools/textformat/tw_stdout.c
diff --git a/fftools/textformat/avtextformat.c b/fftools/textformat/avtextformat.c
new file mode 100644
index 0000000000..1fba78b103
--- /dev/null
+++ b/fftools/textformat/avtextformat.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libavutil/mem.h"
+#include "libavutil/avassert.h"
+#include "libavutil/bprint.h"
+#include "libavutil/error.h"
+#include "libavutil/hash.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/macros.h"
+#include "libavutil/opt.h"
+#include "avtextformat.h"
+
+#define SECTION_ID_NONE -1
+
+#define SHOW_OPTIONAL_FIELDS_AUTO -1
+#define SHOW_OPTIONAL_FIELDS_NEVER 0
+#define SHOW_OPTIONAL_FIELDS_ALWAYS 1
+
+static const struct {
+ double bin_val;
+ double dec_val;
+ const char *bin_str;
+ const char *dec_str;
+} si_prefixes[] = {
+ { 1.0, 1.0, "", "" },
+ { 1.024e3, 1e3, "Ki", "K" },
+ { 1.048576e6, 1e6, "Mi", "M" },
+ { 1.073741824e9, 1e9, "Gi", "G" },
+ { 1.099511627776e12, 1e12, "Ti", "T" },
+ { 1.125899906842624e15, 1e15, "Pi", "P" },
+};
+
+static const char *avtext_context_get_formatter_name(void *p)
+{
+ AVTextFormatContext *tctx = p;
+ return tctx->formatter->name;
+}
+
+#define OFFSET(x) offsetof(AVTextFormatContext, x)
+
+static const AVOption textcontext_options[] = {
+ { "string_validation", "set string validation mode",
+ OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=AV_TEXTFORMAT_STRING_VALIDATION_REPLACE}, 0, AV_TEXTFORMAT_STRING_VALIDATION_NB-1, .unit = "sv" },
+ { "sv", "set string validation mode",
+ OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=AV_TEXTFORMAT_STRING_VALIDATION_REPLACE}, 0, AV_TEXTFORMAT_STRING_VALIDATION_NB-1, .unit = "sv" },
+ { "ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_TEXTFORMAT_STRING_VALIDATION_IGNORE}, .unit = "sv" },
+ { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_TEXTFORMAT_STRING_VALIDATION_REPLACE}, .unit = "sv" },
+ { "fail", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_TEXTFORMAT_STRING_VALIDATION_FAIL}, .unit = "sv" },
+ { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}},
+ { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}},
+ { NULL }
+};
+
+static void *trextcontext_child_next(void *obj, void *prev)
+{
+ AVTextFormatContext *ctx = obj;
+ if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv)
+ return ctx->priv;
+ return NULL;
+}
+
+static const AVClass textcontext_class = {
+ .class_name = "AVTextContext",
+ .item_name = avtext_context_get_formatter_name,
+ .option = textcontext_options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .child_next = trextcontext_child_next,
+};
+
+static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
+{
+ int i;
+ av_bprintf(bp, "0X");
+ for (i = 0; i < ubuf_size; i++)
+ av_bprintf(bp, "%02X", ubuf[i]);
+}
+
+int avtext_context_close(AVTextFormatContext **ptctx)
+{
+ AVTextFormatContext *tctx = *ptctx;
+ int i;
+ int ret = 0;
+
+ if (!tctx)
+ return -1;
+
+ av_hash_freep(&tctx->hash);
+
+ av_hash_freep(&tctx->hash);
+
+ if (tctx->formatter->uninit)
+ tctx->formatter->uninit(tctx);
+ for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
+ av_bprint_finalize(&tctx->section_pbuf[i], NULL);
+ if (tctx->formatter->priv_class)
+ av_opt_free(tctx->priv);
+ av_freep(&tctx->priv);
+ av_opt_free(tctx);
+ av_freep(ptctx);
+ return ret;
+}
+
+
+int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args,
+ const struct AVTextFormatSection *sections, int nb_sections,
+ int show_value_unit,
+ int use_value_prefix,
+ int use_byte_value_binary_prefix,
+ int use_value_sexagesimal_format,
+ int show_optional_fields,
+ char *show_data_hash)
+{
+ AVTextFormatContext *tctx;
+ int i, ret = 0;
+
+ if (!(tctx = av_mallocz(sizeof(AVTextFormatContext)))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (!(tctx->priv = av_mallocz(formatter->priv_size))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ tctx->show_value_unit = show_value_unit;
+ tctx->use_value_prefix = use_value_prefix;
+ tctx->use_byte_value_binary_prefix = use_byte_value_binary_prefix;
+ tctx->use_value_sexagesimal_format = use_value_sexagesimal_format;
+ tctx->show_optional_fields = show_optional_fields;
+
+ if (nb_sections > SECTION_MAX_NB_SECTIONS) {
+ av_log(tctx, AV_LOG_ERROR, "The number of section definitions (%d) is larger than the maximum allowed (%d)\n", nb_sections, SECTION_MAX_NB_SECTIONS);
+ goto fail;
+ }
+
+ tctx->class = &textcontext_class;
+ tctx->formatter = formatter;
+ tctx->level = -1;
+ tctx->sections = sections;
+ tctx->nb_sections = nb_sections;
+ tctx->writer = writer_context;
+
+ av_opt_set_defaults(tctx);
+
+ if (formatter->priv_class) {
+ void *priv_ctx = tctx->priv;
+ *(const AVClass **)priv_ctx = formatter->priv_class;
+ av_opt_set_defaults(priv_ctx);
+ }
+
+ /* convert options to dictionary */
+ if (args) {
+ AVDictionary *opts = NULL;
+ const AVDictionaryEntry *opt = NULL;
+
+ if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
+ av_log(tctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to textformat context\n", args);
+ av_dict_free(&opts);
+ goto fail;
+ }
+
+ while ((opt = av_dict_iterate(opts, opt))) {
+ if ((ret = av_opt_set(tctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
+ av_log(tctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to textformat context\n",
+ opt->key, opt->value);
+ av_dict_free(&opts);
+ goto fail;
+ }
+ }
+
+ av_dict_free(&opts);
+ }
+
+ if (show_data_hash) {
+ if ((ret = av_hash_alloc(&tctx->hash, show_data_hash)) < 0) {
+ if (ret == AVERROR(EINVAL)) {
+ const char *n;
+ av_log(NULL, AV_LOG_ERROR, "Unknown hash algorithm '%s'\nKnown algorithms:", show_data_hash);
+ for (i = 0; (n = av_hash_names(i)); i++)
+ av_log(NULL, AV_LOG_ERROR, " %s", n);
+ av_log(NULL, AV_LOG_ERROR, "\n");
+ }
+ return ret;
+ }
+ }
+
+ /* validate replace string */
+ {
+ const uint8_t *p = tctx->string_validation_replacement;
+ const uint8_t *endp = p + strlen(p);
+ while (*p) {
+ const uint8_t *p0 = p;
+ int32_t code;
+ ret = av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags);
+ if (ret < 0) {
+ AVBPrint bp;
+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
+ bprint_bytes(&bp, p0, p-p0),
+ av_log(tctx, AV_LOG_ERROR,
+ "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
+ bp.str, tctx->string_validation_replacement);
+ return ret;
+ }
+ }
+ }
+
+ for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
+ av_bprint_init(&tctx->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
+
+ if (tctx->formatter->init)
+ ret = tctx->formatter->init(tctx);
+ if (ret < 0)
+ goto fail;
+
+ *ptctx = tctx;
+
+ return 0;
+
+fail:
+ avtext_context_close(&tctx);
+ return ret;
+}
+
+/* Temporary definitions during refactoring */
+static const char unit_second_str[] = "s" ;
+static const char unit_hertz_str[] = "Hz" ;
+static const char unit_byte_str[] = "byte" ;
+static const char unit_bit_per_second_str[] = "bit/s";
+
+
+void avtext_print_section_header(AVTextFormatContext *tctx,
+ const void *data,
+ int section_id)
+{
+ tctx->level++;
+ av_assert0(tctx->level < SECTION_MAX_NB_LEVELS);
+
+ tctx->nb_item[tctx->level] = 0;
+ memset(tctx->nb_item_type[tctx->level], 0, sizeof(tctx->nb_item_type[tctx->level]));
+ tctx->section[tctx->level] = &tctx->sections[section_id];
+
+ if (tctx->formatter->print_section_header)
+ tctx->formatter->print_section_header(tctx, data);
+}
+
+void avtext_print_section_footer(AVTextFormatContext *tctx)
+{
+ int section_id = tctx->section[tctx->level]->id;
+ int parent_section_id = tctx->level ?
+ tctx->section[tctx->level-1]->id : SECTION_ID_NONE;
+
+ if (parent_section_id != SECTION_ID_NONE) {
+ tctx->nb_item[tctx->level - 1]++;
+ tctx->nb_item_type[tctx->level - 1][section_id]++;
+ }
+
+ if (tctx->formatter->print_section_footer)
+ tctx->formatter->print_section_footer(tctx);
+ tctx->level--;
+}
+
+void avtext_print_integer(AVTextFormatContext *tctx,
+ const char *key, int64_t val)
+{
+ const struct AVTextFormatSection *section = tctx->section[tctx->level];
+
+ if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
+ tctx->formatter->print_integer(tctx, key, val);
+ tctx->nb_item[tctx->level]++;
+ }
+}
+
+static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const char *src)
+{
+ const uint8_t *p, *endp;
+ AVBPrint dstbuf;
+ int invalid_chars_nb = 0, ret = 0;
+
+ av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ endp = src + strlen(src);
+ for (p = src; *p;) {
+ uint32_t code;
+ int invalid = 0;
+ const uint8_t *p0 = p;
+
+ if (av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags) < 0) {
+ AVBPrint bp;
+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
+ bprint_bytes(&bp, p0, p-p0);
+ av_log(tctx, AV_LOG_DEBUG,
+ "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
+ invalid = 1;
+ }
+
+ if (invalid) {
+ invalid_chars_nb++;
+
+ switch (tctx->string_validation) {
+ case AV_TEXTFORMAT_STRING_VALIDATION_FAIL:
+ av_log(tctx, AV_LOG_ERROR,
+ "Invalid UTF-8 sequence found in string '%s'\n", src);
+ ret = AVERROR_INVALIDDATA;
+ goto end;
+ break;
+
+ case AV_TEXTFORMAT_STRING_VALIDATION_REPLACE:
+ av_bprintf(&dstbuf, "%s", tctx->string_validation_replacement);
+ break;
+ }
+ }
+
+ if (!invalid || tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_IGNORE)
+ av_bprint_append_data(&dstbuf, p0, p-p0);
+ }
+
+ if (invalid_chars_nb && tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_REPLACE) {
+ av_log(tctx, AV_LOG_WARNING,
+ "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n",
+ invalid_chars_nb, src, tctx->string_validation_replacement);
+ }
+
+end:
+ av_bprint_finalize(&dstbuf, dstp);
+ return ret;
+}
+
+struct unit_value {
+ union { double d; int64_t i; } val;
+ const char *unit;
+};
+
+static char *value_string(AVTextFormatContext *tctx, char *buf, int buf_size, struct unit_value uv)
+{
+ double vald;
+ int64_t vali;
+ int show_float = 0;
+
+ if (uv.unit == unit_second_str) {
+ vald = uv.val.d;
+ show_float = 1;
+ } else {
+ vald = vali = uv.val.i;
+ }
+
+ if (uv.unit == unit_second_str && tctx->use_value_sexagesimal_format) {
+ double secs;
+ int hours, mins;
+ secs = vald;
+ mins = (int)secs / 60;
+ secs = secs - mins * 60;
+ hours = mins / 60;
+ mins %= 60;
+ snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
+ } else {
+ const char *prefix_string = "";
+
+ if (tctx->use_value_prefix && vald > 1) {
+ int64_t index;
+
+ if (uv.unit == unit_byte_str && tctx->use_byte_value_binary_prefix) {
+ index = (int64_t) (log2(vald)) / 10;
+ index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
+ vald /= si_prefixes[index].bin_val;
+ prefix_string = si_prefixes[index].bin_str;
+ } else {
+ index = (int64_t) (log10(vald)) / 3;
+ index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
+ vald /= si_prefixes[index].dec_val;
+ prefix_string = si_prefixes[index].dec_str;
+ }
+ vali = vald;
+ }
+
+ if (show_float || (tctx->use_value_prefix && vald != (int64_t)vald))
+ snprintf(buf, buf_size, "%f", vald);
+ else
+ snprintf(buf, buf_size, "%"PRId64, vali);
+ av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || tctx->show_value_unit ? " " : "",
+ prefix_string, tctx->show_value_unit ? uv.unit : "");
+ }
+
+ return buf;
+}
+
+
+void avtext_print_unit_int(AVTextFormatContext *tctx, const char *key, int value, const char *unit)
+{
+ char val_str[128];
+ struct unit_value uv;
+ uv.val.i = value;
+ uv.unit = unit;
+ avtext_print_string(tctx, key, value_string(tctx, val_str, sizeof(val_str), uv), 0);
+}
+
+
+int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags)
+{
+ const struct AVTextFormatSection *section = tctx->section[tctx->level];
+ int ret = 0;
+
+ if (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
+ (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
+ && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
+ && !(tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS)))
+ return 0;
+
+ if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
+ if (flags & AV_TEXTFORMAT_PRINT_STRING_VALIDATE) {
+ char *key1 = NULL, *val1 = NULL;
+ ret = validate_string(tctx, &key1, key);
+ if (ret < 0) goto end;
+ ret = validate_string(tctx, &val1, val);
+ if (ret < 0) goto end;
+ tctx->formatter->print_string(tctx, key1, val1);
+ end:
+ if (ret < 0) {
+ av_log(tctx, AV_LOG_ERROR,
+ "Invalid key=value string combination %s=%s in section %s\n",
+ key, val, section->unique_name);
+ }
+ av_free(key1);
+ av_free(val1);
+ } else {
+ tctx->formatter->print_string(tctx, key, val);
+ }
+
+ tctx->nb_item[tctx->level]++;
+ }
+
+ return ret;
+}
+
+void avtext_print_rational(AVTextFormatContext *tctx,
+ const char *key, AVRational q, char sep)
+{
+ AVBPrint buf;
+ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
+ av_bprintf(&buf, "%d%c%d", q.num, sep, q.den);
+ avtext_print_string(tctx, key, buf.str, 0);
+}
+
+void avtext_print_time(AVTextFormatContext *tctx, const char *key,
+ int64_t ts, const AVRational *time_base, int is_duration)
+{
+ char buf[128];
+
+ if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
+ avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
+ } else {
+ double d = ts * av_q2d(*time_base);
+ struct unit_value uv;
+ uv.val.d = d;
+ uv.unit = unit_second_str;
+ value_string(tctx, buf, sizeof(buf), uv);
+ avtext_print_string(tctx, key, buf, 0);
+ }
+}
+
+void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration)
+{
+ if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
+ avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
+ } else {
+ avtext_print_integer(tctx, key, ts);
+ }
+}
+
+void avtext_print_data(AVTextFormatContext *tctx, const char *name,
+ const uint8_t *data, int size)
+{
+ AVBPrint bp;
+ int offset = 0, l, i;
+
+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprintf(&bp, "\n");
+ while (size) {
+ av_bprintf(&bp, "%08x: ", offset);
+ l = FFMIN(size, 16);
+ for (i = 0; i < l; i++) {
+ av_bprintf(&bp, "%02x", data[i]);
+ if (i & 1)
+ av_bprintf(&bp, " ");
+ }
+ av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
+ for (i = 0; i < l; i++)
+ av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
+ av_bprintf(&bp, "\n");
+ offset += l;
+ data += l;
+ size -= l;
+ }
+ avtext_print_string(tctx, name, bp.str, 0);
+ av_bprint_finalize(&bp, NULL);
+}
+
+void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name,
+ const uint8_t *data, int size)
+{
+ char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
+
+ if (!tctx->hash)
+ return;
+ av_hash_init(tctx->hash);
+ av_hash_update(tctx->hash, data, size);
+ snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(tctx->hash));
+ p = buf + strlen(buf);
+ av_hash_final_hex(tctx->hash, p, buf + sizeof(buf) - p);
+ avtext_print_string(tctx, name, buf, 0);
+}
+
+void avtext_print_integers(AVTextFormatContext *tctx, const char *name,
+ uint8_t *data, int size, const char *format,
+ int columns, int bytes, int offset_add)
+{
+ AVBPrint bp;
+ int offset = 0, l, i;
+
+ av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprintf(&bp, "\n");
+ while (size) {
+ av_bprintf(&bp, "%08x: ", offset);
+ l = FFMIN(size, columns);
+ for (i = 0; i < l; i++) {
+ if (bytes == 1) av_bprintf(&bp, format, *data);
+ else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data));
+ else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data));
+ data += bytes;
+ size --;
+ }
+ av_bprintf(&bp, "\n");
+ offset += offset_add;
+ }
+ avtext_print_string(tctx, name, bp.str, 0);
+ av_bprint_finalize(&bp, NULL);
+}
+
+static const char *avtextwriter_context_get_writer_name(void *p)
+{
+ AVTextWriterContext *wctx = p;
+ return wctx->writer->name;
+}
+
+static void *writercontext_child_next(void *obj, void *prev)
+{
+ AVTextFormatContext *ctx = obj;
+ if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv)
+ return ctx->priv;
+ return NULL;
+}
+
+static const AVClass textwriter_class = {
+ .class_name = "AVTextWriterContext",
+ .item_name = avtextwriter_context_get_writer_name,
+ .version = LIBAVUTIL_VERSION_INT,
+ .child_next = writercontext_child_next,
+};
+
+
+int avtextwriter_context_close(AVTextWriterContext **pwctx)
+{
+ AVTextWriterContext *wctx = *pwctx;
+ int ret = 0;
+
+ if (!wctx)
+ return -1;
+
+ if (wctx->writer->uninit)
+ wctx->writer->uninit(wctx);
+ if (wctx->writer->priv_class)
+ av_opt_free(wctx->priv);
+ av_freep(&wctx->priv);
+ av_freep(pwctx);
+ return ret;
+}
+
+
+int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer)
+{
+ AVTextWriterContext *wctx;
+ int ret = 0;
+
+ if (!(wctx = av_mallocz(sizeof(AVTextWriterContext)))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (!(wctx->priv = av_mallocz(writer->priv_size))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (writer->priv_class) {
+ void *priv_ctx = wctx->priv;
+ *(const AVClass **)priv_ctx = writer->priv_class;
+ av_opt_set_defaults(priv_ctx);
+ }
+
+ wctx->class = &textwriter_class;
+ wctx->writer = writer;
+
+ av_opt_set_defaults(wctx);
+
+
+ if (wctx->writer->init)
+ ret = wctx->writer->init(wctx);
+ if (ret < 0)
+ goto fail;
+
+ *pwctx = wctx;
+
+ return 0;
+
+fail:
+ avtextwriter_context_close(&wctx);
+ return ret;
+}
+
+static const AVTextFormatter *registered_formatters[7+1];
+static void formatters_register_all(void)
+{
+ static int initialized;
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ registered_formatters[0] = &avtextformatter_default;
+ registered_formatters[1] = &avtextformatter_compact;
+ registered_formatters[2] = &avtextformatter_csv;
+ registered_formatters[3] = &avtextformatter_flat;
+ registered_formatters[4] = &avtextformatter_ini;
+ registered_formatters[5] = &avtextformatter_json;
+ registered_formatters[6] = &avtextformatter_xml;
+}
+
+const AVTextFormatter *avtext_get_formatter_by_name(const char *name)
+{
+ formatters_register_all();
+
+ for (int i = 0; registered_formatters[i]; i++)
+ if (!strcmp(registered_formatters[i]->name, name))
+ return registered_formatters[i];
+
+ return NULL;
+}
diff --git a/fftools/textformat/avtextformat.h b/fftools/textformat/avtextformat.h
new file mode 100644
index 0000000000..b7b6e0eea0
--- /dev/null
+++ b/fftools/textformat/avtextformat.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H
+#define FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "libavutil/attributes.h"
+#include "libavutil/dict.h"
+#include "libavformat/avio.h"
+#include "libavutil/bprint.h"
+#include "libavutil/rational.h"
+#include "libavutil/hash.h"
+#include "avtextwriters.h"
+
+#define SECTION_MAX_NB_CHILDREN 11
+
+
+struct AVTextFormatSection {
+ int id; ///< unique id identifying a section
+ const char *name;
+
+#define AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level
+#define AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type
+#define AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
+ /// For these sections the element_name field is mandatory.
+#define AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE 8 ///< the section contains a type to distinguish multiple nested elements
+#define AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE 16 ///< the items in this array section should be numbered individually by type
+
+ int flags;
+ const int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
+ const char *element_name; ///< name of the contained element, if provided
+ const char *unique_name; ///< unique section name, in case the name is ambiguous
+ AVDictionary *entries_to_show;
+ const char *(* get_type)(const void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined
+ int show_all_entries;
+} AVTextFormatSection;
+
+typedef struct AVTextFormatContext AVTextFormatContext;
+
+#define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS 1
+#define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2
+
+typedef enum {
+ AV_TEXTFORMAT_STRING_VALIDATION_FAIL,
+ AV_TEXTFORMAT_STRING_VALIDATION_REPLACE,
+ AV_TEXTFORMAT_STRING_VALIDATION_IGNORE,
+ AV_TEXTFORMAT_STRING_VALIDATION_NB
+} StringValidation;
+
+typedef struct AVTextFormatter {
+ const AVClass *priv_class; ///< private class of the formatter, if any
+ int priv_size; ///< private size for the formatter context
+ const char *name;
+
+ int (*init) (AVTextFormatContext *tctx);
+ void (*uninit)(AVTextFormatContext *tctx);
+
+ void (*print_section_header)(AVTextFormatContext *tctx, const void *data);
+ void (*print_section_footer)(AVTextFormatContext *tctx);
+ void (*print_integer) (AVTextFormatContext *tctx, const char *, int64_t);
+ void (*print_rational) (AVTextFormatContext *tctx, AVRational *q, char *sep);
+ void (*print_string) (AVTextFormatContext *tctx, const char *, const char *);
+ int flags; ///< a combination or AV_TEXTFORMAT__FLAG_*
+} AVTextFormatter;
+
+#define SECTION_MAX_NB_LEVELS 12
+#define SECTION_MAX_NB_SECTIONS 100
+
+struct AVTextFormatContext {
+ const AVClass *class; ///< class of the formatter
+ const AVTextFormatter *formatter; ///< the AVTextFormatter of which this is an instance
+ AVTextWriterContext *writer; ///< the AVTextWriterContext
+
+ char *name; ///< name of this formatter instance
+ void *priv; ///< private data for use by the filter
+
+ const struct AVTextFormatSection *sections; ///< array containing all sections
+ int nb_sections; ///< number of sections
+
+ int level; ///< current level, starting from 0
+
+ /** number of the item printed in the given section, starting from 0 */
+ unsigned int nb_item[SECTION_MAX_NB_LEVELS];
+ unsigned int nb_item_type[SECTION_MAX_NB_LEVELS][SECTION_MAX_NB_SECTIONS];
+
+ /** section per each level */
+ const struct AVTextFormatSection *section[SECTION_MAX_NB_LEVELS];
+ AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
+ /// used by various formatters
+
+ int show_optional_fields;
+ int show_value_unit;
+ int use_value_prefix;
+ int use_byte_value_binary_prefix;
+ int use_value_sexagesimal_format;
+
+ struct AVHashContext *hash;
+
+ int string_validation;
+ char *string_validation_replacement;
+ unsigned int string_validation_utf8_flags;
+};
+
+#define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1
+#define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2
+
+int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer, const char *args,
+ const struct AVTextFormatSection *sections, int nb_sections,
+ int show_value_unit,
+ int use_value_prefix,
+ int use_byte_value_binary_prefix,
+ int use_value_sexagesimal_format,
+ int show_optional_fields,
+ char *show_data_hash);
+
+int avtext_context_close(AVTextFormatContext **tctx);
+
+
+void avtext_print_section_header(AVTextFormatContext *tctx, const void *data, int section_id);
+
+void avtext_print_section_footer(AVTextFormatContext *tctx);
+
+void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t val);
+
+int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags);
+
+void avtext_print_unit_int(AVTextFormatContext *tctx, const char *key, int value, const char *unit);
+
+void avtext_print_rational(AVTextFormatContext *tctx, const char *key, AVRational q, char sep);
+
+void avtext_print_time(AVTextFormatContext *tctx, const char *key, int64_t ts, const AVRational *time_base, int is_duration);
+
+void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration);
+
+void avtext_print_data(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size);
+
+void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size);
+
+void avtext_print_integers(AVTextFormatContext *tctx, const char *name, uint8_t *data, int size,
+ const char *format, int columns, int bytes, int offset_add);
+
+const AVTextFormatter *avtext_get_formatter_by_name(const char *name);
+
+extern const AVTextFormatter avtextformatter_default;
+extern const AVTextFormatter avtextformatter_compact;
+extern const AVTextFormatter avtextformatter_csv;
+extern const AVTextFormatter avtextformatter_flat;
+extern const AVTextFormatter avtextformatter_ini;
+extern const AVTextFormatter avtextformatter_json;
+extern const AVTextFormatter avtextformatter_xml;
+
+#endif /* FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H */
diff --git a/fftools/textformat/avtextwriters.h b/fftools/textformat/avtextwriters.h
new file mode 100644
index 0000000000..b344881d05
--- /dev/null
+++ b/fftools/textformat/avtextwriters.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H
+#define FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "libavutil/attributes.h"
+#include "libavutil/dict.h"
+#include "libavformat/avio.h"
+#include "libavutil/bprint.h"
+#include "libavutil/rational.h"
+#include "libavutil/hash.h"
+
+typedef struct AVTextWriterContext AVTextWriterContext;
+
+typedef struct AVTextWriter {
+ const AVClass *priv_class; ///< private class of the writer, if any
+ int priv_size; ///< private size for the writer private class
+ const char *name;
+
+ int (* init)(AVTextWriterContext *wctx);
+ void (* uninit)(AVTextWriterContext *wctx);
+ void (* writer_w8)(AVTextWriterContext *wctx, int b);
+ void (* writer_put_str)(AVTextWriterContext *wctx, const char *str);
+ void (* writer_printf)(AVTextWriterContext *wctx, const char *fmt, ...);
+} AVTextWriter;
+
+typedef struct AVTextWriterContext {
+ const AVClass *class; ///< class of the writer
+ const AVTextWriter *writer;
+ const char *name;
+ void *priv; ///< private data for use by the writer
+
+} AVTextWriterContext;
+
+
+int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer);
+
+int avtextwriter_context_close(AVTextWriterContext **pwctx);
+
+int avtextwriter_create_stdout(AVTextWriterContext **pwctx);
+
+int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit);
+
+int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit);
+
+int avtextwriter_create_buffer(AVTextWriterContext **pwctx, AVBPrint *buffer);
+
+#endif /* FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H */
diff --git a/fftools/textformat/tf_compact.c b/fftools/textformat/tf_compact.c
new file mode 100644
index 0000000000..ad07ca4bd0
--- /dev/null
+++ b/fftools/textformat/tf_compact.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextformat.h"
+#include <libavutil/mem.h>
+#include <libavutil/avassert.h>
+#include <libavutil/bprint.h>
+#include <libavutil/error.h>
+#include <libavutil/macros.h>
+#include <libavutil/opt.h>
+
+
+#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
+#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
+#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
+
+
+#define DEFINE_FORMATTER_CLASS(name) \
+static const char *name##_get_name(void *ctx) \
+{ \
+ return #name ; \
+} \
+static const AVClass name##_class = { \
+ .class_name = #name, \
+ .item_name = name##_get_name, \
+ .option = name##_options \
+}
+
+
+/* Compact output */
+
+/**
+ * Apply C-language-like string escaping.
+ */
+static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
+{
+ const char *p;
+
+ for (p = src; *p; p++) {
+ switch (*p) {
+ 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 '\\': av_bprintf(dst, "%s", "\\\\"); break;
+ default:
+ if (*p == sep)
+ av_bprint_chars(dst, '\\', 1);
+ av_bprint_chars(dst, *p, 1);
+ }
+ }
+ return dst->str;
+}
+
+/**
+ * Quote fields containing special characters, check RFC4180.
+ */
+static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
+{
+ char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
+ int needs_quoting = !!src[strcspn(src, meta_chars)];
+
+ if (needs_quoting)
+ av_bprint_chars(dst, '"', 1);
+
+ for (; *src; src++) {
+ if (*src == '"')
+ av_bprint_chars(dst, '"', 1);
+ av_bprint_chars(dst, *src, 1);
+ }
+ if (needs_quoting)
+ av_bprint_chars(dst, '"', 1);
+ return dst->str;
+}
+
+static const char *none_escape_str(AVBPrint *dst, 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;
+ int print_section;
+ char *escape_mode_str;
+ const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
+ int nested_section[SECTION_MAX_NB_LEVELS];
+ int has_nested_elems[SECTION_MAX_NB_LEVELS];
+ int terminate_line[SECTION_MAX_NB_LEVELS];
+} CompactContext;
+
+#undef OFFSET
+#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="|"}, 0, 0 },
+ {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 },
+ {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 },
+ {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 },
+ {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {NULL},
+};
+
+DEFINE_FORMATTER_CLASS(compact);
+
+static av_cold int compact_init(AVTextFormatContext *wctx)
+{
+ CompactContext *compact = wctx->priv;
+
+ 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];
+
+ 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 void compact_print_section_header(AVTextFormatContext *wctx, const void *data)
+{
+ CompactContext *compact = wctx->priv;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+ compact->terminate_line[wctx->level] = 1;
+ compact->has_nested_elems[wctx->level] = 0;
+
+ av_bprint_clear(&wctx->section_pbuf[wctx->level]);
+ if (parent_section &&
+ (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE ||
+ (!(section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) &&
+ !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))))) {
+
+ /* define a prefix for elements not contained in an array or
+ in a wrapper, or for array elements with a type */
+ const char *element_name = (char *)av_x_if_null(section->element_name, section->name);
+ AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level];
+
+ compact->nested_section[wctx->level] = 1;
+ compact->has_nested_elems[wctx->level-1] = 1;
+
+ av_bprintf(section_pbuf, "%s%s",
+ wctx->section_pbuf[wctx->level-1].str, element_name);
+
+ if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) {
+ // add /TYPE to prefix
+ av_bprint_chars(section_pbuf, '/', 1);
+
+ // normalize section type, replace special characters and lower case
+ for (const char *p = section->get_type(data); *p; p++) {
+ char c =
+ (*p >= '0' && *p <= '9') ||
+ (*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_';
+ av_bprint_chars(section_pbuf, c, 1);
+ }
+ }
+ av_bprint_chars(section_pbuf, ':', 1);
+
+ wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
+ } else {
+ if (parent_section && !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) &&
+ wctx->level && wctx->nb_item[wctx->level-1])
+ writer_w8(wctx, compact->item_sep);
+ if (compact->print_section &&
+ !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
+ writer_printf(wctx, "%s%c", section->name, compact->item_sep);
+ }
+}
+
+static void compact_print_section_footer(AVTextFormatContext *wctx)
+{
+ CompactContext *compact = wctx->priv;
+
+ if (!compact->nested_section[wctx->level] &&
+ compact->terminate_line[wctx->level] &&
+ !(wctx->section[wctx->level]->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
+ writer_w8(wctx, '\n');
+}
+
+static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
+{
+ CompactContext *compact = wctx->priv;
+ AVBPrint buf;
+
+ if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
+ if (!compact->nokey)
+ writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx));
+ av_bprint_finalize(&buf, NULL);
+}
+
+static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+{
+ CompactContext *compact = wctx->priv;
+
+ if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
+ if (!compact->nokey)
+ writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
+ writer_printf(wctx, "%"PRId64, value);
+}
+
+const AVTextFormatter avtextformatter_compact = {
+ .name = "compact",
+ .priv_size = sizeof(CompactContext),
+ .init = compact_init,
+ .print_section_header = compact_print_section_header,
+ .print_section_footer = compact_print_section_footer,
+ .print_integer = compact_print_int,
+ .print_string = compact_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
+ .priv_class = &compact_class,
+};
+
+/* CSV output */
+
+#undef OFFSET
+#define OFFSET(x) offsetof(CompactContext, x)
+
+static const AVOption csv_options[] = {
+ {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 },
+ {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 },
+ {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
+ {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
+ {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {NULL},
+};
+
+DEFINE_FORMATTER_CLASS(csv);
+
+const AVTextFormatter avtextformatter_csv = {
+ .name = "csv",
+ .priv_size = sizeof(CompactContext),
+ .init = compact_init,
+ .print_section_header = compact_print_section_header,
+ .print_section_footer = compact_print_section_footer,
+ .print_integer = compact_print_int,
+ .print_string = compact_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
+ .priv_class = &csv_class,
+};
diff --git a/fftools/textformat/tf_default.c b/fftools/textformat/tf_default.c
new file mode 100644
index 0000000000..9625dd813b
--- /dev/null
+++ b/fftools/textformat/tf_default.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextformat.h"
+#include <libavutil/mem.h>
+#include <libavutil/avassert.h>
+#include <libavutil/bprint.h>
+#include <libavutil/opt.h>
+
+#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
+#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
+#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
+
+#define DEFINE_FORMATTER_CLASS(name) \
+static const char *name##_get_name(void *ctx) \
+{ \
+ return #name ; \
+} \
+static const AVClass name##_class = { \
+ .class_name = #name, \
+ .item_name = name##_get_name, \
+ .option = name##_options \
+}
+
+/* Default output */
+
+typedef struct DefaultContext {
+ const AVClass *class;
+ int nokey;
+ int noprint_wrappers;
+ int nested_section[SECTION_MAX_NB_LEVELS];
+} DefaultContext;
+
+#undef OFFSET
+#define OFFSET(x) offsetof(DefaultContext, x)
+
+static const AVOption default_options[] = {
+ { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ { "nw", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {NULL},
+};
+
+DEFINE_FORMATTER_CLASS(default);
+
+/* lame uppercasing routine, assumes the string is lower case ASCII */
+static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
+{
+ int i;
+ for (i = 0; src[i] && i < dst_size-1; i++)
+ dst[i] = av_toupper(src[i]);
+ dst[i] = 0;
+ return dst;
+}
+
+static void default_print_section_header(AVTextFormatContext *wctx, const void *data)
+{
+ DefaultContext *def = wctx->priv;
+ char buf[32];
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+
+ av_bprint_clear(&wctx->section_pbuf[wctx->level]);
+ if (parent_section &&
+ !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) {
+ def->nested_section[wctx->level] = 1;
+ av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
+ wctx->section_pbuf[wctx->level-1].str,
+ upcase_string(buf, sizeof(buf),
+ av_x_if_null(section->element_name, section->name)));
+ }
+
+ if (def->noprint_wrappers || def->nested_section[wctx->level])
+ return;
+
+ if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
+ writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name));
+}
+
+static void default_print_section_footer(AVTextFormatContext *wctx)
+{
+ DefaultContext *def = wctx->priv;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ char buf[32];
+
+ if (def->noprint_wrappers || def->nested_section[wctx->level])
+ return;
+
+ if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
+ writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
+}
+
+static void default_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
+{
+ DefaultContext *def = wctx->priv;
+
+ if (!def->nokey)
+ writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
+ writer_printf(wctx, "%s\n", value);
+}
+
+static void default_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+{
+ DefaultContext *def = wctx->priv;
+
+ if (!def->nokey)
+ writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
+ writer_printf(wctx, "%"PRId64"\n", value);
+}
+
+const AVTextFormatter avtextformatter_default = {
+ .name = "default",
+ .priv_size = sizeof(DefaultContext),
+ .print_section_header = default_print_section_header,
+ .print_section_footer = default_print_section_footer,
+ .print_integer = default_print_int,
+ .print_string = default_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
+ .priv_class = &default_class,
+};
\ No newline at end of file
diff --git a/fftools/textformat/tf_flat.c b/fftools/textformat/tf_flat.c
new file mode 100644
index 0000000000..afdc494aee
--- /dev/null
+++ b/fftools/textformat/tf_flat.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextformat.h"
+#include <libavutil/mem.h>
+#include <libavutil/avassert.h>
+#include <libavutil/bprint.h>
+#include <libavutil/error.h>
+#include <libavutil/macros.h>
+#include <libavutil/opt.h>
+
+#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
+#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
+#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
+
+#define DEFINE_FORMATTER_CLASS(name) \
+static const char *name##_get_name(void *ctx) \
+{ \
+ return #name ; \
+} \
+static const AVClass name##_class = { \
+ .class_name = #name, \
+ .item_name = name##_get_name, \
+ .option = name##_options \
+}
+
+
+/* Flat output */
+
+typedef struct FlatContext {
+ const AVClass *class;
+ 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="."}, 0, 0 },
+ {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 },
+ {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {NULL},
+};
+
+DEFINE_FORMATTER_CLASS(flat);
+
+static av_cold int flat_init(AVTextFormatContext *wctx)
+{
+ FlatContext *flat = wctx->priv;
+
+ 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;
+ 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_header(AVTextFormatContext *wctx, const void *data)
+{
+ FlatContext *flat = wctx->priv;
+ AVBPrint *buf = &wctx->section_pbuf[wctx->level];
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+
+ /* build section header */
+ av_bprint_clear(buf);
+ if (!parent_section)
+ return;
+ av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
+
+ if (flat->hierarchical ||
+ !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) {
+ av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
+
+ if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
+ int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ?
+ wctx->nb_item_type[wctx->level-1][section->id] :
+ wctx->nb_item[wctx->level-1];
+ av_bprintf(buf, "%d%s", n, flat->sep_str);
+ }
+ }
+}
+
+static void flat_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+{
+ writer_printf(wctx, "%s%s=%"PRId64"\n", wctx->section_pbuf[wctx->level].str, key, value);
+}
+
+static void flat_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
+{
+ FlatContext *flat = wctx->priv;
+ AVBPrint buf;
+
+ writer_put_str(wctx, wctx->section_pbuf[wctx->level].str);
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep));
+ av_bprint_clear(&buf);
+ writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value));
+ av_bprint_finalize(&buf, NULL);
+}
+
+const AVTextFormatter avtextformatter_flat = {
+ .name = "flat",
+ .priv_size = sizeof(FlatContext),
+ .init = flat_init,
+ .print_section_header = flat_print_section_header,
+ .print_integer = flat_print_int,
+ .print_string = flat_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
+ .priv_class = &flat_class,
+};
diff --git a/fftools/textformat/tf_ini.c b/fftools/textformat/tf_ini.c
new file mode 100644
index 0000000000..99a9af5690
--- /dev/null
+++ b/fftools/textformat/tf_ini.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextformat.h"
+#include <libavutil/mem.h>
+#include <libavutil/avassert.h>
+#include <libavutil/bprint.h>
+#include <libavutil/opt.h>
+
+#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
+#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
+#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
+
+#define DEFINE_FORMATTER_CLASS(name) \
+static const char *name##_get_name(void *ctx) \
+{ \
+ return #name ; \
+} \
+static const AVClass name##_class = { \
+ .class_name = #name, \
+ .item_name = name##_get_name, \
+ .option = name##_options \
+}
+
+/* Default output */
+
+typedef struct DefaultContext {
+ const AVClass *class;
+ int nokey;
+ int noprint_wrappers;
+ int nested_section[SECTION_MAX_NB_LEVELS];
+} DefaultContext;
+
+/* INI format output */
+
+typedef struct INIContext {
+ const AVClass *class;
+ int hierarchical;
+} INIContext;
+
+#undef OFFSET
+#define OFFSET(x) offsetof(INIContext, x)
+
+static const AVOption ini_options[] = {
+ {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
+ {NULL},
+};
+
+DEFINE_FORMATTER_CLASS(ini);
+
+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_section_header(AVTextFormatContext *wctx, const void *data)
+{
+ INIContext *ini = wctx->priv;
+ AVBPrint *buf = &wctx->section_pbuf[wctx->level];
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+
+ av_bprint_clear(buf);
+ if (!parent_section) {
+ writer_put_str(wctx, "# ffprobe output\n\n");
+ return;
+ }
+
+ if (wctx->nb_item[wctx->level-1])
+ writer_w8(wctx, '\n');
+
+ av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
+ if (ini->hierarchical ||
+ !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) {
+ av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
+
+ if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
+ int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ?
+ wctx->nb_item_type[wctx->level-1][section->id] :
+ wctx->nb_item[wctx->level-1];
+ av_bprintf(buf, ".%d", n);
+ }
+ }
+
+ if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER)))
+ writer_printf(wctx, "[%s]\n", buf->str);
+}
+
+static void ini_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
+{
+ AVBPrint buf;
+
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ writer_printf(wctx, "%s=", ini_escape_str(&buf, key));
+ av_bprint_clear(&buf);
+ writer_printf(wctx, "%s\n", ini_escape_str(&buf, value));
+ av_bprint_finalize(&buf, NULL);
+}
+
+static void ini_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+{
+ writer_printf(wctx, "%s=%"PRId64"\n", key, value);
+}
+
+const AVTextFormatter avtextformatter_ini = {
+ .name = "ini",
+ .priv_size = sizeof(INIContext),
+ .print_section_header = ini_print_section_header,
+ .print_integer = ini_print_int,
+ .print_string = ini_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
+ .priv_class = &ini_class,
+};
diff --git a/fftools/textformat/tf_json.c b/fftools/textformat/tf_json.c
new file mode 100644
index 0000000000..4579c8a3d9
--- /dev/null
+++ b/fftools/textformat/tf_json.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextformat.h"
+#include <libavutil/mem.h>
+#include <libavutil/avassert.h>
+#include <libavutil/bprint.h>
+#include <libavutil/opt.h>
+
+#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
+#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
+#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
+
+#define DEFINE_FORMATTER_CLASS(name) \
+static const char *name##_get_name(void *ctx) \
+{ \
+ return #name ; \
+} \
+static const AVClass name##_class = { \
+ .class_name = #name, \
+ .item_name = name##_get_name, \
+ .option = name##_options \
+}
+
+
+/* JSON output */
+
+typedef struct JSONContext {
+ const AVClass *class;
+ int indent_level;
+ int compact;
+ const char *item_sep, *item_start_end;
+} JSONContext;
+
+#undef OFFSET
+#define OFFSET(x) offsetof(JSONContext, x)
+
+static const AVOption json_options[]= {
+ { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ { NULL }
+};
+
+DEFINE_FORMATTER_CLASS(json);
+
+static av_cold int json_init(AVTextFormatContext *wctx)
+{
+ JSONContext *json = wctx->priv;
+
+ json->item_sep = json->compact ? ", " : ",\n";
+ json->item_start_end = json->compact ? " " : "\n";
+
+ return 0;
+}
+
+static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
+{
+ static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
+ static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
+ const char *p;
+
+ for (p = src; *p; p++) {
+ char *s = strchr(json_escape, *p);
+ if (s) {
+ av_bprint_chars(dst, '\\', 1);
+ av_bprint_chars(dst, json_subst[s - json_escape], 1);
+ } else if ((unsigned char)*p < 32) {
+ av_bprintf(dst, "\\u00%02x", *p & 0xff);
+ } else {
+ av_bprint_chars(dst, *p, 1);
+ }
+ }
+ return dst->str;
+}
+
+#define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
+
+static void json_print_section_header(AVTextFormatContext *wctx, const void *data)
+{
+ JSONContext *json = wctx->priv;
+ AVBPrint buf;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+
+ if (wctx->level && wctx->nb_item[wctx->level-1])
+ writer_put_str(wctx, ",\n");
+
+ if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
+ writer_put_str(wctx, "{\n");
+ json->indent_level++;
+ } else {
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ json_escape_str(&buf, section->name, wctx);
+ JSON_INDENT();
+
+ json->indent_level++;
+ if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
+ writer_printf(wctx, "\"%s\": [\n", buf.str);
+ } else if (parent_section && !(parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) {
+ writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
+ } else {
+ writer_printf(wctx, "{%s", json->item_start_end);
+
+ /* this is required so the parser can distinguish between packets and frames */
+ if (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE) {
+ if (!json->compact)
+ JSON_INDENT();
+ writer_printf(wctx, "\"type\": \"%s\"", section->name);
+ wctx->nb_item[wctx->level]++;
+ }
+ }
+ av_bprint_finalize(&buf, NULL);
+ }
+}
+
+static void json_print_section_footer(AVTextFormatContext *wctx)
+{
+ JSONContext *json = wctx->priv;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+
+ if (wctx->level == 0) {
+ json->indent_level--;
+ writer_put_str(wctx, "\n}\n");
+ } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
+ writer_w8(wctx, '\n');
+ json->indent_level--;
+ JSON_INDENT();
+ writer_w8(wctx, ']');
+ } else {
+ writer_put_str(wctx, json->item_start_end);
+ json->indent_level--;
+ if (!json->compact)
+ JSON_INDENT();
+ writer_w8(wctx, '}');
+ }
+}
+
+static inline void json_print_item_str(AVTextFormatContext *wctx,
+ const char *key, const char *value)
+{
+ AVBPrint buf;
+
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx));
+ av_bprint_clear(&buf);
+ writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
+ av_bprint_finalize(&buf, NULL);
+}
+
+static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
+{
+ JSONContext *json = wctx->priv;
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+
+ if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
+ writer_put_str(wctx, json->item_sep);
+ if (!json->compact)
+ JSON_INDENT();
+ json_print_item_str(wctx, key, value);
+}
+
+static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+{
+ JSONContext *json = wctx->priv;
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+ AVBPrint buf;
+
+ if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
+ writer_put_str(wctx, json->item_sep);
+ if (!json->compact)
+ JSON_INDENT();
+
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value);
+ av_bprint_finalize(&buf, NULL);
+}
+
+const AVTextFormatter avtextformatter_json = {
+ .name = "json",
+ .priv_size = sizeof(JSONContext),
+ .init = json_init,
+ .print_section_header = json_print_section_header,
+ .print_section_footer = json_print_section_footer,
+ .print_integer = json_print_int,
+ .print_string = json_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
+ .priv_class = &json_class,
+};
+
diff --git a/fftools/textformat/tf_xml.c b/fftools/textformat/tf_xml.c
new file mode 100644
index 0000000000..04c43fb85d
--- /dev/null
+++ b/fftools/textformat/tf_xml.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextformat.h"
+#include <libavutil/mem.h>
+#include <libavutil/avassert.h>
+#include <libavutil/bprint.h>
+#include <libavutil/error.h>
+#include <libavutil/macros.h>
+#include <libavutil/opt.h>
+
+#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
+#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
+#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
+
+#define DEFINE_FORMATTER_CLASS(name) \
+static const char *name##_get_name(void *ctx) \
+{ \
+ return #name ; \
+} \
+static const AVClass name##_class = { \
+ .class_name = #name, \
+ .item_name = name##_get_name, \
+ .option = name##_options \
+}
+
+/* XML output */
+
+typedef struct XMLContext {
+ const AVClass *class;
+ int within_tag;
+ int indent_level;
+ int fully_qualified;
+ int xsd_strict;
+} XMLContext;
+
+#undef OFFSET
+#define OFFSET(x) offsetof(XMLContext, x)
+
+static const AVOption xml_options[] = {
+ {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
+ {NULL},
+};
+
+DEFINE_FORMATTER_CLASS(xml);
+
+static av_cold int xml_init(AVTextFormatContext *wctx)
+{
+ XMLContext *xml = wctx->priv;
+
+ if (xml->xsd_strict) {
+ xml->fully_qualified = 1;
+#define CHECK_COMPLIANCE(opt, opt_name) \
+ if (opt) { \
+ av_log(wctx, AV_LOG_ERROR, \
+ "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
+ "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
+ return AVERROR(EINVAL); \
+ }
+ ////CHECK_COMPLIANCE(show_private_data, "private");
+ CHECK_COMPLIANCE(wctx->show_value_unit, "unit");
+ CHECK_COMPLIANCE(wctx->use_value_prefix, "prefix");
+ }
+
+ return 0;
+}
+
+#define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ')
+
+static void xml_print_section_header(AVTextFormatContext *wctx, const void *data)
+{
+ XMLContext *xml = wctx->priv;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+ const struct AVTextFormatSection *parent_section = wctx->level ?
+ wctx->section[wctx->level-1] : NULL;
+
+ if (wctx->level == 0) {
+ const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
+ "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" "
+ "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\"";
+
+ writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ writer_printf(wctx, "<%sffprobe%s>\n",
+ xml->fully_qualified ? "ffprobe:" : "",
+ xml->fully_qualified ? qual : "");
+ return;
+ }
+
+ if (xml->within_tag) {
+ xml->within_tag = 0;
+ writer_put_str(wctx, ">\n");
+ }
+
+ if (parent_section && (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) &&
+ wctx->level && wctx->nb_item[wctx->level-1])
+ writer_w8(wctx, '\n');
+ xml->indent_level++;
+
+ if (section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS)) {
+ XML_INDENT(); writer_printf(wctx, "<%s", section->name);
+
+ if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) {
+ AVBPrint buf;
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ av_bprint_escape(&buf, section->get_type(data), NULL,
+ AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
+ writer_printf(wctx, " type=\"%s\"", buf.str);
+ }
+ writer_printf(wctx, ">\n", section->name);
+ } else {
+ XML_INDENT(); writer_printf(wctx, "<%s ", section->name);
+ xml->within_tag = 1;
+ }
+}
+
+static void xml_print_section_footer(AVTextFormatContext *wctx)
+{
+ XMLContext *xml = wctx->priv;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+
+ if (wctx->level == 0) {
+ writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
+ } else if (xml->within_tag) {
+ xml->within_tag = 0;
+ writer_put_str(wctx, "/>\n");
+ xml->indent_level--;
+ } else {
+ XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name);
+ xml->indent_level--;
+ }
+}
+
+static void xml_print_value(AVTextFormatContext *wctx, const char *key,
+ const char *str, int64_t num, const int is_int)
+{
+ AVBPrint buf;
+ XMLContext *xml = wctx->priv;
+ const struct AVTextFormatSection *section = wctx->section[wctx->level];
+
+ av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
+
+ if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS) {
+ xml->indent_level++;
+ XML_INDENT();
+ av_bprint_escape(&buf, key, NULL,
+ AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
+ writer_printf(wctx, "<%s key=\"%s\"",
+ section->element_name, buf.str);
+ av_bprint_clear(&buf);
+
+ if (is_int) {
+ writer_printf(wctx, " value=\"%"PRId64"\"/>\n", num);
+ } else {
+ av_bprint_escape(&buf, str, NULL,
+ AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
+ writer_printf(wctx, " value=\"%s\"/>\n", buf.str);
+ }
+ xml->indent_level--;
+ } else {
+ if (wctx->nb_item[wctx->level])
+ writer_w8(wctx, ' ');
+
+ if (is_int) {
+ writer_printf(wctx, "%s=\"%"PRId64"\"", key, num);
+ } else {
+ av_bprint_escape(&buf, str, NULL,
+ AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
+ writer_printf(wctx, "%s=\"%s\"", key, buf.str);
+ }
+ }
+
+ av_bprint_finalize(&buf, NULL);
+}
+
+static inline void xml_print_str(AVTextFormatContext *wctx, const char *key, const char *value) {
+ xml_print_value(wctx, key, value, 0, 0);
+}
+
+static void xml_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
+{
+ xml_print_value(wctx, key, NULL, value, 1);
+}
+
+const AVTextFormatter avtextformatter_xml = {
+ .name = "xml",
+ .priv_size = sizeof(XMLContext),
+ .init = xml_init,
+ .print_section_header = xml_print_section_header,
+ .print_section_footer = xml_print_section_footer,
+ .print_integer = xml_print_int,
+ .print_string = xml_print_str,
+ .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
+ .priv_class = &xml_class,
+};
+
diff --git a/fftools/textformat/tw_avio.c b/fftools/textformat/tw_avio.c
new file mode 100644
index 0000000000..e1605a1272
--- /dev/null
+++ b/fftools/textformat/tw_avio.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+
+#include "avtextwriters.h"
+#include "libavutil/opt.h"
+
+/* AVIO Writer */
+
+# define WRITER_NAME "aviowriter"
+
+typedef struct IOWriterContext {
+ const AVClass *class;
+ AVIOContext *avio_context;
+ int close_on_uninit;
+} IOWriterContext;
+
+static const char *iowriter_get_name(void *ctx)
+{
+ return WRITER_NAME;
+}
+
+static const AVClass iowriter_class = {
+ .class_name = WRITER_NAME,
+ .item_name = iowriter_get_name,
+};
+
+static av_cold void iowriter_uninit(AVTextWriterContext *wctx)
+{
+ IOWriterContext *ctx = wctx->priv;
+
+ if (ctx->close_on_uninit && ctx->avio_context) {
+ avio_flush(ctx->avio_context);
+ avio_close(ctx->avio_context);
+ }
+}
+
+static void io_w8(AVTextWriterContext *wctx, int b)
+{
+ IOWriterContext *ctx = wctx->priv;
+ avio_w8(ctx->avio_context, b);
+}
+
+static void io_put_str(AVTextWriterContext *wctx, const char *str)
+{
+ IOWriterContext *ctx = wctx->priv;
+ avio_write(ctx->avio_context, str, strlen(str));
+}
+
+static void io_printf(AVTextWriterContext *wctx, const char *fmt, ...)
+{
+ IOWriterContext *ctx = wctx->priv;
+ va_list ap;
+
+ va_start(ap, fmt);
+ avio_vprintf(ctx->avio_context, fmt, ap);
+ va_end(ap);
+}
+
+
+const AVTextWriter avtextwriter_avio = {
+ .name = WRITER_NAME,
+ .priv_size = sizeof(IOWriterContext),
+ .uninit = iowriter_uninit,
+ .priv_class = &iowriter_class,
+ .writer_put_str = io_put_str,
+ .writer_printf = io_printf,
+ .writer_w8 = io_w8
+};
+
+int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit)
+{
+ IOWriterContext *ctx;
+ int ret;
+
+
+ ret = avtextwriter_context_open(pwctx, &avtextwriter_avio);
+ if (ret < 0)
+ return ret;
+
+ ctx = (*pwctx)->priv;
+
+ if ((ret = avio_open(&ctx->avio_context, output_filename, AVIO_FLAG_WRITE)) < 0) {
+ av_log(ctx, AV_LOG_ERROR,
+ "Failed to open output '%s' with error: %s\n", output_filename, av_err2str(ret));
+ avtextwriter_context_close(pwctx);
+ return ret;
+ }
+
+ ctx->close_on_uninit = close_on_uninit;
+
+ return ret;
+}
+
+
+int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit)
+{
+ IOWriterContext *ctx;
+ int ret;
+
+ ret = avtextwriter_context_open(pwctx, &avtextwriter_avio);
+ if (ret < 0)
+ return ret;
+
+ ctx = (*pwctx)->priv;
+ ctx->avio_context = avio_ctx;
+ ctx->close_on_uninit = close_on_uninit;
+
+ return ret;
+}
diff --git a/fftools/textformat/tw_buffer.c b/fftools/textformat/tw_buffer.c
new file mode 100644
index 0000000000..4aeec4afa0
--- /dev/null
+++ b/fftools/textformat/tw_buffer.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+
+#include "avtextwriters.h"
+#include "libavutil/opt.h"
+#include "libavutil/bprint.h"
+
+/* Buffer Writer */
+
+# define WRITER_NAME "bufferwriter"
+
+typedef struct BufferWriterContext {
+ const AVClass *class;
+ AVBPrint *buffer;
+} BufferWriterContext;
+
+static const char *bufferwriter_get_name(void *ctx)
+{
+ return WRITER_NAME;
+}
+
+static const AVClass bufferwriter_class = {
+ .class_name = WRITER_NAME,
+ .item_name = bufferwriter_get_name,
+};
+
+static void buffer_w8(AVTextWriterContext *wctx, int b)
+{
+ BufferWriterContext *ctx = wctx->priv;
+ av_bprintf(ctx->buffer, "%c", b);
+}
+
+static void buffer_put_str(AVTextWriterContext *wctx, const char *str)
+{
+ BufferWriterContext *ctx = wctx->priv;
+ av_bprintf(ctx->buffer, "%s", str);
+}
+
+static void buffer_printf(AVTextWriterContext *wctx, const char *fmt, ...)
+{
+ BufferWriterContext *ctx = wctx->priv;
+
+ va_list vargs;
+ va_start(vargs, fmt);
+ av_vbprintf(ctx->buffer, fmt, vargs);
+ va_end(vargs);
+}
+
+
+const AVTextWriter avtextwriter_buffer = {
+ .name = WRITER_NAME,
+ .priv_size = sizeof(BufferWriterContext),
+ .priv_class = &bufferwriter_class,
+ .writer_put_str = buffer_put_str,
+ .writer_printf = buffer_printf,
+ .writer_w8 = buffer_w8
+};
+
+int avtextwriter_create_buffer(AVTextWriterContext **pwctx, AVBPrint *buffer)
+{
+ BufferWriterContext *ctx;
+ int ret;
+
+ ret = avtextwriter_context_open(pwctx, &avtextwriter_buffer);
+ if (ret < 0)
+ return ret;
+
+ ctx = (*pwctx)->priv;
+ ctx->buffer = buffer;
+
+ return ret;
+}
diff --git a/fftools/textformat/tw_stdout.c b/fftools/textformat/tw_stdout.c
new file mode 100644
index 0000000000..b9a613ad61
--- /dev/null
+++ b/fftools/textformat/tw_stdout.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) The ffmpeg developers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "avtextwriters.h"
+#include "libavutil/opt.h"
+
+/* STDOUT Writer */
+
+# define WRITER_NAME "stdoutwriter"
+
+typedef struct StdOutWriterContext {
+ const AVClass *class;
+} StdOutWriterContext;
+
+static const char *stdoutwriter_get_name(void *ctx)
+{
+ return WRITER_NAME;
+}
+
+static const AVClass stdoutwriter_class = {
+ .class_name = WRITER_NAME,
+ .item_name = stdoutwriter_get_name,
+};
+
+static inline void stdout_w8(AVTextWriterContext *wctx, int b)
+{
+ printf("%c", b);
+}
+
+static inline void stdout_put_str(AVTextWriterContext *wctx, const char *str)
+{
+ printf("%s", str);
+}
+
+static inline void stdout_printf(AVTextWriterContext *wctx, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+
+static const AVTextWriter avtextwriter_stdout = {
+ .name = WRITER_NAME,
+ .priv_size = sizeof(StdOutWriterContext),
+ .priv_class = &stdoutwriter_class,
+ .writer_put_str = stdout_put_str,
+ .writer_printf = stdout_printf,
+ .writer_w8 = stdout_w8
+};
+
+int avtextwriter_create_stdout(AVTextWriterContext **pwctx)
+{
+ int ret;
+
+ ret = avtextwriter_context_open(pwctx, &avtextwriter_stdout);
+
+ return ret;
+}
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread
* [FFmpeg-devel] [PATCH v3 2/7] fftools/ffprobe: Change to use textformat api
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 1/7] fftools/textformat: Extract and generalize textformat api from ffprobe.c softworkz
@ 2025-03-01 10:01 ` softworkz
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 3/7] fftools/ffprobe: Rename writer_print_section_* and WriterContext softworkz
` (4 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:01 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
Signed-off-by: softworkz <softworkz@hotmail.com>
---
fftools/Makefile | 12 +
fftools/ffprobe.c | 1849 ++++-----------------------------------------
2 files changed, 142 insertions(+), 1719 deletions(-)
diff --git a/fftools/Makefile b/fftools/Makefile
index 4499799818..664b73b161 100644
--- a/fftools/Makefile
+++ b/fftools/Makefile
@@ -22,6 +22,18 @@ OBJS-ffmpeg += \
fftools/sync_queue.o \
fftools/thread_queue.o \
+OBJS-ffprobe += \
+ fftools/textformat/avtextformat.o \
+ fftools/textformat/tf_compact.o \
+ fftools/textformat/tf_default.o \
+ fftools/textformat/tf_flat.o \
+ fftools/textformat/tf_ini.o \
+ fftools/textformat/tf_json.o \
+ fftools/textformat/tf_xml.o \
+ fftools/textformat/tw_avio.o \
+ fftools/textformat/tw_buffer.o \
+ fftools/textformat/tw_stdout.o \
+
OBJS-ffplay += fftools/ffplay_renderer.o
define DOFFTOOL
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 7341731d2f..f398057df7 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -40,7 +40,6 @@
#include "libavutil/channel_layout.h"
#include "libavutil/display.h"
#include "libavutil/film_grain_params.h"
-#include "libavutil/hash.h"
#include "libavutil/hdr_dynamic_metadata.h"
#include "libavutil/iamf.h"
#include "libavutil/mastering_display_metadata.h"
@@ -66,11 +65,17 @@
#include "libpostproc/postprocess.h"
#include "libpostproc/version.h"
#include "libavfilter/version.h"
+#include "textformat/avtextformat.h"
#include "cmdutils.h"
#include "opt_common.h"
#include "libavutil/thread.h"
+// TEMPORARY DEFINES
+#define writer_print_section_header(w, d, s) avtext_print_section_header(w, d, s)
+#define writer_print_section_footer(w) avtext_print_section_footer(w)
+#define WriterContext AVTextFormatContext
+
// attached as opaque_ref to packets/frames
typedef struct FrameData {
int64_t pkt_pos;
@@ -156,10 +161,7 @@ static int find_stream_info = 1;
/* section structure definition */
-#define SECTION_MAX_NB_CHILDREN 11
-
typedef enum {
- SECTION_ID_NONE = -1,
SECTION_ID_CHAPTER,
SECTION_ID_CHAPTER_TAGS,
SECTION_ID_CHAPTERS,
@@ -228,25 +230,6 @@ typedef enum {
SECTION_ID_SUBTITLE,
} SectionID;
-struct section {
- int id; ///< unique id identifying a section
- const char *name;
-
-#define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level
-#define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type
-#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
- /// For these sections the element_name field is mandatory.
-#define SECTION_FLAG_HAS_TYPE 8 ///< the section contains a type to distinguish multiple nested elements
-
- int flags;
- const SectionID children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
- const char *element_name; ///< name of the contained element, if provided
- const char *unique_name; ///< unique section name, in case the name is ambiguous
- AVDictionary *entries_to_show;
- const char *(* get_type)(const void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined
- int show_all_entries;
-};
-
static const char *get_packet_side_data_type(const void *data)
{
const AVPacketSideData *sd = (const AVPacketSideData *)data;
@@ -270,75 +253,75 @@ static const char *get_stream_group_type(const void *data)
return av_x_if_null(avformat_stream_group_name(stg->type), "unknown");
}
-static struct section sections[] = {
- [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
+static struct AVTextFormatSection sections[] = {
+ [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
[SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } },
- [SECTION_ID_CHAPTER_TAGS] = { SECTION_ID_CHAPTER_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
+ [SECTION_ID_CHAPTER_TAGS] = { SECTION_ID_CHAPTER_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "chapter_tags" },
[SECTION_ID_ERROR] = { SECTION_ID_ERROR, "error", 0, { -1 } },
[SECTION_ID_FORMAT] = { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
- [SECTION_ID_FORMAT_TAGS] = { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
- [SECTION_ID_FRAMES] = { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } },
+ [SECTION_ID_FORMAT_TAGS] = { SECTION_ID_FORMAT_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
+ [SECTION_ID_FRAMES] = { SECTION_ID_FRAMES, "frames", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, SECTION_ID_SUBTITLE, -1 } },
[SECTION_ID_FRAME] = { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_LOGS, -1 } },
- [SECTION_ID_FRAME_TAGS] = { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
- [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" },
- [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, -1 }, .unique_name = "frame_side_data", .element_name = "side_datum", .get_type = get_frame_side_data_type },
- [SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, "timecodes", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, -1 } },
+ [SECTION_ID_FRAME_TAGS] = { SECTION_ID_FRAME_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
+ [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" },
+ [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, -1 }, .unique_name = "frame_side_data", .element_name = "side_datum", .get_type = get_frame_side_data_type },
+ [SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, "timecodes", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, -1 } },
[SECTION_ID_FRAME_SIDE_DATA_TIMECODE] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } },
- [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 }, .element_name = "component", .unique_name = "frame_side_data_components" },
- [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 }, .unique_name = "frame_side_data_component", .element_name = "component_entry", .get_type = get_raw_string_type },
- [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece", .unique_name = "frame_side_data_pieces" },
- [SECTION_ID_FRAME_SIDE_DATA_PIECE] = { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "frame_side_data_piece", .get_type = get_raw_string_type },
- [SECTION_ID_FRAME_LOGS] = { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
+ [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 }, .element_name = "component", .unique_name = "frame_side_data_components" },
+ [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 }, .unique_name = "frame_side_data_component", .element_name = "component_entry", .get_type = get_raw_string_type },
+ [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece", .unique_name = "frame_side_data_pieces" },
+ [SECTION_ID_FRAME_SIDE_DATA_PIECE] = { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "frame_side_data_piece", .get_type = get_raw_string_type },
+ [SECTION_ID_FRAME_LOGS] = { SECTION_ID_FRAME_LOGS, "logs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } },
[SECTION_ID_FRAME_LOG] = { SECTION_ID_FRAME_LOG, "log", 0, { -1 }, },
- [SECTION_ID_LIBRARY_VERSIONS] = { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
+ [SECTION_ID_LIBRARY_VERSIONS] = { SECTION_ID_LIBRARY_VERSIONS, "library_versions", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
[SECTION_ID_LIBRARY_VERSION] = { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
- [SECTION_ID_PACKETS] = { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
- [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
+ [SECTION_ID_PACKETS] = { SECTION_ID_PACKETS, "packets", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
+ [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY | AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE, { SECTION_ID_PACKET, -1} },
[SECTION_ID_PACKET] = { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } },
- [SECTION_ID_PACKET_TAGS] = { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" },
- [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" },
- [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .unique_name = "packet_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
- [SECTION_ID_PIXEL_FORMATS] = { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } },
+ [SECTION_ID_PACKET_TAGS] = { SECTION_ID_PACKET_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" },
+ [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" },
+ [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { -1 }, .unique_name = "packet_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
+ [SECTION_ID_PIXEL_FORMATS] = { SECTION_ID_PIXEL_FORMATS, "pixel_formats", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } },
[SECTION_ID_PIXEL_FORMAT] = { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } },
[SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" },
- [SECTION_ID_PIXEL_FORMAT_COMPONENTS] = { SECTION_ID_PIXEL_FORMAT_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, {SECTION_ID_PIXEL_FORMAT_COMPONENT, -1 }, .unique_name = "pixel_format_components" },
+ [SECTION_ID_PIXEL_FORMAT_COMPONENTS] = { SECTION_ID_PIXEL_FORMAT_COMPONENTS, "components", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, {SECTION_ID_PIXEL_FORMAT_COMPONENT, -1 }, .unique_name = "pixel_format_components" },
[SECTION_ID_PIXEL_FORMAT_COMPONENT] = { SECTION_ID_PIXEL_FORMAT_COMPONENT, "component", 0, { -1 } },
[SECTION_ID_PROGRAM_STREAM_DISPOSITION] = { SECTION_ID_PROGRAM_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "program_stream_disposition" },
- [SECTION_ID_PROGRAM_STREAM_TAGS] = { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" },
+ [SECTION_ID_PROGRAM_STREAM_TAGS] = { SECTION_ID_PROGRAM_STREAM_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_stream_tags" },
[SECTION_ID_PROGRAM] = { SECTION_ID_PROGRAM, "program", 0, { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_STREAMS, -1 } },
- [SECTION_ID_PROGRAM_STREAMS] = { SECTION_ID_PROGRAM_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" },
+ [SECTION_ID_PROGRAM_STREAMS] = { SECTION_ID_PROGRAM_STREAMS, "streams", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM_STREAM, -1 }, .unique_name = "program_streams" },
[SECTION_ID_PROGRAM_STREAM] = { SECTION_ID_PROGRAM_STREAM, "stream", 0, { SECTION_ID_PROGRAM_STREAM_DISPOSITION, SECTION_ID_PROGRAM_STREAM_TAGS, -1 }, .unique_name = "program_stream" },
- [SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
+ [SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" },
[SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
- [SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
+ [SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS, "programs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
[SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION] = { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_stream_disposition" },
- [SECTION_ID_STREAM_GROUP_STREAM_TAGS] = { SECTION_ID_STREAM_GROUP_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_stream_tags" },
+ [SECTION_ID_STREAM_GROUP_STREAM_TAGS] = { SECTION_ID_STREAM_GROUP_STREAM_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_stream_tags" },
[SECTION_ID_STREAM_GROUP] = { SECTION_ID_STREAM_GROUP, "stream_group", 0, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, -1 } },
- [SECTION_ID_STREAM_GROUP_COMPONENTS] = { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_COMPONENT, -1 }, .element_name = "component", .unique_name = "stream_group_components" },
- [SECTION_ID_STREAM_GROUP_COMPONENT] = { SECTION_ID_STREAM_GROUP_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_stream_group_type },
- [SECTION_ID_STREAM_GROUP_SUBCOMPONENTS] = { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, "subcomponents", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, -1 }, .element_name = "component" },
- [SECTION_ID_STREAM_GROUP_SUBCOMPONENT] = { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, "subcomponent", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .element_name = "subcomponent_entry", .get_type = get_raw_string_type },
- [SECTION_ID_STREAM_GROUP_PIECES] = { SECTION_ID_STREAM_GROUP_PIECES, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = "stream_group_pieces" },
- [SECTION_ID_STREAM_GROUP_PIECE] = { SECTION_ID_STREAM_GROUP_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SUBPIECES, -1 }, .unique_name = "stream_group_piece", .element_name = "piece_entry", .get_type = get_raw_string_type },
- [SECTION_ID_STREAM_GROUP_SUBPIECES] = { SECTION_ID_STREAM_GROUP_SUBPIECES, "subpieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBPIECE, -1 }, .element_name = "subpiece" },
- [SECTION_ID_STREAM_GROUP_SUBPIECE] = { SECTION_ID_STREAM_GROUP_SUBPIECE, "subpiece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_BLOCKS, -1 }, .element_name = "subpiece_entry", .get_type = get_raw_string_type },
- [SECTION_ID_STREAM_GROUP_BLOCKS] = { SECTION_ID_STREAM_GROUP_BLOCKS, "blocks", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_BLOCK, -1 }, .element_name = "block" },
- [SECTION_ID_STREAM_GROUP_BLOCK] = { SECTION_ID_STREAM_GROUP_BLOCK, "block", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "block_entry", .get_type = get_raw_string_type },
- [SECTION_ID_STREAM_GROUP_STREAMS] = { SECTION_ID_STREAM_GROUP_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_STREAM, -1 }, .unique_name = "stream_group_streams" },
+ [SECTION_ID_STREAM_GROUP_COMPONENTS] = { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_COMPONENT, -1 }, .element_name = "component", .unique_name = "stream_group_components" },
+ [SECTION_ID_STREAM_GROUP_COMPONENT] = { SECTION_ID_STREAM_GROUP_COMPONENT, "component", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_stream_group_type },
+ [SECTION_ID_STREAM_GROUP_SUBCOMPONENTS] = { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, "subcomponents", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, -1 }, .element_name = "component" },
+ [SECTION_ID_STREAM_GROUP_SUBCOMPONENT] = { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, "subcomponent", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .element_name = "subcomponent_entry", .get_type = get_raw_string_type },
+ [SECTION_ID_STREAM_GROUP_PIECES] = { SECTION_ID_STREAM_GROUP_PIECES, "pieces", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = "stream_group_pieces" },
+ [SECTION_ID_STREAM_GROUP_PIECE] = { SECTION_ID_STREAM_GROUP_PIECE, "piece", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SUBPIECES, -1 }, .unique_name = "stream_group_piece", .element_name = "piece_entry", .get_type = get_raw_string_type },
+ [SECTION_ID_STREAM_GROUP_SUBPIECES] = { SECTION_ID_STREAM_GROUP_SUBPIECES, "subpieces", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBPIECE, -1 }, .element_name = "subpiece" },
+ [SECTION_ID_STREAM_GROUP_SUBPIECE] = { SECTION_ID_STREAM_GROUP_SUBPIECE, "subpiece", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_BLOCKS, -1 }, .element_name = "subpiece_entry", .get_type = get_raw_string_type },
+ [SECTION_ID_STREAM_GROUP_BLOCKS] = { SECTION_ID_STREAM_GROUP_BLOCKS, "blocks", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_BLOCK, -1 }, .element_name = "block" },
+ [SECTION_ID_STREAM_GROUP_BLOCK] = { SECTION_ID_STREAM_GROUP_BLOCK, "block", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS|AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "block_entry", .get_type = get_raw_string_type },
+ [SECTION_ID_STREAM_GROUP_STREAMS] = { SECTION_ID_STREAM_GROUP_STREAMS, "streams", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_STREAM, -1 }, .unique_name = "stream_group_streams" },
[SECTION_ID_STREAM_GROUP_STREAM] = { SECTION_ID_STREAM_GROUP_STREAM, "stream", 0, { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAM_TAGS, -1 }, .unique_name = "stream_group_stream" },
[SECTION_ID_STREAM_GROUP_DISPOSITION] = { SECTION_ID_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_disposition" },
- [SECTION_ID_STREAM_GROUP_TAGS] = { SECTION_ID_STREAM_GROUP_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_tags" },
- [SECTION_ID_STREAM_GROUPS] = { SECTION_ID_STREAM_GROUPS, "stream_groups", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP, -1 } },
- [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
+ [SECTION_ID_STREAM_GROUP_TAGS] = { SECTION_ID_STREAM_GROUP_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_tags" },
+ [SECTION_ID_STREAM_GROUPS] = { SECTION_ID_STREAM_GROUPS, "stream_groups", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP, -1 } },
+ [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER,
{ SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAM_GROUPS, SECTION_ID_STREAMS,
SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS,
SECTION_ID_PIXEL_FORMATS, -1} },
- [SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
+ [SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
[SECTION_ID_STREAM] = { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, SECTION_ID_STREAM_SIDE_DATA_LIST, -1 } },
[SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
- [SECTION_ID_STREAM_TAGS] = { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
- [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" },
- [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", SECTION_FLAG_HAS_TYPE|SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .unique_name = "stream_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
+ [SECTION_ID_STREAM_TAGS] = { SECTION_ID_STREAM_TAGS, "tags", AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
+ [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" },
+ [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE|AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .unique_name = "stream_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type },
[SECTION_ID_SUBTITLE] = { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } },
};
@@ -350,22 +333,6 @@ static const char *print_input_filename;
static const AVInputFormat *iformat = NULL;
static const char *output_filename = NULL;
-static struct AVHashContext *hash;
-
-static const struct {
- double bin_val;
- double dec_val;
- const char *bin_str;
- const char *dec_str;
-} si_prefixes[] = {
- { 1.0, 1.0, "", "" },
- { 1.024e3, 1e3, "Ki", "K" },
- { 1.048576e6, 1e6, "Mi", "M" },
- { 1.073741824e9, 1e9, "Gi", "G" },
- { 1.099511627776e12, 1e12, "Ti", "T" },
- { 1.125899906842624e15, 1e15, "Pi", "P" },
-};
-
static const char unit_second_str[] = "s" ;
static const char unit_hertz_str[] = "Hz" ;
static const char unit_byte_str[] = "byte" ;
@@ -441,1554 +408,11 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
#endif
}
-struct unit_value {
- union { double d; int64_t i; } val;
- const char *unit;
-};
-
-static char *value_string(char *buf, int buf_size, struct unit_value uv)
-{
- double vald;
- int64_t vali;
- int show_float = 0;
-
- if (uv.unit == unit_second_str) {
- vald = uv.val.d;
- show_float = 1;
- } else {
- vald = vali = uv.val.i;
- }
-
- if (uv.unit == unit_second_str && use_value_sexagesimal_format) {
- double secs;
- int hours, mins;
- secs = vald;
- mins = (int)secs / 60;
- secs = secs - mins * 60;
- hours = mins / 60;
- mins %= 60;
- snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
- } else {
- const char *prefix_string = "";
-
- if (use_value_prefix && vald > 1) {
- int64_t index;
-
- if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) {
- index = (int64_t) (log2(vald)) / 10;
- index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
- vald /= si_prefixes[index].bin_val;
- prefix_string = si_prefixes[index].bin_str;
- } else {
- index = (int64_t) (log10(vald)) / 3;
- index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
- vald /= si_prefixes[index].dec_val;
- prefix_string = si_prefixes[index].dec_str;
- }
- vali = vald;
- }
-
- if (show_float || (use_value_prefix && vald != (int64_t)vald))
- snprintf(buf, buf_size, "%f", vald);
- else
- snprintf(buf, buf_size, "%"PRId64, vali);
- av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "",
- prefix_string, show_value_unit ? uv.unit : "");
- }
-
- return buf;
-}
-
-/* WRITERS API */
-
-typedef struct WriterContext WriterContext;
-
-#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
-#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
-
-typedef enum {
- WRITER_STRING_VALIDATION_FAIL,
- WRITER_STRING_VALIDATION_REPLACE,
- WRITER_STRING_VALIDATION_IGNORE,
- WRITER_STRING_VALIDATION_NB
-} StringValidation;
-
-typedef struct Writer {
- const AVClass *priv_class; ///< private class of the writer, if any
- int priv_size; ///< private size for the writer context
- const char *name;
-
- int (*init) (WriterContext *wctx);
- void (*uninit)(WriterContext *wctx);
-
- void (*print_section_header)(WriterContext *wctx, const void *data);
- void (*print_section_footer)(WriterContext *wctx);
- void (*print_integer) (WriterContext *wctx, const char *, int64_t);
- void (*print_rational) (WriterContext *wctx, AVRational *q, char *sep);
- void (*print_string) (WriterContext *wctx, const char *, const char *);
- int flags; ///< a combination or WRITER_FLAG_*
-} Writer;
-
-#define SECTION_MAX_NB_LEVELS 12
-
-struct WriterContext {
- const AVClass *class; ///< class of the writer
- const Writer *writer; ///< the Writer of which this is an instance
- AVIOContext *avio; ///< the I/O context used to write
-
- void (* writer_w8)(WriterContext *wctx, int b);
- void (* writer_put_str)(WriterContext *wctx, const char *str);
- void (* writer_printf)(WriterContext *wctx, const char *fmt, ...);
-
- char *name; ///< name of this writer instance
- void *priv; ///< private data for use by the filter
-
- const struct section *sections; ///< array containing all sections
- int nb_sections; ///< number of sections
-
- int level; ///< current level, starting from 0
-
- /** number of the item printed in the given section, starting from 0 */
- unsigned int nb_item[SECTION_MAX_NB_LEVELS];
-
- /** section per each level */
- const struct section *section[SECTION_MAX_NB_LEVELS];
- AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
- /// used by various writers
-
- unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section
- unsigned int nb_section_frame; ///< number of the frame section in case we are in "packets_and_frames" section
- unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames
-
- int string_validation;
- char *string_validation_replacement;
- unsigned int string_validation_utf8_flags;
-};
-
-static const char *writer_get_name(void *p)
-{
- WriterContext *wctx = p;
- return wctx->writer->name;
-}
-
-#define OFFSET(x) offsetof(WriterContext, x)
-
-static const AVOption writer_options[] = {
- { "string_validation", "set string validation mode",
- OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
- { "sv", "set string validation mode",
- OFFSET(string_validation), AV_OPT_TYPE_INT, {.i64=WRITER_STRING_VALIDATION_REPLACE}, 0, WRITER_STRING_VALIDATION_NB-1, .unit = "sv" },
- { "ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_IGNORE}, .unit = "sv" },
- { "replace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_REPLACE}, .unit = "sv" },
- { "fail", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = WRITER_STRING_VALIDATION_FAIL}, .unit = "sv" },
- { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str=""}},
- { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, {.str="\xEF\xBF\xBD"}},
- { NULL }
-};
-
-static void *writer_child_next(void *obj, void *prev)
-{
- WriterContext *ctx = obj;
- if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
- return ctx->priv;
- return NULL;
-}
-
-static const AVClass writer_class = {
- .class_name = "Writer",
- .item_name = writer_get_name,
- .option = writer_options,
- .version = LIBAVUTIL_VERSION_INT,
- .child_next = writer_child_next,
-};
-
-static int writer_close(WriterContext **wctx)
-{
- int i;
- int ret = 0;
-
- if (!*wctx)
- return -1;
-
- if ((*wctx)->writer->uninit)
- (*wctx)->writer->uninit(*wctx);
- for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
- av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL);
- if ((*wctx)->writer->priv_class)
- av_opt_free((*wctx)->priv);
- av_freep(&((*wctx)->priv));
- av_opt_free(*wctx);
- if ((*wctx)->avio) {
- avio_flush((*wctx)->avio);
- ret = avio_close((*wctx)->avio);
- }
- av_freep(wctx);
- return ret;
-}
-
-static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
-{
- int i;
- av_bprintf(bp, "0X");
- for (i = 0; i < ubuf_size; i++)
- av_bprintf(bp, "%02X", ubuf[i]);
-}
-
-static inline void writer_w8_avio(WriterContext *wctx, int b)
-{
- avio_w8(wctx->avio, b);
-}
-
-static inline void writer_put_str_avio(WriterContext *wctx, const char *str)
-{
- avio_write(wctx->avio, str, strlen(str));
-}
-
-static inline void writer_printf_avio(WriterContext *wctx, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- avio_vprintf(wctx->avio, fmt, ap);
- va_end(ap);
-}
-
-static inline void writer_w8_printf(WriterContext *wctx, int b)
-{
- printf("%c", b);
-}
-
-static inline void writer_put_str_printf(WriterContext *wctx, const char *str)
-{
- printf("%s", str);
-}
-
-static inline void writer_printf_printf(WriterContext *wctx, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
-}
-
-static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
- const struct section *sections, int nb_sections, const char *output)
-{
- int i, ret = 0;
-
- if (!(*wctx = av_mallocz(sizeof(WriterContext)))) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
- if (!((*wctx)->priv = av_mallocz(writer->priv_size))) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
- (*wctx)->class = &writer_class;
- (*wctx)->writer = writer;
- (*wctx)->level = -1;
- (*wctx)->sections = sections;
- (*wctx)->nb_sections = nb_sections;
-
- av_opt_set_defaults(*wctx);
-
- if (writer->priv_class) {
- void *priv_ctx = (*wctx)->priv;
- *((const AVClass **)priv_ctx) = writer->priv_class;
- av_opt_set_defaults(priv_ctx);
- }
-
- /* convert options to dictionary */
- if (args) {
- AVDictionary *opts = NULL;
- const AVDictionaryEntry *opt = NULL;
-
- if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
- av_log(*wctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to writer context\n", args);
- av_dict_free(&opts);
- goto fail;
- }
-
- while ((opt = av_dict_iterate(opts, opt))) {
- if ((ret = av_opt_set(*wctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
- av_log(*wctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to writer context\n",
- opt->key, opt->value);
- av_dict_free(&opts);
- goto fail;
- }
- }
-
- av_dict_free(&opts);
- }
-
- /* validate replace string */
- {
- const uint8_t *p = (*wctx)->string_validation_replacement;
- const uint8_t *endp = p + strlen(p);
- while (*p) {
- const uint8_t *p0 = p;
- int32_t code;
- ret = av_utf8_decode(&code, &p, endp, (*wctx)->string_validation_utf8_flags);
- if (ret < 0) {
- AVBPrint bp;
- av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
- bprint_bytes(&bp, p0, p-p0),
- av_log(wctx, AV_LOG_ERROR,
- "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
- bp.str, (*wctx)->string_validation_replacement);
- return ret;
- }
- }
- }
-
- if (!output_filename) {
- (*wctx)->writer_w8 = writer_w8_printf;
- (*wctx)->writer_put_str = writer_put_str_printf;
- (*wctx)->writer_printf = writer_printf_printf;
- } else {
- if ((ret = avio_open(&(*wctx)->avio, output, AVIO_FLAG_WRITE)) < 0) {
- av_log(*wctx, AV_LOG_ERROR,
- "Failed to open output '%s' with error: %s\n", output, av_err2str(ret));
- goto fail;
- }
- (*wctx)->writer_w8 = writer_w8_avio;
- (*wctx)->writer_put_str = writer_put_str_avio;
- (*wctx)->writer_printf = writer_printf_avio;
- }
-
- for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
- av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
-
- if ((*wctx)->writer->init)
- ret = (*wctx)->writer->init(*wctx);
- if (ret < 0)
- goto fail;
-
- return 0;
-
-fail:
- writer_close(wctx);
- return ret;
-}
-
-static inline void writer_print_section_header(WriterContext *wctx,
- const void *data,
- int section_id)
-{
- int parent_section_id;
- wctx->level++;
- av_assert0(wctx->level < SECTION_MAX_NB_LEVELS);
- parent_section_id = wctx->level ?
- (wctx->section[wctx->level-1])->id : SECTION_ID_NONE;
-
- wctx->nb_item[wctx->level] = 0;
- wctx->section[wctx->level] = &wctx->sections[section_id];
-
- if (section_id == SECTION_ID_PACKETS_AND_FRAMES) {
- wctx->nb_section_packet = wctx->nb_section_frame =
- wctx->nb_section_packet_frame = 0;
- } else if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
- wctx->nb_section_packet_frame = section_id == SECTION_ID_PACKET ?
- wctx->nb_section_packet : wctx->nb_section_frame;
- }
-
- if (wctx->writer->print_section_header)
- wctx->writer->print_section_header(wctx, data);
-}
-
-static inline void writer_print_section_footer(WriterContext *wctx)
-{
- int section_id = wctx->section[wctx->level]->id;
- int parent_section_id = wctx->level ?
- wctx->section[wctx->level-1]->id : SECTION_ID_NONE;
-
- if (parent_section_id != SECTION_ID_NONE)
- wctx->nb_item[wctx->level-1]++;
- if (parent_section_id == SECTION_ID_PACKETS_AND_FRAMES) {
- if (section_id == SECTION_ID_PACKET) wctx->nb_section_packet++;
- else wctx->nb_section_frame++;
- }
- if (wctx->writer->print_section_footer)
- wctx->writer->print_section_footer(wctx);
- wctx->level--;
-}
-
-static inline void writer_print_integer(WriterContext *wctx,
- const char *key, int64_t val)
-{
- const struct section *section = wctx->section[wctx->level];
-
- if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
- wctx->writer->print_integer(wctx, key, val);
- wctx->nb_item[wctx->level]++;
- }
-}
-
-static inline int validate_string(WriterContext *wctx, char **dstp, const char *src)
-{
- const uint8_t *p, *endp;
- AVBPrint dstbuf;
- int invalid_chars_nb = 0, ret = 0;
-
- av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
-
- endp = src + strlen(src);
- for (p = src; *p;) {
- uint32_t code;
- int invalid = 0;
- const uint8_t *p0 = p;
-
- if (av_utf8_decode(&code, &p, endp, wctx->string_validation_utf8_flags) < 0) {
- AVBPrint bp;
- av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
- bprint_bytes(&bp, p0, p-p0);
- av_log(wctx, AV_LOG_DEBUG,
- "Invalid UTF-8 sequence %s found in string '%s'\n", bp.str, src);
- invalid = 1;
- }
-
- if (invalid) {
- invalid_chars_nb++;
-
- switch (wctx->string_validation) {
- case WRITER_STRING_VALIDATION_FAIL:
- av_log(wctx, AV_LOG_ERROR,
- "Invalid UTF-8 sequence found in string '%s'\n", src);
- ret = AVERROR_INVALIDDATA;
- goto end;
- break;
-
- case WRITER_STRING_VALIDATION_REPLACE:
- av_bprintf(&dstbuf, "%s", wctx->string_validation_replacement);
- break;
- }
- }
-
- if (!invalid || wctx->string_validation == WRITER_STRING_VALIDATION_IGNORE)
- av_bprint_append_data(&dstbuf, p0, p-p0);
- }
-
- if (invalid_chars_nb && wctx->string_validation == WRITER_STRING_VALIDATION_REPLACE) {
- av_log(wctx, AV_LOG_WARNING,
- "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n",
- invalid_chars_nb, src, wctx->string_validation_replacement);
- }
-
-end:
- av_bprint_finalize(&dstbuf, dstp);
- return ret;
-}
-
-#define PRINT_STRING_OPT 1
-#define PRINT_STRING_VALIDATE 2
-
-static inline int writer_print_string(WriterContext *wctx,
- const char *key, const char *val, int flags)
-{
- const struct section *section = wctx->section[wctx->level];
- int ret = 0;
-
- if (show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
- (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
- && (flags & PRINT_STRING_OPT)
- && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS)))
- return 0;
-
- if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
- if (flags & PRINT_STRING_VALIDATE) {
- char *key1 = NULL, *val1 = NULL;
- ret = validate_string(wctx, &key1, key);
- if (ret < 0) goto end;
- ret = validate_string(wctx, &val1, val);
- if (ret < 0) goto end;
- wctx->writer->print_string(wctx, key1, val1);
- end:
- if (ret < 0) {
- av_log(wctx, AV_LOG_ERROR,
- "Invalid key=value string combination %s=%s in section %s\n",
- key, val, section->unique_name);
- }
- av_free(key1);
- av_free(val1);
- } else {
- wctx->writer->print_string(wctx, key, val);
- }
-
- wctx->nb_item[wctx->level]++;
- }
-
- return ret;
-}
-
-static inline void writer_print_rational(WriterContext *wctx,
- const char *key, AVRational q, char sep)
-{
- AVBPrint buf;
- av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
- av_bprintf(&buf, "%d%c%d", q.num, sep, q.den);
- writer_print_string(wctx, key, buf.str, 0);
-}
-
-static void writer_print_time(WriterContext *wctx, const char *key,
- int64_t ts, const AVRational *time_base, int is_duration)
-{
- char buf[128];
-
- if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
- writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
- } else {
- double d = ts * av_q2d(*time_base);
- struct unit_value uv;
- uv.val.d = d;
- uv.unit = unit_second_str;
- value_string(buf, sizeof(buf), uv);
- writer_print_string(wctx, key, buf, 0);
- }
-}
-
-static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
-{
- if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
- writer_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
- } else {
- writer_print_integer(wctx, key, ts);
- }
-}
-
-static void writer_print_data(WriterContext *wctx, const char *name,
- const uint8_t *data, int size)
-{
- AVBPrint bp;
- int offset = 0, l, i;
-
- av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
- av_bprintf(&bp, "\n");
- while (size) {
- av_bprintf(&bp, "%08x: ", offset);
- l = FFMIN(size, 16);
- for (i = 0; i < l; i++) {
- av_bprintf(&bp, "%02x", data[i]);
- if (i & 1)
- av_bprintf(&bp, " ");
- }
- av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
- for (i = 0; i < l; i++)
- av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
- av_bprintf(&bp, "\n");
- offset += l;
- data += l;
- size -= l;
- }
- writer_print_string(wctx, name, bp.str, 0);
- av_bprint_finalize(&bp, NULL);
-}
-
-static void writer_print_data_hash(WriterContext *wctx, const char *name,
- const uint8_t *data, int size)
-{
- char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
-
- if (!hash)
- return;
- av_hash_init(hash);
- av_hash_update(hash, data, size);
- snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(hash));
- p = buf + strlen(buf);
- av_hash_final_hex(hash, p, buf + sizeof(buf) - p);
- writer_print_string(wctx, name, buf, 0);
-}
-
-static void writer_print_integers(WriterContext *wctx, const char *name,
- uint8_t *data, int size, const char *format,
- int columns, int bytes, int offset_add)
-{
- AVBPrint bp;
- int offset = 0, l, i;
-
- av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
- av_bprintf(&bp, "\n");
- while (size) {
- av_bprintf(&bp, "%08x: ", offset);
- l = FFMIN(size, columns);
- for (i = 0; i < l; i++) {
- if (bytes == 1) av_bprintf(&bp, format, *data);
- else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data));
- else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data));
- data += bytes;
- size --;
- }
- av_bprintf(&bp, "\n");
- offset += offset_add;
- }
- writer_print_string(wctx, name, bp.str, 0);
- av_bprint_finalize(&bp, NULL);
-}
-
-#define writer_w8(wctx_, b_) (wctx_)->writer_w8(wctx_, b_)
-#define writer_put_str(wctx_, str_) (wctx_)->writer_put_str(wctx_, str_)
-#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer_printf(wctx_, fmt_, __VA_ARGS__)
-
-#define MAX_REGISTERED_WRITERS_NB 64
-
-static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
-
-static int writer_register(const Writer *writer)
-{
- static int next_registered_writer_idx = 0;
-
- if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB)
- return AVERROR(ENOMEM);
-
- registered_writers[next_registered_writer_idx++] = writer;
- return 0;
-}
-
-static const Writer *writer_get_by_name(const char *name)
-{
- int i;
-
- for (i = 0; registered_writers[i]; i++)
- if (!strcmp(registered_writers[i]->name, name))
- return registered_writers[i];
-
- return NULL;
-}
-
-
-/* WRITERS */
-
-#define DEFINE_WRITER_CLASS(name) \
-static const char *name##_get_name(void *ctx) \
-{ \
- return #name ; \
-} \
-static const AVClass name##_class = { \
- .class_name = #name, \
- .item_name = name##_get_name, \
- .option = name##_options \
-}
-
-/* Default output */
-
-typedef struct DefaultContext {
- const AVClass *class;
- int nokey;
- int noprint_wrappers;
- int nested_section[SECTION_MAX_NB_LEVELS];
-} DefaultContext;
-
-#undef OFFSET
-#define OFFSET(x) offsetof(DefaultContext, x)
-
-static const AVOption default_options[] = {
- { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- { "nw", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {NULL},
-};
-
-DEFINE_WRITER_CLASS(default);
-
-/* lame uppercasing routine, assumes the string is lower case ASCII */
-static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
-{
- int i;
- for (i = 0; src[i] && i < dst_size-1; i++)
- dst[i] = av_toupper(src[i]);
- dst[i] = 0;
- return dst;
-}
-
-static void default_print_section_header(WriterContext *wctx, const void *data)
-{
- DefaultContext *def = wctx->priv;
- char buf[32];
- const struct section *section = wctx->section[wctx->level];
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
-
- av_bprint_clear(&wctx->section_pbuf[wctx->level]);
- if (parent_section &&
- !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
- def->nested_section[wctx->level] = 1;
- av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
- wctx->section_pbuf[wctx->level-1].str,
- upcase_string(buf, sizeof(buf),
- av_x_if_null(section->element_name, section->name)));
- }
-
- if (def->noprint_wrappers || def->nested_section[wctx->level])
- return;
-
- if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
- writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name));
-}
-
-static void default_print_section_footer(WriterContext *wctx)
-{
- DefaultContext *def = wctx->priv;
- const struct section *section = wctx->section[wctx->level];
- char buf[32];
-
- if (def->noprint_wrappers || def->nested_section[wctx->level])
- return;
-
- if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
- writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
-}
-
-static void default_print_str(WriterContext *wctx, const char *key, const char *value)
-{
- DefaultContext *def = wctx->priv;
-
- if (!def->nokey)
- writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
- writer_printf(wctx, "%s\n", value);
-}
-
-static void default_print_int(WriterContext *wctx, const char *key, int64_t value)
-{
- DefaultContext *def = wctx->priv;
-
- if (!def->nokey)
- writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
- writer_printf(wctx, "%"PRId64"\n", value);
-}
-
-static const Writer default_writer = {
- .name = "default",
- .priv_size = sizeof(DefaultContext),
- .print_section_header = default_print_section_header,
- .print_section_footer = default_print_section_footer,
- .print_integer = default_print_int,
- .print_string = default_print_str,
- .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
- .priv_class = &default_class,
-};
-
-/* Compact output */
-
-/**
- * Apply C-language-like string escaping.
- */
-static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
-{
- const char *p;
-
- for (p = src; *p; p++) {
- switch (*p) {
- 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 '\\': av_bprintf(dst, "%s", "\\\\"); break;
- default:
- if (*p == sep)
- av_bprint_chars(dst, '\\', 1);
- av_bprint_chars(dst, *p, 1);
- }
- }
- return dst->str;
-}
-
-/**
- * Quote fields containing special characters, check RFC4180.
- */
-static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
-{
- char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
- int needs_quoting = !!src[strcspn(src, meta_chars)];
-
- if (needs_quoting)
- av_bprint_chars(dst, '"', 1);
-
- for (; *src; src++) {
- if (*src == '"')
- av_bprint_chars(dst, '"', 1);
- av_bprint_chars(dst, *src, 1);
- }
- if (needs_quoting)
- av_bprint_chars(dst, '"', 1);
- return dst->str;
-}
-
-static const char *none_escape_str(AVBPrint *dst, 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;
- int print_section;
- char *escape_mode_str;
- const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
- int nested_section[SECTION_MAX_NB_LEVELS];
- int has_nested_elems[SECTION_MAX_NB_LEVELS];
- int terminate_line[SECTION_MAX_NB_LEVELS];
-} CompactContext;
-
-#undef OFFSET
-#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="|"}, 0, 0 },
- {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 },
- {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 },
- {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 },
- {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {NULL},
-};
-
-DEFINE_WRITER_CLASS(compact);
-
-static av_cold int compact_init(WriterContext *wctx)
-{
- CompactContext *compact = wctx->priv;
-
- 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];
-
- 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 void compact_print_section_header(WriterContext *wctx, const void *data)
-{
- CompactContext *compact = wctx->priv;
- const struct section *section = wctx->section[wctx->level];
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
- compact->terminate_line[wctx->level] = 1;
- compact->has_nested_elems[wctx->level] = 0;
-
- av_bprint_clear(&wctx->section_pbuf[wctx->level]);
- if (parent_section &&
- (section->flags & SECTION_FLAG_HAS_TYPE ||
- (!(section->flags & SECTION_FLAG_IS_ARRAY) &&
- !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))))) {
-
- /* define a prefix for elements not contained in an array or
- in a wrapper, or for array elements with a type */
- const char *element_name = (char *)av_x_if_null(section->element_name, section->name);
- AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level];
-
- compact->nested_section[wctx->level] = 1;
- compact->has_nested_elems[wctx->level-1] = 1;
-
- av_bprintf(section_pbuf, "%s%s",
- wctx->section_pbuf[wctx->level-1].str, element_name);
-
- if (section->flags & SECTION_FLAG_HAS_TYPE) {
- // add /TYPE to prefix
- av_bprint_chars(section_pbuf, '/', 1);
-
- // normalize section type, replace special characters and lower case
- for (const char *p = section->get_type(data); *p; p++) {
- char c =
- (*p >= '0' && *p <= '9') ||
- (*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_';
- av_bprint_chars(section_pbuf, c, 1);
- }
- }
- av_bprint_chars(section_pbuf, ':', 1);
-
- wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
- } else {
- if (parent_section && !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)) &&
- wctx->level && wctx->nb_item[wctx->level-1])
- writer_w8(wctx, compact->item_sep);
- if (compact->print_section &&
- !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
- writer_printf(wctx, "%s%c", section->name, compact->item_sep);
- }
-}
-
-static void compact_print_section_footer(WriterContext *wctx)
-{
- CompactContext *compact = wctx->priv;
-
- if (!compact->nested_section[wctx->level] &&
- compact->terminate_line[wctx->level] &&
- !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
- writer_w8(wctx, '\n');
-}
-
-static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
-{
- CompactContext *compact = wctx->priv;
- AVBPrint buf;
-
- if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
- if (!compact->nokey)
- writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx));
- av_bprint_finalize(&buf, NULL);
-}
-
-static void compact_print_int(WriterContext *wctx, const char *key, int64_t value)
-{
- CompactContext *compact = wctx->priv;
-
- if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
- if (!compact->nokey)
- writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
- writer_printf(wctx, "%"PRId64, value);
-}
-
-static const Writer compact_writer = {
- .name = "compact",
- .priv_size = sizeof(CompactContext),
- .init = compact_init,
- .print_section_header = compact_print_section_header,
- .print_section_footer = compact_print_section_footer,
- .print_integer = compact_print_int,
- .print_string = compact_print_str,
- .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
- .priv_class = &compact_class,
-};
-
-/* CSV output */
-
-#undef OFFSET
-#define OFFSET(x) offsetof(CompactContext, x)
-
-static const AVOption csv_options[] = {
- {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 },
- {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 },
- {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
- {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
- {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {NULL},
-};
-
-DEFINE_WRITER_CLASS(csv);
-
-static const Writer csv_writer = {
- .name = "csv",
- .priv_size = sizeof(CompactContext),
- .init = compact_init,
- .print_section_header = compact_print_section_header,
- .print_section_footer = compact_print_section_footer,
- .print_integer = compact_print_int,
- .print_string = compact_print_str,
- .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
- .priv_class = &csv_class,
-};
-
-/* Flat output */
-
-typedef struct FlatContext {
- const AVClass *class;
- 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="."}, 0, 0 },
- {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 },
- {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {NULL},
-};
-
-DEFINE_WRITER_CLASS(flat);
-
-static av_cold int flat_init(WriterContext *wctx)
-{
- FlatContext *flat = wctx->priv;
-
- 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;
- 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_header(WriterContext *wctx, const void *data)
-{
- FlatContext *flat = wctx->priv;
- AVBPrint *buf = &wctx->section_pbuf[wctx->level];
- const struct section *section = wctx->section[wctx->level];
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
-
- /* build section header */
- av_bprint_clear(buf);
- if (!parent_section)
- return;
- av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
-
- if (flat->hierarchical ||
- !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
- av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
-
- if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
- int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
- wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
- av_bprintf(buf, "%d%s", n, flat->sep_str);
- }
- }
-}
-
-static void flat_print_int(WriterContext *wctx, const char *key, int64_t value)
-{
- writer_printf(wctx, "%s%s=%"PRId64"\n", wctx->section_pbuf[wctx->level].str, key, value);
-}
-
-static void flat_print_str(WriterContext *wctx, const char *key, const char *value)
-{
- FlatContext *flat = wctx->priv;
- AVBPrint buf;
-
- writer_put_str(wctx, wctx->section_pbuf[wctx->level].str);
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep));
- av_bprint_clear(&buf);
- writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value));
- av_bprint_finalize(&buf, NULL);
-}
-
-static const Writer flat_writer = {
- .name = "flat",
- .priv_size = sizeof(FlatContext),
- .init = flat_init,
- .print_section_header = flat_print_section_header,
- .print_integer = flat_print_int,
- .print_string = flat_print_str,
- .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
- .priv_class = &flat_class,
-};
-
-/* INI format output */
-
-typedef struct INIContext {
- const AVClass *class;
- int hierarchical;
-} INIContext;
-
-#undef OFFSET
-#define OFFSET(x) offsetof(INIContext, x)
-
-static const AVOption ini_options[] = {
- {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
- {NULL},
-};
-
-DEFINE_WRITER_CLASS(ini);
-
-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_section_header(WriterContext *wctx, const void *data)
-{
- INIContext *ini = wctx->priv;
- AVBPrint *buf = &wctx->section_pbuf[wctx->level];
- const struct section *section = wctx->section[wctx->level];
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
-
- av_bprint_clear(buf);
- if (!parent_section) {
- writer_put_str(wctx, "# ffprobe output\n\n");
- return;
- }
-
- if (wctx->nb_item[wctx->level-1])
- writer_w8(wctx, '\n');
-
- av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
- if (ini->hierarchical ||
- !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
- av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
-
- if (parent_section->flags & SECTION_FLAG_IS_ARRAY) {
- int n = parent_section->id == SECTION_ID_PACKETS_AND_FRAMES ?
- wctx->nb_section_packet_frame : wctx->nb_item[wctx->level-1];
- av_bprintf(buf, ".%d", n);
- }
- }
-
- if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
- writer_printf(wctx, "[%s]\n", buf->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);
- writer_printf(wctx, "%s=", ini_escape_str(&buf, key));
- av_bprint_clear(&buf);
- writer_printf(wctx, "%s\n", ini_escape_str(&buf, value));
- av_bprint_finalize(&buf, NULL);
-}
-
-static void ini_print_int(WriterContext *wctx, const char *key, int64_t value)
-{
- writer_printf(wctx, "%s=%"PRId64"\n", key, value);
-}
-
-static const Writer ini_writer = {
- .name = "ini",
- .priv_size = sizeof(INIContext),
- .print_section_header = ini_print_section_header,
- .print_integer = ini_print_int,
- .print_string = ini_print_str,
- .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
- .priv_class = &ini_class,
-};
-
-/* JSON output */
-
-typedef struct JSONContext {
- const AVClass *class;
- int indent_level;
- int compact;
- const char *item_sep, *item_start_end;
-} JSONContext;
-
-#undef OFFSET
-#define OFFSET(x) offsetof(JSONContext, x)
-
-static const AVOption json_options[]= {
- { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- { NULL }
-};
-
-DEFINE_WRITER_CLASS(json);
-
-static av_cold int json_init(WriterContext *wctx)
-{
- JSONContext *json = wctx->priv;
-
- json->item_sep = json->compact ? ", " : ",\n";
- json->item_start_end = json->compact ? " " : "\n";
-
- return 0;
-}
-
-static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
-{
- static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
- static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
- const char *p;
-
- for (p = src; *p; p++) {
- char *s = strchr(json_escape, *p);
- if (s) {
- av_bprint_chars(dst, '\\', 1);
- av_bprint_chars(dst, json_subst[s - json_escape], 1);
- } else if ((unsigned char)*p < 32) {
- av_bprintf(dst, "\\u00%02x", *p & 0xff);
- } else {
- av_bprint_chars(dst, *p, 1);
- }
- }
- return dst->str;
-}
-
-#define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
-
-static void json_print_section_header(WriterContext *wctx, const void *data)
-{
- JSONContext *json = wctx->priv;
- AVBPrint buf;
- const struct section *section = wctx->section[wctx->level];
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
-
- if (wctx->level && wctx->nb_item[wctx->level-1])
- writer_put_str(wctx, ",\n");
-
- if (section->flags & SECTION_FLAG_IS_WRAPPER) {
- writer_put_str(wctx, "{\n");
- json->indent_level++;
- } else {
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
- json_escape_str(&buf, section->name, wctx);
- JSON_INDENT();
-
- json->indent_level++;
- if (section->flags & SECTION_FLAG_IS_ARRAY) {
- writer_printf(wctx, "\"%s\": [\n", buf.str);
- } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
- writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
- } else {
- writer_printf(wctx, "{%s", json->item_start_end);
-
- /* this is required so the parser can distinguish between packets and frames */
- if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
- if (!json->compact)
- JSON_INDENT();
- writer_printf(wctx, "\"type\": \"%s\"", section->name);
- wctx->nb_item[wctx->level]++;
- }
- }
- av_bprint_finalize(&buf, NULL);
- }
-}
-
-static void json_print_section_footer(WriterContext *wctx)
-{
- JSONContext *json = wctx->priv;
- const struct section *section = wctx->section[wctx->level];
-
- if (wctx->level == 0) {
- json->indent_level--;
- writer_put_str(wctx, "\n}\n");
- } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
- writer_w8(wctx, '\n');
- json->indent_level--;
- JSON_INDENT();
- writer_w8(wctx, ']');
- } else {
- writer_put_str(wctx, json->item_start_end);
- json->indent_level--;
- if (!json->compact)
- JSON_INDENT();
- writer_w8(wctx, '}');
- }
-}
-
-static inline void json_print_item_str(WriterContext *wctx,
- const char *key, const char *value)
-{
- AVBPrint buf;
-
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx));
- av_bprint_clear(&buf);
- writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
- av_bprint_finalize(&buf, NULL);
-}
-
-static void json_print_str(WriterContext *wctx, const char *key, const char *value)
-{
- JSONContext *json = wctx->priv;
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
-
- if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
- writer_put_str(wctx, json->item_sep);
- if (!json->compact)
- JSON_INDENT();
- json_print_item_str(wctx, key, value);
-}
-
-static void json_print_int(WriterContext *wctx, const char *key, int64_t value)
-{
- JSONContext *json = wctx->priv;
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
- AVBPrint buf;
-
- if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
- writer_put_str(wctx, json->item_sep);
- if (!json->compact)
- JSON_INDENT();
-
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value);
- av_bprint_finalize(&buf, NULL);
-}
-
-static const Writer json_writer = {
- .name = "json",
- .priv_size = sizeof(JSONContext),
- .init = json_init,
- .print_section_header = json_print_section_header,
- .print_section_footer = json_print_section_footer,
- .print_integer = json_print_int,
- .print_string = json_print_str,
- .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
- .priv_class = &json_class,
-};
-
-/* XML output */
-
-typedef struct XMLContext {
- const AVClass *class;
- int within_tag;
- int indent_level;
- int fully_qualified;
- int xsd_strict;
-} XMLContext;
-
-#undef OFFSET
-#define OFFSET(x) offsetof(XMLContext, x)
-
-static const AVOption xml_options[] = {
- {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
- {NULL},
-};
-
-DEFINE_WRITER_CLASS(xml);
-
-static av_cold int xml_init(WriterContext *wctx)
-{
- XMLContext *xml = wctx->priv;
-
- if (xml->xsd_strict) {
- xml->fully_qualified = 1;
-#define CHECK_COMPLIANCE(opt, opt_name) \
- if (opt) { \
- av_log(wctx, AV_LOG_ERROR, \
- "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
- "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
- return AVERROR(EINVAL); \
- }
- CHECK_COMPLIANCE(show_private_data, "private");
- CHECK_COMPLIANCE(show_value_unit, "unit");
- CHECK_COMPLIANCE(use_value_prefix, "prefix");
- }
-
- return 0;
-}
-
-#define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ')
-
-static void xml_print_section_header(WriterContext *wctx, const void *data)
-{
- XMLContext *xml = wctx->priv;
- const struct section *section = wctx->section[wctx->level];
- const struct section *parent_section = wctx->level ?
- wctx->section[wctx->level-1] : NULL;
-
- if (wctx->level == 0) {
- const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
- "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" "
- "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\"";
-
- writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- writer_printf(wctx, "<%sffprobe%s>\n",
- xml->fully_qualified ? "ffprobe:" : "",
- xml->fully_qualified ? qual : "");
- return;
- }
-
- if (xml->within_tag) {
- xml->within_tag = 0;
- writer_put_str(wctx, ">\n");
- }
-
- if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
- wctx->level && wctx->nb_item[wctx->level-1])
- writer_w8(wctx, '\n');
- xml->indent_level++;
-
- if (section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_HAS_VARIABLE_FIELDS)) {
- XML_INDENT(); writer_printf(wctx, "<%s", section->name);
-
- if (section->flags & SECTION_FLAG_HAS_TYPE) {
- AVBPrint buf;
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
- av_bprint_escape(&buf, section->get_type(data), NULL,
- AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
- writer_printf(wctx, " type=\"%s\"", buf.str);
- }
- writer_printf(wctx, ">\n", section->name);
- } else {
- XML_INDENT(); writer_printf(wctx, "<%s ", section->name);
- xml->within_tag = 1;
- }
-}
-
-static void xml_print_section_footer(WriterContext *wctx)
-{
- XMLContext *xml = wctx->priv;
- const struct section *section = wctx->section[wctx->level];
-
- if (wctx->level == 0) {
- writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
- } else if (xml->within_tag) {
- xml->within_tag = 0;
- writer_put_str(wctx, "/>\n");
- xml->indent_level--;
- } else {
- XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name);
- xml->indent_level--;
- }
-}
-
-static void xml_print_value(WriterContext *wctx, const char *key,
- const char *str, int64_t num, const int is_int)
-{
- AVBPrint buf;
- XMLContext *xml = wctx->priv;
- const struct section *section = wctx->section[wctx->level];
-
- av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
-
- if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
- xml->indent_level++;
- XML_INDENT();
- av_bprint_escape(&buf, key, NULL,
- AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
- writer_printf(wctx, "<%s key=\"%s\"",
- section->element_name, buf.str);
- av_bprint_clear(&buf);
-
- if (is_int) {
- writer_printf(wctx, " value=\"%"PRId64"\"/>\n", num);
- } else {
- av_bprint_escape(&buf, str, NULL,
- AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
- writer_printf(wctx, " value=\"%s\"/>\n", buf.str);
- }
- xml->indent_level--;
- } else {
- if (wctx->nb_item[wctx->level])
- writer_w8(wctx, ' ');
-
- if (is_int) {
- writer_printf(wctx, "%s=\"%"PRId64"\"", key, num);
- } else {
- av_bprint_escape(&buf, str, NULL,
- AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
- writer_printf(wctx, "%s=\"%s\"", key, buf.str);
- }
- }
-
- av_bprint_finalize(&buf, NULL);
-}
-
-static inline void xml_print_str(WriterContext *wctx, const char *key, const char *value) {
- xml_print_value(wctx, key, value, 0, 0);
-}
-
-static void xml_print_int(WriterContext *wctx, const char *key, int64_t value)
-{
- xml_print_value(wctx, key, NULL, value, 1);
-}
-
-static Writer xml_writer = {
- .name = "xml",
- .priv_size = sizeof(XMLContext),
- .init = xml_init,
- .print_section_header = xml_print_section_header,
- .print_section_footer = xml_print_section_footer,
- .print_integer = xml_print_int,
- .print_string = xml_print_str,
- .flags = WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER,
- .priv_class = &xml_class,
-};
-
-static void writer_register_all(void)
-{
- static int initialized;
-
- if (initialized)
- return;
- initialized = 1;
-
- writer_register(&default_writer);
- writer_register(&compact_writer);
- writer_register(&csv_writer);
- writer_register(&flat_writer);
- writer_register(&ini_writer);
- writer_register(&json_writer);
- writer_register(&xml_writer);
-}
#define print_fmt(k, f, ...) do { \
av_bprint_clear(&pbuf); \
av_bprintf(&pbuf, f, __VA_ARGS__); \
- writer_print_string(w, k, pbuf.str, 0); \
+ avtext_print_string(w, k, pbuf.str, 0); \
} while (0)
#define print_list_fmt(k, f, n, m, ...) do { \
@@ -2000,28 +424,19 @@ static void writer_register_all(void)
av_bprintf(&pbuf, f, __VA_ARGS__); \
} \
} \
- writer_print_string(w, k, pbuf.str, 0); \
+ avtext_print_string(w, k, pbuf.str, 0); \
} while (0)
-#define print_int(k, v) writer_print_integer(w, k, v)
-#define print_q(k, v, s) writer_print_rational(w, k, v, s)
-#define print_str(k, v) writer_print_string(w, k, v, 0)
-#define print_str_opt(k, v) writer_print_string(w, k, v, PRINT_STRING_OPT)
-#define print_str_validate(k, v) writer_print_string(w, k, v, PRINT_STRING_VALIDATE)
-#define print_time(k, v, tb) writer_print_time(w, k, v, tb, 0)
-#define print_ts(k, v) writer_print_ts(w, k, v, 0)
-#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1)
-#define print_duration_ts(k, v) writer_print_ts(w, k, v, 1)
-#define print_val(k, v, u) do { \
- struct unit_value uv; \
- uv.val.i = v; \
- uv.unit = u; \
- writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \
-} while (0)
-
-#define print_section_header(s) writer_print_section_header(w, NULL, s)
-#define print_section_header_data(s, d) writer_print_section_header(w, d, s)
-#define print_section_footer(s) writer_print_section_footer(w, s)
+#define print_int(k, v) avtext_print_integer(w, k, v)
+#define print_q(k, v, s) avtext_print_rational(w, k, v, s)
+#define print_str(k, v) avtext_print_string(w, k, v, 0)
+#define print_str_opt(k, v) avtext_print_string(w, k, v, AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
+#define print_str_validate(k, v) avtext_print_string(w, k, v, AV_TEXTFORMAT_PRINT_STRING_VALIDATE)
+#define print_time(k, v, tb) avtext_print_time(w, k, v, tb, 0)
+#define print_ts(k, v) avtext_print_ts(w, k, v, 0)
+#define print_duration_time(k, v, tb) avtext_print_time(w, k, v, tb, 1)
+#define print_duration_ts(k, v) avtext_print_ts(w, k, v, 1)
+#define print_val(k, v, u) avtext_print_unit_int(w, k, v, u)
#define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n) \
{ \
@@ -2529,7 +944,7 @@ static void print_pkt_side_data(WriterContext *w,
double rotation = av_display_rotation_get((int32_t *)sd->data);
if (isnan(rotation))
rotation = 0;
- writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
+ avtext_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
print_int("rotation", rotation);
} else if (sd->type == AV_PKT_DATA_STEREO3D) {
const AVStereo3D *stereo = (AVStereo3D *)sd->data;
@@ -2626,8 +1041,8 @@ static void print_pkt_side_data(WriterContext *w,
} else if (sd->type == AV_PKT_DATA_WEBVTT_IDENTIFIER ||
sd->type == AV_PKT_DATA_WEBVTT_SETTINGS) {
if (do_show_data)
- writer_print_data(w, "data", sd->data, sd->size);
- writer_print_data_hash(w, "data_hash", sd->data, sd->size);
+ avtext_print_data(w, "data", sd->data, sd->size);
+ avtext_print_data_hash(w, "data_hash", sd->data, sd->size);
} else if (sd->type == AV_PKT_DATA_FRAME_CROPPING && sd->size >= sizeof(uint32_t) * 4) {
print_int("crop_top", AV_RL32(sd->data));
print_int("crop_bottom", AV_RL32(sd->data + 4));
@@ -2754,7 +1169,6 @@ static int show_log(WriterContext *w, int section_ids, int section_id, int log_l
static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
{
- char val_str[128];
AVStream *st = ifile->streams[pkt->stream_index].st;
AVBPrint pbuf;
const char *s;
@@ -2780,8 +1194,8 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
pkt->flags & AV_PKT_FLAG_DISCARD ? 'D' : '_',
pkt->flags & AV_PKT_FLAG_CORRUPT ? 'C' : '_');
if (do_show_data)
- writer_print_data(w, "data", pkt->data, pkt->size);
- writer_print_data_hash(w, "data_hash", pkt->data, pkt->size);
+ avtext_print_data(w, "data", pkt->data, pkt->size);
+ avtext_print_data_hash(w, "data_hash", pkt->data, pkt->size);
if (pkt->side_data_elems) {
size_t size;
@@ -2850,7 +1264,7 @@ static void print_frame_side_data(WriterContext *w,
double rotation = av_display_rotation_get((int32_t *)sd->data);
if (isnan(rotation))
rotation = 0;
- writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
+ avtext_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1);
print_int("rotation", rotation);
} else if (sd->type == AV_FRAME_DATA_AFD && sd->size > 0) {
print_int("active_format", *sd->data);
@@ -3450,12 +1864,12 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
if (nb_streams_packets[stream_idx]) print_fmt ("nb_read_packets", "%"PRIu64, nb_streams_packets[stream_idx]);
else print_str_opt("nb_read_packets", "N/A");
if (do_show_data)
- writer_print_data(w, "extradata", par->extradata,
+ avtext_print_data(w, "extradata", par->extradata,
par->extradata_size);
if (par->extradata_size > 0) {
print_int("extradata_size", par->extradata_size);
- writer_print_data_hash(w, "extradata_hash", par->extradata,
+ avtext_print_data_hash(w, "extradata_hash", par->extradata,
par->extradata_size);
}
@@ -3829,7 +2243,6 @@ static int show_chapters(WriterContext *w, InputFile *ifile)
static int show_format(WriterContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
- char val_str[128];
int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
int ret = 0;
@@ -4005,7 +2418,7 @@ static void close_input_file(InputFile *ifile)
avformat_close_input(&ifile->fmt_ctx);
}
-static int probe_file(WriterContext *wctx, const char *filename,
+static int probe_file(WriterContext *tctx, const char *filename,
const char *print_filename)
{
InputFile ifile = { 0 };
@@ -4047,40 +2460,40 @@ static int probe_file(WriterContext *wctx, const char *filename,
if (do_read_frames || do_read_packets) {
if (do_show_frames && do_show_packets &&
- wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
+ tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT)
section_id = SECTION_ID_PACKETS_AND_FRAMES;
else if (do_show_packets && !do_show_frames)
section_id = SECTION_ID_PACKETS;
else // (!do_show_packets && do_show_frames)
section_id = SECTION_ID_FRAMES;
if (do_show_frames || do_show_packets)
- writer_print_section_header(wctx, NULL, section_id);
- ret = read_packets(wctx, &ifile);
+ writer_print_section_header(tctx, NULL, section_id);
+ ret = read_packets(tctx, &ifile);
if (do_show_frames || do_show_packets)
- writer_print_section_footer(wctx);
+ writer_print_section_footer(tctx);
CHECK_END;
}
if (do_show_programs) {
- ret = show_programs(wctx, &ifile);
+ ret = show_programs(tctx, &ifile);
CHECK_END;
}
if (do_show_stream_groups) {
- ret = show_stream_groups(wctx, &ifile);
+ ret = show_stream_groups(tctx, &ifile);
CHECK_END;
}
if (do_show_streams) {
- ret = show_streams(wctx, &ifile);
+ ret = show_streams(tctx, &ifile);
CHECK_END;
}
if (do_show_chapters) {
- ret = show_chapters(wctx, &ifile);
+ ret = show_chapters(tctx, &ifile);
CHECK_END;
}
if (do_show_format) {
- ret = show_format(wctx, &ifile);
+ ret = show_format(tctx, &ifile);
CHECK_END;
}
@@ -4229,11 +2642,11 @@ static int opt_format(void *optctx, const char *opt, const char *arg)
static inline void mark_section_show_entries(SectionID section_id,
int show_all_entries, AVDictionary *entries)
{
- struct section *section = §ions[section_id];
+ struct AVTextFormatSection *section = §ions[section_id];
section->show_all_entries = show_all_entries;
if (show_all_entries) {
- for (const SectionID *id = section->children_ids; *id != -1; id++)
+ for (const int *id = section->children_ids; *id != -1; id++)
mark_section_show_entries(*id, show_all_entries, entries);
} else {
av_dict_copy(§ion->entries_to_show, entries, 0);
@@ -4246,7 +2659,7 @@ static int match_section(const char *section_name,
int i, ret = 0;
for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) {
- const struct section *section = §ions[i];
+ const struct AVTextFormatSection *section = §ions[i];
if (!strcmp(section_name, section->name) ||
(section->unique_name && !strcmp(section_name, section->unique_name))) {
av_log(NULL, AV_LOG_DEBUG,
@@ -4518,13 +2931,13 @@ static int opt_pretty(void *optctx, const char *opt, const char *arg)
static void print_section(SectionID id, int level)
{
- const SectionID *pid;
- const struct section *section = §ions[id];
+ const int *pid;
+ const struct AVTextFormatSection *section = §ions[id];
printf("%c%c%c%c",
- section->flags & SECTION_FLAG_IS_WRAPPER ? 'W' : '.',
- section->flags & SECTION_FLAG_IS_ARRAY ? 'A' : '.',
- section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS ? 'V' : '.',
- section->flags & SECTION_FLAG_HAS_TYPE ? 'T' : '.');
+ section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER ? 'W' : '.',
+ section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY ? 'A' : '.',
+ section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS ? 'V' : '.',
+ section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE ? 'T' : '.');
printf("%*c %s", level * 4, ' ', section->name);
if (section->unique_name)
printf("/%s", section->unique_name);
@@ -4627,10 +3040,10 @@ static const OptionDef real_options[] = {
static inline int check_section_show_entries(int section_id)
{
- struct section *section = §ions[section_id];
+ struct AVTextFormatSection *section = §ions[section_id];
if (sections[section_id].show_all_entries || sections[section_id].entries_to_show)
return 1;
- for (const SectionID *id = section->children_ids; *id != -1; id++)
+ for (const int *id = section->children_ids; *id != -1; id++)
if (check_section_show_entries(*id))
return 1;
return 0;
@@ -4643,10 +3056,11 @@ static inline int check_section_show_entries(int section_id)
int main(int argc, char **argv)
{
- const Writer *w;
- WriterContext *wctx;
+ const AVTextFormatter *f;
+ WriterContext *tctx;
+ AVTextWriterContext *wctx;
char *buf;
- char *w_name = NULL, *w_args = NULL;
+ char *f_name = NULL, *f_args = NULL;
int ret, input_ret, i;
init_dynload();
@@ -4708,58 +3122,51 @@ int main(int argc, char **argv)
goto end;
}
- writer_register_all();
-
if (!output_format)
output_format = av_strdup("default");
if (!output_format) {
ret = AVERROR(ENOMEM);
goto end;
}
- w_name = av_strtok(output_format, "=", &buf);
- if (!w_name) {
+ f_name = av_strtok(output_format, "=", &buf);
+ if (!f_name) {
av_log(NULL, AV_LOG_ERROR,
"No name specified for the output format\n");
ret = AVERROR(EINVAL);
goto end;
}
- w_args = buf;
-
- if (show_data_hash) {
- if ((ret = av_hash_alloc(&hash, show_data_hash)) < 0) {
- if (ret == AVERROR(EINVAL)) {
- const char *n;
- av_log(NULL, AV_LOG_ERROR,
- "Unknown hash algorithm '%s'\nKnown algorithms:",
- show_data_hash);
- for (i = 0; (n = av_hash_names(i)); i++)
- av_log(NULL, AV_LOG_ERROR, " %s", n);
- av_log(NULL, AV_LOG_ERROR, "\n");
- }
- goto end;
- }
- }
+ f_args = buf;
- w = writer_get_by_name(w_name);
- if (!w) {
- av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
+ f = avtext_get_formatter_by_name(f_name);
+ if (!f) {
+ av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", f_name);
ret = AVERROR(EINVAL);
goto end;
}
- if ((ret = writer_open(&wctx, w, w_args,
- sections, FF_ARRAY_ELEMS(sections), output_filename)) >= 0) {
- if (w == &xml_writer)
- wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
+ if (output_filename) {
+ ret = avtextwriter_create_file(&wctx, output_filename, 1);
+ } else
+ ret = avtextwriter_create_stdout(&wctx);
- writer_print_section_header(wctx, NULL, SECTION_ID_ROOT);
+ if (ret < 0)
+ goto end;
+
+ if ((ret = avtext_context_open(&tctx, f, wctx, f_args,
+ sections, FF_ARRAY_ELEMS(sections), show_value_unit,
+ use_value_prefix, use_byte_value_binary_prefix, use_value_sexagesimal_format,
+ show_optional_fields, show_data_hash)) >= 0) {
+ if (f == &avtextformatter_xml)
+ tctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
+
+ writer_print_section_header(tctx, NULL, SECTION_ID_ROOT);
if (do_show_program_version)
- ffprobe_show_program_version(wctx);
+ ffprobe_show_program_version(tctx);
if (do_show_library_versions)
- ffprobe_show_library_versions(wctx);
+ ffprobe_show_library_versions(tctx);
if (do_show_pixel_formats)
- ffprobe_show_pixel_formats(wctx);
+ ffprobe_show_pixel_formats(tctx);
if (!input_filename &&
((do_show_format || do_show_programs || do_show_stream_groups || do_show_streams || do_show_chapters || do_show_packets || do_show_error) ||
@@ -4769,17 +3176,22 @@ int main(int argc, char **argv)
av_log(NULL, AV_LOG_ERROR, "Use -h to get full help or, even better, run 'man %s'.\n", program_name);
ret = AVERROR(EINVAL);
} else if (input_filename) {
- ret = probe_file(wctx, input_filename, print_input_filename);
+ ret = probe_file(tctx, input_filename, print_input_filename);
if (ret < 0 && do_show_error)
- show_error(wctx, ret);
+ show_error(tctx, ret);
}
input_ret = ret;
- writer_print_section_footer(wctx);
- ret = writer_close(&wctx);
+ avtext_print_section_footer(tctx);
+
+ ret = avtextwriter_context_close(&wctx);
+ if (ret < 0)
+ av_log(NULL, AV_LOG_ERROR, "Writing output failed (closing writer): %s\n", av_err2str(ret));
+
+ ret = avtext_context_close(&tctx);
if (ret < 0)
- av_log(NULL, AV_LOG_ERROR, "Writing output failed: %s\n", av_err2str(ret));
+ av_log(NULL, AV_LOG_ERROR, "Writing output failed (closing formatter): %s\n", av_err2str(ret));
ret = FFMIN(ret, input_ret);
}
@@ -4790,7 +3202,6 @@ end:
av_freep(&input_filename);
av_freep(&print_input_filename);
av_freep(&read_intervals);
- av_hash_freep(&hash);
uninit_opts();
for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread
* [FFmpeg-devel] [PATCH v3 3/7] fftools/ffprobe: Rename writer_print_section_* and WriterContext
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 1/7] fftools/textformat: Extract and generalize textformat api from ffprobe.c softworkz
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 2/7] fftools/ffprobe: Change to use textformat api softworkz
@ 2025-03-01 10:02 ` softworkz
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 4/7] fftools/ffmpeg_filter: Move some declaration to new header file softworkz
` (3 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:02 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
separated for better clarity of the preceding commit
Signed-off-by: softworkz <softworkz@hotmail.com>
ren
---
fftools/ffprobe.c | 361 +++++++++++++++++++++++-----------------------
1 file changed, 178 insertions(+), 183 deletions(-)
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index f398057df7..4a90bc4824 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -71,11 +71,6 @@
#include "libavutil/thread.h"
-// TEMPORARY DEFINES
-#define writer_print_section_header(w, d, s) avtext_print_section_header(w, d, s)
-#define writer_print_section_footer(w) avtext_print_section_footer(w)
-#define WriterContext AVTextFormatContext
-
// attached as opaque_ref to packets/frames
typedef struct FrameData {
int64_t pkt_pos;
@@ -446,25 +441,25 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
memset( (ptr) + (cur_n), 0, ((new_n) - (cur_n)) * sizeof(*(ptr)) ); \
}
-static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id)
+static inline int show_tags(AVTextFormatContext *w, AVDictionary *tags, int section_id)
{
const AVDictionaryEntry *tag = NULL;
int ret = 0;
if (!tags)
return 0;
- writer_print_section_header(w, NULL, section_id);
+ avtext_print_section_header(w, NULL, section_id);
while ((tag = av_dict_iterate(tags, tag))) {
if ((ret = print_str_validate(tag->key, tag->value)) < 0)
break;
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
+static void print_dovi_metadata(AVTextFormatContext *w, const AVDOVIMetadata *dovi)
{
if (!dovi)
return;
@@ -519,15 +514,15 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
print_int("num_x_partitions", mapping->num_x_partitions);
print_int("num_y_partitions", mapping->num_y_partitions);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
for (int c = 0; c < 3; c++) {
const AVDOVIReshapingCurve *curve = &mapping->curves[c];
- writer_print_section_header(w, "Reshaping curve", SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
+ avtext_print_section_header(w, "Reshaping curve", SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
print_list_fmt("pivots", "%"PRIu16, curve->num_pivots, 1, curve->pivots[idx]);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST);
for (int i = 0; i < curve->num_pivots - 1; i++) {
AVBPrint piece_buf;
@@ -545,7 +540,7 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
}
av_bprintf(&piece_buf, " mapping");
- writer_print_section_header(w, piece_buf.str, SECTION_ID_FRAME_SIDE_DATA_PIECE);
+ avtext_print_section_header(w, piece_buf.str, SECTION_ID_FRAME_SIDE_DATA_PIECE);
print_int("mapping_idc", curve->mapping_idc[i]);
switch (curve->mapping_idc[i]) {
case AV_DOVI_MAPPING_POLYNOMIAL:
@@ -569,11 +564,11 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
}
// SECTION_ID_FRAME_SIDE_DATA_PIECE
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
// SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
if (mapping->nlq_method_idc != AV_DOVI_NLQ_NONE) {
const AVDOVINLQParams *nlq = &mapping->nlq[c];
@@ -589,11 +584,11 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
}
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
// color metadata
print_int("dm_metadata_id", color->dm_metadata_id);
@@ -626,7 +621,7 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi)
}
}
-static void print_dynamic_hdr10_plus(WriterContext *w, const AVDynamicHDRPlus *metadata)
+static void print_dynamic_hdr10_plus(AVTextFormatContext *w, const AVDynamicHDRPlus *metadata)
{
if (!metadata)
return;
@@ -725,7 +720,7 @@ static void print_dynamic_hdr10_plus(WriterContext *w, const AVDynamicHDRPlus *m
}
}
-static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *metadata)
+static void print_dynamic_hdr_vivid(AVTextFormatContext *w, const AVDynamicHDRVivid *metadata)
{
if (!metadata)
return;
@@ -795,7 +790,7 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m
}
}
-static void print_ambient_viewing_environment(WriterContext *w,
+static void print_ambient_viewing_environment(AVTextFormatContext *w,
const AVAmbientViewingEnvironment *env)
{
if (!env)
@@ -806,7 +801,7 @@ static void print_ambient_viewing_environment(WriterContext *w,
print_q("ambient_light_y", env->ambient_light_y, '/');
}
-static void print_film_grain_params(WriterContext *w,
+static void print_film_grain_params(AVTextFormatContext *w,
const AVFilmGrainParams *fgp)
{
const char *color_range, *color_primaries, *color_trc, *color_space;
@@ -852,10 +847,10 @@ static void print_film_grain_params(WriterContext *w,
print_int("overlap_flag", aom->overlap_flag);
print_int("limit_output_range", aom->limit_output_range);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
if (aom->num_y_points) {
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
print_int("bit_depth_luma", fgp->bit_depth_luma);
print_list_fmt("y_points_value", "%"PRIu8, aom->num_y_points, 1, aom->y_points[idx][0]);
@@ -863,14 +858,14 @@ static void print_film_grain_params(WriterContext *w,
print_list_fmt("ar_coeffs_y", "%"PRId8, num_ar_coeffs_y, 1, aom->ar_coeffs_y[idx]);
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
for (int uv = 0; uv < 2; uv++) {
if (!aom->num_uv_points[uv] && !aom->chroma_scaling_from_luma)
continue;
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
print_int("bit_depth_chroma", fgp->bit_depth_chroma);
print_list_fmt("uv_points_value", "%"PRIu8, aom->num_uv_points[uv], 1, aom->uv_points[uv][idx][0]);
@@ -881,11 +876,11 @@ static void print_film_grain_params(WriterContext *w,
print_int("uv_offset", aom->uv_offset[uv]);
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
break;
}
case AV_FILM_GRAIN_PARAMS_H274: {
@@ -894,36 +889,36 @@ static void print_film_grain_params(WriterContext *w,
print_int("blending_mode_id", h274->blending_mode_id);
print_int("log2_scale_factor", h274->log2_scale_factor);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST);
for (int c = 0; c < 3; c++) {
if (!h274->component_model_present[c])
continue;
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT);
print_int(c ? "bit_depth_chroma" : "bit_depth_luma", c ? fgp->bit_depth_chroma : fgp->bit_depth_luma);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST);
for (int i = 0; i < h274->num_intensity_intervals[c]; i++) {
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE);
print_int("intensity_interval_lower_bound", h274->intensity_interval_lower_bound[c][i]);
print_int("intensity_interval_upper_bound", h274->intensity_interval_upper_bound[c][i]);
print_list_fmt("comp_model_value", "%"PRId16, h274->num_model_values[c], 1, h274->comp_model_value[c][i][idx]);
// SECTION_ID_FRAME_SIDE_DATA_PIECE
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
// SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
// SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
break;
}
}
@@ -931,14 +926,14 @@ static void print_film_grain_params(WriterContext *w,
av_bprint_finalize(&pbuf, NULL);
}
-static void print_pkt_side_data(WriterContext *w,
+static void print_pkt_side_data(AVTextFormatContext *w,
AVCodecParameters *par,
const AVPacketSideData *sd,
SectionID id_data)
{
const char *name = av_packet_side_data_name(sd->type);
- writer_print_section_header(w, sd, id_data);
+ avtext_print_section_header(w, sd, id_data);
print_str("side_data_type", name ? name : "unknown");
if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
double rotation = av_display_rotation_get((int32_t *)sd->data);
@@ -1053,7 +1048,7 @@ static void print_pkt_side_data(WriterContext *w,
}
}
-static void print_private_data(WriterContext *w, void *priv_data)
+static void print_private_data(AVTextFormatContext *w, void *priv_data)
{
const AVOption *opt = NULL;
while (opt = av_opt_next(priv_data, opt)) {
@@ -1066,7 +1061,7 @@ static void print_private_data(WriterContext *w, void *priv_data)
}
}
-static void print_color_range(WriterContext *w, enum AVColorRange color_range)
+static void print_color_range(AVTextFormatContext *w, enum AVColorRange color_range)
{
const char *val = av_color_range_name(color_range);
if (!val || color_range == AVCOL_RANGE_UNSPECIFIED) {
@@ -1076,7 +1071,7 @@ static void print_color_range(WriterContext *w, enum AVColorRange color_range)
}
}
-static void print_color_space(WriterContext *w, enum AVColorSpace color_space)
+static void print_color_space(AVTextFormatContext *w, enum AVColorSpace color_space)
{
const char *val = av_color_space_name(color_space);
if (!val || color_space == AVCOL_SPC_UNSPECIFIED) {
@@ -1086,7 +1081,7 @@ static void print_color_space(WriterContext *w, enum AVColorSpace color_space)
}
}
-static void print_primaries(WriterContext *w, enum AVColorPrimaries color_primaries)
+static void print_primaries(AVTextFormatContext *w, enum AVColorPrimaries color_primaries)
{
const char *val = av_color_primaries_name(color_primaries);
if (!val || color_primaries == AVCOL_PRI_UNSPECIFIED) {
@@ -1096,7 +1091,7 @@ static void print_primaries(WriterContext *w, enum AVColorPrimaries color_primar
}
}
-static void print_color_trc(WriterContext *w, enum AVColorTransferCharacteristic color_trc)
+static void print_color_trc(AVTextFormatContext *w, enum AVColorTransferCharacteristic color_trc)
{
const char *val = av_color_transfer_name(color_trc);
if (!val || color_trc == AVCOL_TRC_UNSPECIFIED) {
@@ -1106,7 +1101,7 @@ static void print_color_trc(WriterContext *w, enum AVColorTransferCharacteristic
}
}
-static void print_chroma_location(WriterContext *w, enum AVChromaLocation chroma_location)
+static void print_chroma_location(AVTextFormatContext *w, enum AVChromaLocation chroma_location)
{
const char *val = av_chroma_location_name(chroma_location);
if (!val || chroma_location == AVCHROMA_LOC_UNSPECIFIED) {
@@ -1132,7 +1127,7 @@ static void clear_log(int need_lock)
ff_mutex_unlock(&log_mutex);
}
-static int show_log(WriterContext *w, int section_ids, int section_id, int log_level)
+static int show_log(AVTextFormatContext *w, int section_ids, int section_id, int log_level)
{
int i;
ff_mutex_lock(&log_mutex);
@@ -1140,11 +1135,11 @@ static int show_log(WriterContext *w, int section_ids, int section_id, int log_l
ff_mutex_unlock(&log_mutex);
return 0;
}
- writer_print_section_header(w, NULL, section_ids);
+ avtext_print_section_header(w, NULL, section_ids);
for (i=0; i<log_buffer_size; i++) {
if (log_buffer[i].log_level <= log_level) {
- writer_print_section_header(w, NULL, section_id);
+ avtext_print_section_header(w, NULL, section_id);
print_str("context", log_buffer[i].context_name);
print_int("level", log_buffer[i].log_level);
print_int("category", log_buffer[i].category);
@@ -1156,18 +1151,18 @@ static int show_log(WriterContext *w, int section_ids, int section_id, int log_l
print_str_opt("parent_category", "N/A");
}
print_str("message", log_buffer[i].log_message);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
}
clear_log(0);
ff_mutex_unlock(&log_mutex);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return 0;
}
-static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
+static void show_packet(AVTextFormatContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
{
AVStream *st = ifile->streams[pkt->stream_index].st;
AVBPrint pbuf;
@@ -1175,7 +1170,7 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, SECTION_ID_PACKET);
+ avtext_print_section_header(w, NULL, SECTION_ID_PACKET);
s = av_get_media_type_string(st->codecpar->codec_type);
if (s) print_str ("codec_type", s);
@@ -1209,29 +1204,29 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
av_dict_free(&dict);
}
- writer_print_section_header(w, NULL, SECTION_ID_PACKET_SIDE_DATA_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_PACKET_SIDE_DATA_LIST);
for (int i = 0; i < pkt->side_data_elems; i++) {
print_pkt_side_data(w, st->codecpar, &pkt->side_data[i],
SECTION_ID_PACKET_SIDE_DATA);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
fflush(stdout);
}
-static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
+static void show_subtitle(AVTextFormatContext *w, AVSubtitle *sub, AVStream *stream,
AVFormatContext *fmt_ctx)
{
AVBPrint pbuf;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, SECTION_ID_SUBTITLE);
+ avtext_print_section_header(w, NULL, SECTION_ID_SUBTITLE);
print_str ("media_type", "subtitle");
print_ts ("pts", sub->pts);
@@ -1241,23 +1236,23 @@ static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
print_int ("end_display_time", sub->end_display_time);
print_int ("num_rects", sub->num_rects);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
fflush(stdout);
}
-static void print_frame_side_data(WriterContext *w,
+static void print_frame_side_data(AVTextFormatContext *w,
const AVFrame *frame,
const AVStream *stream)
{
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_LIST);
for (int i = 0; i < frame->nb_side_data; i++) {
const AVFrameSideData *sd = frame->side_data[i];
const char *name;
- writer_print_section_header(w, sd, SECTION_ID_FRAME_SIDE_DATA);
+ avtext_print_section_header(w, sd, SECTION_ID_FRAME_SIDE_DATA);
name = av_frame_side_data_name(sd->type);
print_str("side_data_type", name ? name : "unknown");
if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) {
@@ -1275,15 +1270,15 @@ static void print_frame_side_data(WriterContext *w,
} else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size == 16) {
uint32_t *tc = (uint32_t*)sd->data;
int m = FFMIN(tc[0],3);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST);
for (int j = 1; j <= m ; j++) {
char tcbuf[AV_TIMECODE_STR_SIZE];
av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, tc[j], 0, 0);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE);
print_str("value", tcbuf);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
} else if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) {
AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data;
@@ -1328,12 +1323,12 @@ static void print_frame_side_data(WriterContext *w,
} else if (sd->type == AV_FRAME_DATA_VIEW_ID) {
print_int("view_id", *(int*)sd->data);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
-static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
+static void show_frame(AVTextFormatContext *w, AVFrame *frame, AVStream *stream,
AVFormatContext *fmt_ctx)
{
FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL;
@@ -1343,7 +1338,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, SECTION_ID_FRAME);
+ avtext_print_section_header(w, NULL, SECTION_ID_FRAME);
s = av_get_media_type_string(stream->codecpar->codec_type);
if (s) print_str ("media_type", s);
@@ -1415,13 +1410,13 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
if (frame->nb_side_data)
print_frame_side_data(w, frame, stream);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
fflush(stdout);
}
-static av_always_inline int process_frame(WriterContext *w,
+static av_always_inline int process_frame(AVTextFormatContext *w,
InputFile *ifile,
AVFrame *frame, const AVPacket *pkt,
int *packet_new)
@@ -1518,7 +1513,7 @@ static void log_read_interval(const ReadInterval *interval, void *log_ctx, int l
av_log(log_ctx, log_level, "\n");
}
-static int read_interval_packets(WriterContext *w, InputFile *ifile,
+static int read_interval_packets(AVTextFormatContext *w, InputFile *ifile,
const ReadInterval *interval, int64_t *cur_ts)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
@@ -1643,7 +1638,7 @@ end:
return ret;
}
-static int read_packets(WriterContext *w, InputFile *ifile)
+static int read_packets(AVTextFormatContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int i, ret = 0;
@@ -1663,22 +1658,22 @@ static int read_packets(WriterContext *w, InputFile *ifile)
return ret;
}
-static void print_dispositions(WriterContext *w, uint32_t disposition, SectionID section_id)
+static void print_dispositions(AVTextFormatContext *w, uint32_t disposition, SectionID section_id)
{
- writer_print_section_header(w, NULL, section_id);
+ avtext_print_section_header(w, NULL, section_id);
for (int i = 0; i < sizeof(disposition) * CHAR_BIT; i++) {
const char *disposition_str = av_disposition_to_string(1U << i);
if (disposition_str)
print_int(disposition_str, !!(disposition & (1U << i)));
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
#define IN_PROGRAM 1
#define IN_STREAM_GROUP 2
-static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int container)
+static int show_stream(AVTextFormatContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int container)
{
AVStream *stream = ist->st;
AVCodecParameters *par;
@@ -1710,7 +1705,7 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, section_header[container]);
+ avtext_print_section_header(w, NULL, section_header[container]);
print_int("index", stream->index);
@@ -1885,45 +1880,45 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
}
if (stream->codecpar->nb_coded_side_data) {
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_SIDE_DATA_LIST);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_SIDE_DATA_LIST);
for (int i = 0; i < stream->codecpar->nb_coded_side_data; i++) {
print_pkt_side_data(w, stream->codecpar, &stream->codecpar->coded_side_data[i],
SECTION_ID_STREAM_SIDE_DATA);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
fflush(stdout);
return ret;
}
-static int show_streams(WriterContext *w, InputFile *ifile)
+static int show_streams(AVTextFormatContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int i, ret = 0;
- writer_print_section_header(w, NULL, SECTION_ID_STREAMS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAMS);
for (i = 0; i < ifile->nb_streams; i++)
if (selected_streams[i]) {
ret = show_stream(w, fmt_ctx, i, &ifile->streams[i], 0);
if (ret < 0)
break;
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program)
+static int show_program(AVTextFormatContext *w, InputFile *ifile, AVProgram *program)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int i, ret = 0;
- writer_print_section_header(w, NULL, SECTION_ID_PROGRAM);
+ avtext_print_section_header(w, NULL, SECTION_ID_PROGRAM);
print_int("program_id", program->id);
print_int("program_num", program->program_num);
print_int("nb_streams", program->nb_stream_indexes);
@@ -1934,7 +1929,7 @@ static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program)
if (ret < 0)
goto end;
- writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_STREAMS);
+ avtext_print_section_header(w, NULL, SECTION_ID_PROGRAM_STREAMS);
for (i = 0; i < program->nb_stream_indexes; i++) {
if (selected_streams[program->stream_index[i]]) {
ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], IN_PROGRAM);
@@ -1942,19 +1937,19 @@ static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program)
break;
}
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
end:
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static int show_programs(WriterContext *w, InputFile *ifile)
+static int show_programs(AVTextFormatContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int i, ret = 0;
- writer_print_section_header(w, NULL, SECTION_ID_PROGRAMS);
+ avtext_print_section_header(w, NULL, SECTION_ID_PROGRAMS);
for (i = 0; i < fmt_ctx->nb_programs; i++) {
AVProgram *program = fmt_ctx->programs[i];
if (!program)
@@ -1963,14 +1958,14 @@ static int show_programs(WriterContext *w, InputFile *ifile)
if (ret < 0)
break;
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static void print_tile_grid_params(WriterContext *w, const AVStreamGroup *stg,
+static void print_tile_grid_params(AVTextFormatContext *w, const AVStreamGroup *stg,
const AVStreamGroupTileGrid *tile_grid)
{
- writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT);
+ avtext_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT);
print_int("nb_tiles", tile_grid->nb_tiles);
print_int("coded_width", tile_grid->coded_width);
print_int("coded_height", tile_grid->coded_height);
@@ -1978,19 +1973,19 @@ static void print_tile_grid_params(WriterContext *w, const AVStreamGroup *stg,
print_int("vertical_offset", tile_grid->vertical_offset);
print_int("width", tile_grid->width);
print_int("height", tile_grid->height);
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS);
for (int i = 0; i < tile_grid->nb_tiles; i++) {
- writer_print_section_header(w, "tile_offset", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
+ avtext_print_section_header(w, "tile_offset", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
print_int("stream_index", tile_grid->offsets[i].idx);
print_int("tile_horizontal_offset", tile_grid->offsets[i].horizontal);
print_int("tile_vertical_offset", tile_grid->offsets[i].vertical);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
+ avtext_print_section_footer(w);
}
-static void print_iamf_param_definition(WriterContext *w, const char *name,
+static void print_iamf_param_definition(AVTextFormatContext *w, const char *name,
const AVIAMFParamDefinition *param, SectionID section_id)
{
SectionID subsection_id, parameter_section_id;
@@ -1998,7 +1993,7 @@ static void print_iamf_param_definition(WriterContext *w, const char *name,
av_assert0(subsection_id != -1);
parameter_section_id = sections[subsection_id].children_ids[0];
av_assert0(parameter_section_id != -1);
- writer_print_section_header(w, "IAMF Param Definition", section_id);
+ avtext_print_section_header(w, "IAMF Param Definition", section_id);
print_str("name", name);
print_int("nb_subblocks", param->nb_subblocks);
print_int("type", param->type);
@@ -2007,56 +2002,56 @@ static void print_iamf_param_definition(WriterContext *w, const char *name,
print_int("duration", param->duration);
print_int("constant_subblock_duration", param->constant_subblock_duration);
if (param->nb_subblocks > 0)
- writer_print_section_header(w, NULL, subsection_id);
+ avtext_print_section_header(w, NULL, subsection_id);
for (int i = 0; i < param->nb_subblocks; i++) {
const void *subblock = av_iamf_param_definition_get_subblock(param, i);
switch(param->type) {
case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
const AVIAMFMixGain *mix = subblock;
- writer_print_section_header(w, "IAMF Mix Gain Parameters", parameter_section_id);
+ avtext_print_section_header(w, "IAMF Mix Gain Parameters", parameter_section_id);
print_int("subblock_duration", mix->subblock_duration);
print_int("animation_type", mix->animation_type);
print_q("start_point_value", mix->start_point_value, '/');
print_q("end_point_value", mix->end_point_value, '/');
print_q("control_point_value", mix->control_point_value, '/');
print_q("control_point_relative_time", mix->control_point_relative_time, '/');
- writer_print_section_footer(w); // parameter_section_id
+ avtext_print_section_footer(w); // parameter_section_id
break;
}
case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
const AVIAMFDemixingInfo *demix = subblock;
- writer_print_section_header(w, "IAMF Demixing Info", parameter_section_id);
+ avtext_print_section_header(w, "IAMF Demixing Info", parameter_section_id);
print_int("subblock_duration", demix->subblock_duration);
print_int("dmixp_mode", demix->dmixp_mode);
- writer_print_section_footer(w); // parameter_section_id
+ avtext_print_section_footer(w); // parameter_section_id
break;
}
case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
const AVIAMFReconGain *recon = subblock;
- writer_print_section_header(w, "IAMF Recon Gain", parameter_section_id);
+ avtext_print_section_header(w, "IAMF Recon Gain", parameter_section_id);
print_int("subblock_duration", recon->subblock_duration);
- writer_print_section_footer(w); // parameter_section_id
+ avtext_print_section_footer(w); // parameter_section_id
break;
}
}
}
if (param->nb_subblocks > 0)
- writer_print_section_footer(w); // subsection_id
- writer_print_section_footer(w); // section_id
+ avtext_print_section_footer(w); // subsection_id
+ avtext_print_section_footer(w); // section_id
}
-static void print_iamf_audio_element_params(WriterContext *w, const AVStreamGroup *stg,
+static void print_iamf_audio_element_params(AVTextFormatContext *w, const AVStreamGroup *stg,
const AVIAMFAudioElement *audio_element)
{
- writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT);
+ avtext_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT);
print_int("nb_layers", audio_element->nb_layers);
print_int("audio_element_type", audio_element->audio_element_type);
print_int("default_w", audio_element->default_w);
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS);
for (int i = 0; i < audio_element->nb_layers; i++) {
const AVIAMFLayer *layer = audio_element->layers[i];
char val_str[128];
- writer_print_section_header(w, "IAMF Audio Layer", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
+ avtext_print_section_header(w, "IAMF Audio Layer", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
av_channel_layout_describe(&layer->ch_layout, val_str, sizeof(val_str));
print_str("channel_layout", val_str);
if (audio_element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) {
@@ -2064,7 +2059,7 @@ static void print_iamf_audio_element_params(WriterContext *w, const AVStreamGrou
print_q("output_gain", layer->output_gain, '/');
} else if (audio_element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE)
print_int("ambisonics_mode", layer->ambisonics_mode);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT
}
if (audio_element->demixing_info)
print_iamf_param_definition(w, "demixing_info", audio_element->demixing_info,
@@ -2072,36 +2067,36 @@ static void print_iamf_audio_element_params(WriterContext *w, const AVStreamGrou
if (audio_element->recon_gain_info)
print_iamf_param_definition(w, "recon_gain_info", audio_element->recon_gain_info,
SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENTS
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENT
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENTS
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENT
}
-static void print_iamf_submix_params(WriterContext *w, const AVIAMFSubmix *submix)
+static void print_iamf_submix_params(AVTextFormatContext *w, const AVIAMFSubmix *submix)
{
- writer_print_section_header(w, "IAMF Submix", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
+ avtext_print_section_header(w, "IAMF Submix", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
print_int("nb_elements", submix->nb_elements);
print_int("nb_layouts", submix->nb_layouts);
print_q("default_mix_gain", submix->default_mix_gain, '/');
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_PIECES);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_PIECES);
for (int i = 0; i < submix->nb_elements; i++) {
const AVIAMFSubmixElement *element = submix->elements[i];
- writer_print_section_header(w, "IAMF Submix Element", SECTION_ID_STREAM_GROUP_PIECE);
+ avtext_print_section_header(w, "IAMF Submix Element", SECTION_ID_STREAM_GROUP_PIECE);
print_int("stream_id", element->audio_element_id);
print_q("default_mix_gain", element->default_mix_gain, '/');
print_int("headphones_rendering_mode", element->headphones_rendering_mode);
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBPIECES);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBPIECES);
if (element->annotations) {
const AVDictionaryEntry *annotation = NULL;
- writer_print_section_header(w, "IAMF Annotations", SECTION_ID_STREAM_GROUP_SUBPIECE);
+ avtext_print_section_header(w, "IAMF Annotations", SECTION_ID_STREAM_GROUP_SUBPIECE);
while (annotation = av_dict_iterate(element->annotations, annotation))
print_str(annotation->key, annotation->value);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBPIECE
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBPIECE
}
if (element->element_mix_config)
print_iamf_param_definition(w, "element_mix_config", element->element_mix_config,
SECTION_ID_STREAM_GROUP_SUBPIECE);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBPIECES
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECE
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBPIECES
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECE
}
if (submix->output_mix_config)
print_iamf_param_definition(w, "output_mix_config", submix->output_mix_config,
@@ -2109,7 +2104,7 @@ static void print_iamf_submix_params(WriterContext *w, const AVIAMFSubmix *submi
for (int i = 0; i < submix->nb_layouts; i++) {
const AVIAMFSubmixLayout *layout = submix->layouts[i];
char val_str[128];
- writer_print_section_header(w, "IAMF Submix Layout", SECTION_ID_STREAM_GROUP_PIECE);
+ avtext_print_section_header(w, "IAMF Submix Layout", SECTION_ID_STREAM_GROUP_PIECE);
av_channel_layout_describe(&layout->sound_system, val_str, sizeof(val_str));
print_str("sound_system", val_str);
print_q("integrated_loudness", layout->integrated_loudness, '/');
@@ -2117,51 +2112,51 @@ static void print_iamf_submix_params(WriterContext *w, const AVIAMFSubmix *submi
print_q("true_peak", layout->true_peak, '/');
print_q("dialogue_anchored_loudness", layout->dialogue_anchored_loudness, '/');
print_q("album_anchored_loudness", layout->album_anchored_loudness, '/');
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECE
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECE
}
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECES
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECES
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT
}
-static void print_iamf_mix_presentation_params(WriterContext *w, const AVStreamGroup *stg,
+static void print_iamf_mix_presentation_params(AVTextFormatContext *w, const AVStreamGroup *stg,
const AVIAMFMixPresentation *mix_presentation)
{
- writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT);
+ avtext_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT);
print_int("nb_submixes", mix_presentation->nb_submixes);
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS);
if (mix_presentation->annotations) {
const AVDictionaryEntry *annotation = NULL;
- writer_print_section_header(w, "IAMF Annotations", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
+ avtext_print_section_header(w, "IAMF Annotations", SECTION_ID_STREAM_GROUP_SUBCOMPONENT);
while (annotation = av_dict_iterate(mix_presentation->annotations, annotation))
print_str(annotation->key, annotation->value);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT
}
for (int i = 0; i < mix_presentation->nb_submixes; i++)
print_iamf_submix_params(w, mix_presentation->submixes[i]);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENTS
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENT
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENTS
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENT
}
-static void print_stream_group_params(WriterContext *w, AVStreamGroup *stg)
+static void print_stream_group_params(AVTextFormatContext *w, AVStreamGroup *stg)
{
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_COMPONENTS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_COMPONENTS);
if (stg->type == AV_STREAM_GROUP_PARAMS_TILE_GRID)
print_tile_grid_params(w, stg, stg->params.tile_grid);
else if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
print_iamf_audio_element_params(w, stg, stg->params.iamf_audio_element);
else if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION)
print_iamf_mix_presentation_params(w, stg, stg->params.iamf_mix_presentation);
- writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENTS
+ avtext_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENTS
}
-static int show_stream_group(WriterContext *w, InputFile *ifile, AVStreamGroup *stg)
+static int show_stream_group(AVTextFormatContext *w, InputFile *ifile, AVStreamGroup *stg)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
AVBPrint pbuf;
int i, ret = 0;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP);
print_int("index", stg->index);
if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%"PRIx64, stg->id);
else print_str_opt("id", "N/A");
@@ -2182,7 +2177,7 @@ static int show_stream_group(WriterContext *w, InputFile *ifile, AVStreamGroup *
if (ret < 0)
goto end;
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_STREAMS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_STREAMS);
for (i = 0; i < stg->nb_streams; i++) {
if (selected_streams[stg->streams[i]->index]) {
ret = show_stream(w, fmt_ctx, stg->streams[i]->index, &ifile->streams[stg->streams[i]->index], IN_STREAM_GROUP);
@@ -2190,20 +2185,20 @@ static int show_stream_group(WriterContext *w, InputFile *ifile, AVStreamGroup *
break;
}
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
end:
av_bprint_finalize(&pbuf, NULL);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static int show_stream_groups(WriterContext *w, InputFile *ifile)
+static int show_stream_groups(AVTextFormatContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int i, ret = 0;
- writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUPS);
+ avtext_print_section_header(w, NULL, SECTION_ID_STREAM_GROUPS);
for (i = 0; i < fmt_ctx->nb_stream_groups; i++) {
AVStreamGroup *stg = fmt_ctx->stream_groups[i];
@@ -2211,20 +2206,20 @@ static int show_stream_groups(WriterContext *w, InputFile *ifile)
if (ret < 0)
break;
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static int show_chapters(WriterContext *w, InputFile *ifile)
+static int show_chapters(AVTextFormatContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int i, ret = 0;
- writer_print_section_header(w, NULL, SECTION_ID_CHAPTERS);
+ avtext_print_section_header(w, NULL, SECTION_ID_CHAPTERS);
for (i = 0; i < fmt_ctx->nb_chapters; i++) {
AVChapter *chapter = fmt_ctx->chapters[i];
- writer_print_section_header(w, NULL, SECTION_ID_CHAPTER);
+ avtext_print_section_header(w, NULL, SECTION_ID_CHAPTER);
print_int("id", chapter->id);
print_q ("time_base", chapter->time_base, '/');
print_int("start", chapter->start);
@@ -2233,20 +2228,20 @@ static int show_chapters(WriterContext *w, InputFile *ifile)
print_time("end_time", chapter->end, &chapter->time_base);
if (do_show_chapter_tags)
ret = show_tags(w, chapter->metadata, SECTION_ID_CHAPTER_TAGS);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
return ret;
}
-static int show_format(WriterContext *w, InputFile *ifile)
+static int show_format(AVTextFormatContext *w, InputFile *ifile)
{
AVFormatContext *fmt_ctx = ifile->fmt_ctx;
int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1;
int ret = 0;
- writer_print_section_header(w, NULL, SECTION_ID_FORMAT);
+ avtext_print_section_header(w, NULL, SECTION_ID_FORMAT);
print_str_validate("filename", fmt_ctx->url);
print_int("nb_streams", fmt_ctx->nb_streams);
print_int("nb_programs", fmt_ctx->nb_programs);
@@ -2266,17 +2261,17 @@ static int show_format(WriterContext *w, InputFile *ifile)
if (do_show_format_tags)
ret = show_tags(w, fmt_ctx->metadata, SECTION_ID_FORMAT_TAGS);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
fflush(stdout);
return ret;
}
-static void show_error(WriterContext *w, int err)
+static void show_error(AVTextFormatContext *w, int err)
{
- writer_print_section_header(w, NULL, SECTION_ID_ERROR);
+ avtext_print_section_header(w, NULL, SECTION_ID_ERROR);
print_int("code", err);
print_str("string", av_err2str(err));
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
static int open_input_file(InputFile *ifile, const char *filename,
@@ -2418,7 +2413,7 @@ static void close_input_file(InputFile *ifile)
avformat_close_input(&ifile->fmt_ctx);
}
-static int probe_file(WriterContext *tctx, const char *filename,
+static int probe_file(AVTextFormatContext *tctx, const char *filename,
const char *print_filename)
{
InputFile ifile = { 0 };
@@ -2467,10 +2462,10 @@ static int probe_file(WriterContext *tctx, const char *filename,
else // (!do_show_packets && do_show_frames)
section_id = SECTION_ID_FRAMES;
if (do_show_frames || do_show_packets)
- writer_print_section_header(tctx, NULL, section_id);
+ avtext_print_section_header(tctx, NULL, section_id);
ret = read_packets(tctx, &ifile);
if (do_show_frames || do_show_packets)
- writer_print_section_footer(tctx);
+ avtext_print_section_footer(tctx);
CHECK_END;
}
@@ -2516,18 +2511,18 @@ static void show_usage(void)
av_log(NULL, AV_LOG_INFO, "\n");
}
-static void ffprobe_show_program_version(WriterContext *w)
+static void ffprobe_show_program_version(AVTextFormatContext *w)
{
AVBPrint pbuf;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_VERSION);
+ avtext_print_section_header(w, NULL, SECTION_ID_PROGRAM_VERSION);
print_str("version", FFMPEG_VERSION);
print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers",
program_birth_year, CONFIG_THIS_YEAR);
print_str("compiler_ident", CC_IDENT);
print_str("configuration", FFMPEG_CONFIGURATION);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
av_bprint_finalize(&pbuf, NULL);
}
@@ -2536,20 +2531,20 @@ static void ffprobe_show_program_version(WriterContext *w)
do { \
if (CONFIG_##LIBNAME) { \
unsigned int version = libname##_version(); \
- writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSION); \
+ avtext_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSION); \
print_str("name", "lib" #libname); \
print_int("major", LIB##LIBNAME##_VERSION_MAJOR); \
print_int("minor", LIB##LIBNAME##_VERSION_MINOR); \
print_int("micro", LIB##LIBNAME##_VERSION_MICRO); \
print_int("version", version); \
print_str("ident", LIB##LIBNAME##_IDENT); \
- writer_print_section_footer(w); \
+ avtext_print_section_footer(w); \
} \
} while (0)
-static void ffprobe_show_library_versions(WriterContext *w)
+static void ffprobe_show_library_versions(AVTextFormatContext *w)
{
- writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSIONS);
+ avtext_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSIONS);
SHOW_LIB_VERSION(avutil, AVUTIL);
SHOW_LIB_VERSION(avcodec, AVCODEC);
SHOW_LIB_VERSION(avformat, AVFORMAT);
@@ -2558,7 +2553,7 @@ static void ffprobe_show_library_versions(WriterContext *w)
SHOW_LIB_VERSION(swscale, SWSCALE);
SHOW_LIB_VERSION(swresample, SWRESAMPLE);
SHOW_LIB_VERSION(postproc, POSTPROC);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
#define PRINT_PIX_FMT_FLAG(flagname, name) \
@@ -2566,14 +2561,14 @@ static void ffprobe_show_library_versions(WriterContext *w)
print_int(name, !!(pixdesc->flags & AV_PIX_FMT_FLAG_##flagname)); \
} while (0)
-static void ffprobe_show_pixel_formats(WriterContext *w)
+static void ffprobe_show_pixel_formats(AVTextFormatContext *w)
{
const AVPixFmtDescriptor *pixdesc = NULL;
int i, n;
- writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMATS);
+ avtext_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMATS);
while (pixdesc = av_pix_fmt_desc_next(pixdesc)) {
- writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT);
+ avtext_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT);
print_str("name", pixdesc->name);
print_int("nb_components", pixdesc->nb_components);
if ((pixdesc->nb_components >= 3) && !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB)) {
@@ -2587,7 +2582,7 @@ static void ffprobe_show_pixel_formats(WriterContext *w)
if (n) print_int ("bits_per_pixel", n);
else print_str_opt("bits_per_pixel", "N/A");
if (do_show_pixel_format_flags) {
- writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_FLAGS);
+ avtext_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_FLAGS);
PRINT_PIX_FMT_FLAG(BE, "big_endian");
PRINT_PIX_FMT_FLAG(PAL, "palette");
PRINT_PIX_FMT_FLAG(BITSTREAM, "bitstream");
@@ -2595,21 +2590,21 @@ static void ffprobe_show_pixel_formats(WriterContext *w)
PRINT_PIX_FMT_FLAG(PLANAR, "planar");
PRINT_PIX_FMT_FLAG(RGB, "rgb");
PRINT_PIX_FMT_FLAG(ALPHA, "alpha");
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
if (do_show_pixel_format_components && (pixdesc->nb_components > 0)) {
- writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENTS);
+ avtext_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENTS);
for (i = 0; i < pixdesc->nb_components; i++) {
- writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENT);
+ avtext_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENT);
print_int("index", i + 1);
print_int("bit_depth", pixdesc->comp[i].depth);
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
- writer_print_section_footer(w);
+ avtext_print_section_footer(w);
}
static int opt_show_optional_fields(void *optctx, const char *opt, const char *arg)
@@ -3057,7 +3052,7 @@ static inline int check_section_show_entries(int section_id)
int main(int argc, char **argv)
{
const AVTextFormatter *f;
- WriterContext *tctx;
+ AVTextFormatContext *tctx;
AVTextWriterContext *wctx;
char *buf;
char *f_name = NULL, *f_args = NULL;
@@ -3159,7 +3154,7 @@ int main(int argc, char **argv)
if (f == &avtextformatter_xml)
tctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
- writer_print_section_header(tctx, NULL, SECTION_ID_ROOT);
+ avtext_print_section_header(tctx, NULL, SECTION_ID_ROOT);
if (do_show_program_version)
ffprobe_show_program_version(tctx);
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread
* [FFmpeg-devel] [PATCH v3 4/7] fftools/ffmpeg_filter: Move some declaration to new header file
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
` (2 preceding siblings ...)
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 3/7] fftools/ffprobe: Rename writer_print_section_* and WriterContext softworkz
@ 2025-03-01 10:02 ` softworkz
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 5/7] avfilter/avfilter Add avfilter_link_get_hw_frames_ctx() softworkz
` (2 subsequent siblings)
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:02 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
to allow print_graph to access the information.
Signed-off-by: softworkz <softworkz@hotmail.com>
---
fftools/ffmpeg_filter.c | 188 +-------------------------------
fftools/ffmpeg_filter.h | 232 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 233 insertions(+), 187 deletions(-)
create mode 100644 fftools/ffmpeg_filter.h
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 800e2a3f06..6de4e87ade 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -21,6 +21,7 @@
#include <stdint.h>
#include "ffmpeg.h"
+#include "ffmpeg_filter.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
@@ -42,44 +43,6 @@
// FIXME private header, used for mid_pred()
#include "libavcodec/mathops.h"
-typedef struct FilterGraphPriv {
- FilterGraph fg;
-
- // name used for logging
- char log_name[32];
-
- int is_simple;
- // true when the filtergraph contains only meta filters
- // that do not modify the frame data
- int is_meta;
- // source filters are present in the graph
- int have_sources;
- int disable_conversions;
-
- unsigned nb_outputs_done;
-
- const char *graph_desc;
-
- int nb_threads;
-
- // frame for temporarily holding output from the filtergraph
- AVFrame *frame;
- // frame for sending output to the encoder
- AVFrame *frame_enc;
-
- Scheduler *sch;
- unsigned sch_idx;
-} FilterGraphPriv;
-
-static FilterGraphPriv *fgp_from_fg(FilterGraph *fg)
-{
- return (FilterGraphPriv*)fg;
-}
-
-static const FilterGraphPriv *cfgp_from_cfg(const FilterGraph *fg)
-{
- return (const FilterGraphPriv*)fg;
-}
// data that is local to the filter thread and not visible outside of it
typedef struct FilterGraphThread {
@@ -102,155 +65,6 @@ typedef struct FilterGraphThread {
uint8_t *eof_out;
} FilterGraphThread;
-typedef struct InputFilterPriv {
- InputFilter ifilter;
-
- InputFilterOptions opts;
-
- int index;
-
- AVFilterContext *filter;
-
- // used to hold submitted input
- AVFrame *frame;
-
- /* for filters that are not yet bound to an input stream,
- * this stores the input linklabel, if any */
- uint8_t *linklabel;
-
- // filter data type
- enum AVMediaType type;
- // source data type: AVMEDIA_TYPE_SUBTITLE for sub2video,
- // same as type otherwise
- enum AVMediaType type_src;
-
- int eof;
- int bound;
-
- // parameters configured for this input
- int format;
-
- int width, height;
- AVRational sample_aspect_ratio;
- enum AVColorSpace color_space;
- enum AVColorRange color_range;
-
- int sample_rate;
- AVChannelLayout ch_layout;
-
- AVRational time_base;
-
- AVFrameSideData **side_data;
- int nb_side_data;
-
- AVFifo *frame_queue;
-
- AVBufferRef *hw_frames_ctx;
-
- int displaymatrix_present;
- int displaymatrix_applied;
- int32_t displaymatrix[9];
-
- int downmixinfo_present;
- AVDownmixInfo downmixinfo;
-
- struct {
- AVFrame *frame;
-
- int64_t last_pts;
- int64_t end_pts;
-
- ///< marks if sub2video_update should force an initialization
- unsigned int initialize;
- } sub2video;
-} InputFilterPriv;
-
-static InputFilterPriv *ifp_from_ifilter(InputFilter *ifilter)
-{
- return (InputFilterPriv*)ifilter;
-}
-
-typedef struct FPSConvContext {
- AVFrame *last_frame;
- /* number of frames emitted by the video-encoding sync code */
- int64_t frame_number;
- /* history of nb_frames_prev, i.e. the number of times the
- * previous frame was duplicated by vsync code in recent
- * do_video_out() calls */
- int64_t frames_prev_hist[3];
-
- uint64_t dup_warning;
-
- int last_dropped;
- int dropped_keyframe;
-
- enum VideoSyncMethod vsync_method;
-
- AVRational framerate;
- AVRational framerate_max;
- const AVRational *framerate_supported;
- int framerate_clip;
-} FPSConvContext;
-
-typedef struct OutputFilterPriv {
- OutputFilter ofilter;
-
- int index;
-
- void *log_parent;
- char log_name[32];
-
- char *name;
-
- AVFilterContext *filter;
-
- /* desired output stream properties */
- int format;
- int width, height;
- int sample_rate;
- AVChannelLayout ch_layout;
- enum AVColorSpace color_space;
- enum AVColorRange color_range;
-
- AVFrameSideData **side_data;
- int nb_side_data;
-
- // time base in which the output is sent to our downstream
- // does not need to match the filtersink's timebase
- AVRational tb_out;
- // at least one frame with the above timebase was sent
- // to our downstream, so it cannot change anymore
- int tb_out_locked;
-
- AVRational sample_aspect_ratio;
-
- AVDictionary *sws_opts;
- AVDictionary *swr_opts;
-
- // those are only set if no format is specified and the encoder gives us multiple options
- // They point directly to the relevant lists of the encoder.
- const int *formats;
- const AVChannelLayout *ch_layouts;
- const int *sample_rates;
- const enum AVColorSpace *color_spaces;
- const enum AVColorRange *color_ranges;
-
- AVRational enc_timebase;
- int64_t trim_start_us;
- int64_t trim_duration_us;
- // offset for output timestamps, in AV_TIME_BASE_Q
- int64_t ts_offset;
- int64_t next_pts;
- FPSConvContext fps;
-
- unsigned flags;
-} OutputFilterPriv;
-
-static OutputFilterPriv *ofp_from_ofilter(OutputFilter *ofilter)
-{
- return (OutputFilterPriv*)ofilter;
-}
-
typedef struct FilterCommand {
char *target;
char *command;
diff --git a/fftools/ffmpeg_filter.h b/fftools/ffmpeg_filter.h
new file mode 100644
index 0000000000..628d272bcd
--- /dev/null
+++ b/fftools/ffmpeg_filter.h
@@ -0,0 +1,232 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FFTOOLS_FFMPEG_FILTER_H
+#define FFTOOLS_FFMPEG_FILTER_H
+
+#include "ffmpeg.h"
+
+#include <stdint.h>
+
+#include "ffmpeg_sched.h"
+#include "sync_queue.h"
+
+#include "libavfilter/avfilter.h"
+
+#include "libavutil/avutil.h"
+#include "libavutil/dict.h"
+#include "libavutil/fifo.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/rational.h"
+#include "libavutil/bprint.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/downmix_info.h"
+
+typedef struct FilterGraphPriv {
+ FilterGraph fg;
+
+ // name used for logging
+ char log_name[32];
+
+ int is_simple;
+ // true when the filtergraph contains only meta filters
+ // that do not modify the frame data
+ int is_meta;
+ // source filters are present in the graph
+ int have_sources;
+ int disable_conversions;
+
+ unsigned nb_outputs_done;
+
+ const char *graph_desc;
+
+ int nb_threads;
+
+ // frame for temporarily holding output from the filtergraph
+ AVFrame *frame;
+ // frame for sending output to the encoder
+ AVFrame *frame_enc;
+
+ Scheduler *sch;
+ unsigned sch_idx;
+
+ AVBPrint graph_print_buf;
+
+} FilterGraphPriv;
+
+static FilterGraphPriv *fgp_from_fg(FilterGraph *fg)
+{
+ return (FilterGraphPriv*)fg;
+}
+
+static const FilterGraphPriv *cfgp_from_cfg(const FilterGraph *fg)
+{
+ return (const FilterGraphPriv*)fg;
+}
+
+typedef struct InputFilterPriv {
+ InputFilter ifilter;
+
+ InputFilterOptions opts;
+
+ int index;
+
+ AVFilterContext *filter;
+
+ // used to hold submitted input
+ AVFrame *frame;
+
+ /* for filters that are not yet bound to an input stream,
+ * this stores the input linklabel, if any */
+ uint8_t *linklabel;
+
+ // filter data type
+ enum AVMediaType type;
+ // source data type: AVMEDIA_TYPE_SUBTITLE for sub2video,
+ // same as type otherwise
+ enum AVMediaType type_src;
+
+ int eof;
+ int bound;
+
+ // parameters configured for this input
+ int format;
+
+ int width, height;
+ AVRational sample_aspect_ratio;
+ enum AVColorSpace color_space;
+ enum AVColorRange color_range;
+
+ int sample_rate;
+ AVChannelLayout ch_layout;
+
+ AVRational time_base;
+
+ AVFrameSideData **side_data;
+ int nb_side_data;
+
+ AVFifo *frame_queue;
+
+ AVBufferRef *hw_frames_ctx;
+
+ int displaymatrix_present;
+ int displaymatrix_applied;
+ int32_t displaymatrix[9];
+
+ int downmixinfo_present;
+ AVDownmixInfo downmixinfo;
+
+ struct {
+ AVFrame *frame;
+
+ int64_t last_pts;
+ int64_t end_pts;
+
+ ///< marks if sub2video_update should force an initialization
+ unsigned int initialize;
+ } sub2video;
+} InputFilterPriv;
+
+static InputFilterPriv *ifp_from_ifilter(InputFilter *ifilter)
+{
+ return (InputFilterPriv*)ifilter;
+}
+
+typedef struct FPSConvContext {
+ AVFrame *last_frame;
+ /* number of frames emitted by the video-encoding sync code */
+ int64_t frame_number;
+ /* history of nb_frames_prev, i.e. the number of times the
+ * previous frame was duplicated by vsync code in recent
+ * do_video_out() calls */
+ int64_t frames_prev_hist[3];
+
+ uint64_t dup_warning;
+
+ int last_dropped;
+ int dropped_keyframe;
+
+ enum VideoSyncMethod vsync_method;
+
+ AVRational framerate;
+ AVRational framerate_max;
+ const AVRational *framerate_supported;
+ int framerate_clip;
+} FPSConvContext;
+
+
+typedef struct OutputFilterPriv {
+ OutputFilter ofilter;
+
+ int index;
+
+ void *log_parent;
+ char log_name[32];
+
+ char *name;
+
+ AVFilterContext *filter;
+
+ /* desired output stream properties */
+ int format;
+ int width, height;
+ int sample_rate;
+ AVChannelLayout ch_layout;
+ enum AVColorSpace color_space;
+ enum AVColorRange color_range;
+
+ AVFrameSideData **side_data;
+ int nb_side_data;
+
+ // time base in which the output is sent to our downstream
+ // does not need to match the filtersink's timebase
+ AVRational tb_out;
+ // at least one frame with the above timebase was sent
+ // to our downstream, so it cannot change anymore
+ int tb_out_locked;
+
+ AVRational sample_aspect_ratio;
+
+ AVDictionary *sws_opts;
+ AVDictionary *swr_opts;
+
+ // those are only set if no format is specified and the encoder gives us multiple options
+ // They point directly to the relevant lists of the encoder.
+ const int *formats;
+ const AVChannelLayout *ch_layouts;
+ const int *sample_rates;
+ const enum AVColorSpace *color_spaces;
+ const enum AVColorRange *color_ranges;
+
+ AVRational enc_timebase;
+ int64_t trim_start_us;
+ int64_t trim_duration_us;
+ // offset for output timestamps, in AV_TIME_BASE_Q
+ int64_t ts_offset;
+ int64_t next_pts;
+ FPSConvContext fps;
+
+ unsigned flags;
+} OutputFilterPriv;
+
+static OutputFilterPriv *ofp_from_ofilter(OutputFilter *ofilter)
+{
+ return (OutputFilterPriv*)ofilter;
+}
+
+#endif /* FFTOOLS_FFMPEG_FILTER_H */
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread
* [FFmpeg-devel] [PATCH v3 5/7] avfilter/avfilter Add avfilter_link_get_hw_frames_ctx()
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
` (3 preceding siblings ...)
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 4/7] fftools/ffmpeg_filter: Move some declaration to new header file softworkz
@ 2025-03-01 10:02 ` softworkz
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 6/7] fftools/ffmpeg_graphprint: Add options for filtergraph printing softworkz
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 7/7] fftools: Enable filtergraph printing and update docs softworkz
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:02 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
---
doc/APIchanges | 3 +++
libavfilter/avfilter.c | 9 +++++++++
libavfilter/avfilter.h | 12 ++++++++++++
libavfilter/version.h | 2 +-
4 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/doc/APIchanges b/doc/APIchanges
index ac506f4b56..acdd473a67 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
API changes, most recent first:
+2025-02-xx - xxxxxxxxxx - lavfi 10.10.100 - avfilter.h
+ Add avfilter_link_get_hw_frames_ctx().
+
2025-02-xx - xxxxxxxxxx - lavu 59.57.100 - log.h
Add flags AV_LOG_PRINT_TIME and AV_LOG_PRINT_DATETIME.
diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c
index e732556ffa..13abd7e8ad 100644
--- a/libavfilter/avfilter.c
+++ b/libavfilter/avfilter.c
@@ -1006,6 +1006,15 @@ enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx)
return pads[pad_idx].type;
}
+AVBufferRef *avfilter_link_get_hw_frames_ctx(AVFilterLink *link)
+{
+ FilterLink* plink = ff_filter_link(link);
+ if (plink->hw_frames_ctx)
+ return av_buffer_ref(plink->hw_frames_ctx);
+
+ return NULL;
+}
+
static int default_filter_frame(AVFilterLink *link, AVFrame *frame)
{
return ff_filter_frame(link->dst->outputs[0], frame);
diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h
index 4520d5f978..27c50520b3 100644
--- a/libavfilter/avfilter.h
+++ b/libavfilter/avfilter.h
@@ -96,6 +96,18 @@ const char *avfilter_pad_get_name(const AVFilterPad *pads, int pad_idx);
*/
enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx);
+/**
+ * Get the hardware frames context of a filter link.
+ *
+ * @param link an AVFilterLink
+ *
+ * @return a ref-counted copy of the link's hw_frames_ctx if there's a hardware
+ * frames context associated with the link or NULL otherwise.
+ * The returned AVBufferRef needs to be released with av_buffer_unref()
+ * when it's no longer used.
+ */
+AVBufferRef* avfilter_link_get_hw_frames_ctx(AVFilterLink *link);
+
/**
* Lists of formats / etc. supported by an end of a link.
*
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 77f38cb9b4..4a69d6be98 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -31,7 +31,7 @@
#include "version_major.h"
-#define LIBAVFILTER_VERSION_MINOR 9
+#define LIBAVFILTER_VERSION_MINOR 10
#define LIBAVFILTER_VERSION_MICRO 100
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread
* [FFmpeg-devel] [PATCH v3 6/7] fftools/ffmpeg_graphprint: Add options for filtergraph printing
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
` (4 preceding siblings ...)
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 5/7] avfilter/avfilter Add avfilter_link_get_hw_frames_ctx() softworkz
@ 2025-03-01 10:02 ` softworkz
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 7/7] fftools: Enable filtergraph printing and update docs softworkz
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:02 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
The key benefits are:
- Different to other graph printing methods, this is outputting:
- all graphs with runtime state
(including auto-inserted filters)
- each graph with its inputs and outputs
- all filters with their in- and output pads
- all connections between all input- and output pads
- for each connection:
- the runtime-negotiated format and media type
- the hw context
- if video hw context, both: hw pixfmt + sw pixfmt
- Output can either be printed to stdout or written to specified file
- Output is machine-readable
- Use the same output implementation as ffprobe, supporting multiple
formats
Signed-off-by: softworkz <softworkz@hotmail.com>
---
fftools/Makefile | 11 +
fftools/ffmpeg.h | 3 +
fftools/ffmpeg_graphprint.c | 474 ++++++++++++++++++++++++++++++++++++
fftools/ffmpeg_graphprint.h | 79 ++++++
fftools/ffmpeg_opt.c | 12 +
5 files changed, 579 insertions(+)
create mode 100644 fftools/ffmpeg_graphprint.c
create mode 100644 fftools/ffmpeg_graphprint.h
diff --git a/fftools/Makefile b/fftools/Makefile
index 664b73b161..03cdbd4b6e 100644
--- a/fftools/Makefile
+++ b/fftools/Makefile
@@ -19,8 +19,19 @@ OBJS-ffmpeg += \
fftools/ffmpeg_mux_init.o \
fftools/ffmpeg_opt.o \
fftools/ffmpeg_sched.o \
+ fftools/ffmpeg_graphprint.o \
fftools/sync_queue.o \
fftools/thread_queue.o \
+ fftools/textformat/avtextformat.o \
+ fftools/textformat/tf_compact.o \
+ fftools/textformat/tf_default.o \
+ fftools/textformat/tf_flat.o \
+ fftools/textformat/tf_ini.o \
+ fftools/textformat/tf_json.o \
+ fftools/textformat/tf_xml.o \
+ fftools/textformat/tw_avio.o \
+ fftools/textformat/tw_buffer.o \
+ fftools/textformat/tw_stdout.o \
OBJS-ffprobe += \
fftools/textformat/avtextformat.o \
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 86a3e10c6b..9880236162 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -715,6 +715,9 @@ extern float max_error_rate;
extern char *filter_nbthreads;
extern int filter_complex_nbthreads;
extern int vstats_version;
+extern int print_graphs;
+extern char* print_graphs_file;
+extern char* print_graphs_format;
extern int auto_conversion_filters;
extern const AVIOInterruptCB int_cb;
diff --git a/fftools/ffmpeg_graphprint.c b/fftools/ffmpeg_graphprint.c
new file mode 100644
index 0000000000..b374c92743
--- /dev/null
+++ b/fftools/ffmpeg_graphprint.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2018 - softworkz
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * output writers for filtergraph details
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "ffmpeg_graphprint.h"
+#include "ffmpeg_filter.h"
+
+#include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/dict.h"
+#include "libavutil/common.h"
+#include "libavfilter/avfilter.h"
+#include "libavutil/buffer.h"
+#include "libavutil/hwcontext.h"
+#include "textformat/avtextformat.h"
+
+/* Text Format API Shortcuts */
+#define print_int(k, v) avtext_print_integer(w, k, v)
+#define print_q(k, v, s) avtext_print_rational(w, k, v, s)
+#define print_str(k, v) avtext_print_string(w, k, v, 0)
+
+static void print_hwdevicecontext(AVTextFormatContext *w, const AVHWDeviceContext *hw_device_context)
+{
+ avtext_print_section_header(w, NULL, SECTION_ID_HWDEViCECONTEXT);
+
+ print_int("HasHwDeviceContext", 1);
+ print_str("DeviceType", av_hwdevice_get_type_name(hw_device_context->type));
+
+ avtext_print_section_footer(w); // SECTION_ID_HWDEViCECONTEXT
+}
+
+static void print_hwframescontext(AVTextFormatContext *w, const AVHWFramesContext *hw_frames_context)
+{
+ const AVPixFmtDescriptor* pixdescHw;
+ const AVPixFmtDescriptor* pixdescSw;
+
+ avtext_print_section_header(w, NULL, SECTION_ID_HWFRAMESCONTEXT);
+
+ print_int("HasHwFramesContext", 1);
+
+ pixdescHw = av_pix_fmt_desc_get(hw_frames_context->format);
+ if (pixdescHw) {
+ print_str("HwPixelFormat", pixdescHw->name);
+ print_str("HwPixelFormatAlias", pixdescHw->alias);
+ }
+
+ pixdescSw = av_pix_fmt_desc_get(hw_frames_context->sw_format);
+ if (pixdescSw) {
+ print_str("SwPixelFormat", pixdescSw->name);
+ print_str("SwPixelFormatAlias", pixdescSw->alias);
+ }
+
+ print_int("Width", hw_frames_context->width);
+ print_int("Height", hw_frames_context->height);
+
+ print_hwdevicecontext(w, hw_frames_context->device_ctx);
+
+ avtext_print_section_footer(w); // SECTION_ID_HWFRAMESCONTEXT
+}
+
+static void print_link(AVTextFormatContext *w, AVFilterLink *link)
+{
+ char layoutString[64];
+
+ switch (link->type) {
+ case AVMEDIA_TYPE_VIDEO:
+ print_str("Format", av_x_if_null(av_get_pix_fmt_name(link->format), "?"));
+ print_int("Width", link->w);
+ print_int("Height", link->h);
+ print_q("SAR", link->sample_aspect_ratio, ':');
+ print_q("TimeBase", link->time_base, '/');
+ break;
+
+ ////case AVMEDIA_TYPE_SUBTITLE:
+ //// print_str("Format", av_x_if_null(av_get_subtitle_fmt_name(link->format), "?"));
+ //// print_int("Width", link->w);
+ //// print_int("Height", link->h);
+ //// print_q("TimeBase", link->time_base, '/');
+ //// break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ av_channel_layout_describe(&link->ch_layout, layoutString, sizeof(layoutString));
+ print_str("ChannelString", layoutString);
+ print_int("Channels", link->ch_layout.nb_channels);
+ ////print_int("ChannelLayout", link->ch_layout);
+ print_int("SampleRate", link->sample_rate);
+ break;
+ }
+
+ AVBufferRef *hw_frames_ctx = avfilter_link_get_hw_frames_ctx(link);
+
+ if (hw_frames_ctx && hw_frames_ctx->buffer) {
+ print_hwframescontext(w, (AVHWFramesContext *)hw_frames_ctx->data);
+ }
+}
+
+static void print_filter(AVTextFormatContext *w, const AVFilterContext* filter)
+{
+ avtext_print_section_header(w, NULL, SECTION_ID_FILTER);
+
+ print_str("Name", filter->name);
+
+ if (filter->filter) {
+ print_str("Name2", filter->filter->name);
+ print_str("Description", filter->filter->description);
+ }
+
+ if (filter->hw_device_ctx) {
+ AVHWDeviceContext* decCtx = (AVHWDeviceContext*)filter->hw_device_ctx->data;
+ print_hwdevicecontext(w, decCtx);
+ }
+
+ avtext_print_section_header(w, NULL, SECTION_ID_INPUTS);
+
+ for (unsigned i = 0; i < filter->nb_inputs; i++) {
+ AVFilterLink *link = filter->inputs[i];
+ avtext_print_section_header(w, NULL, SECTION_ID_INPUT);
+
+ print_str("SourceName", link->src->name);
+ print_str("SourcePadName", avfilter_pad_get_name(link->srcpad, 0));
+ print_str("DestPadName", avfilter_pad_get_name(link->dstpad, 0));
+
+ print_link(w, link);
+
+ avtext_print_section_footer(w); // SECTION_ID_INPUT
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_INPUTS
+
+ avtext_print_section_header(w, NULL, SECTION_ID_OUTPUTS);
+
+ for (unsigned i = 0; i < filter->nb_outputs; i++) {
+ AVFilterLink *link = filter->outputs[i];
+ avtext_print_section_header(w, NULL, SECTION_ID_OUTPUT);
+
+ print_str("DestName", link->dst->name);
+ print_str("DestPadName", avfilter_pad_get_name(link->dstpad, 0));
+ print_str("SourceName", link->src->name);
+
+ print_link(w, link);
+
+ avtext_print_section_footer(w); // SECTION_ID_OUTPUT
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_OUTPUTS
+
+ avtext_print_section_footer(w); // SECTION_ID_FILTER
+}
+
+static void init_sections(void)
+{
+ for (unsigned i = 0; i < FF_ARRAY_ELEMS(sections); i++)
+ sections[i].show_all_entries = 1;
+}
+
+static void print_filtergraph_single(AVTextFormatContext *w, FilterGraph* fg, AVFilterGraph *graph)
+{
+ char layoutString[64];
+ FilterGraphPriv *fgp = fgp_from_fg(fg);
+
+ print_int("GraphIndex", fg->index);
+ print_str("Description", fgp->graph_desc);
+
+ avtext_print_section_header(w, NULL, SECTION_ID_INPUTS);
+
+ for (int i = 0; i < fg->nb_inputs; i++) {
+ InputFilterPriv* ifilter = ifp_from_ifilter(fg->inputs[i]);
+ enum AVMediaType mediaType = ifilter->type;
+
+ avtext_print_section_header(w, NULL, SECTION_ID_INPUT);
+
+ print_str("Name1", (char*)ifilter->ifilter.name);
+
+ if (ifilter->filter) {
+ print_str("Name2", ifilter->filter->name);
+ print_str("Name3", ifilter->filter->filter->name);
+ print_str("Description", ifilter->filter->filter->description);
+ }
+
+ print_str("MediaType", av_get_media_type_string(mediaType));
+ print_int("MediaTypeId", mediaType);
+
+ switch (ifilter->type) {
+ case AVMEDIA_TYPE_VIDEO:
+ case AVMEDIA_TYPE_SUBTITLE:
+ print_str("Format", av_x_if_null(av_get_pix_fmt_name(ifilter->format), "?"));
+ print_int("Width", ifilter->width);
+ print_int("Height", ifilter->height);
+ print_q("SAR", ifilter->sample_aspect_ratio, ':');
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+
+ av_channel_layout_describe(&ifilter->ch_layout, layoutString, sizeof(layoutString));
+ print_str("ChannelString", layoutString);
+ print_int("SampleRate", ifilter->sample_rate);
+ break;
+ case AVMEDIA_TYPE_ATTACHMENT:
+ case AVMEDIA_TYPE_DATA:
+ break;
+ }
+
+ if (ifilter->hw_frames_ctx)
+ print_hwframescontext(w, (AVHWFramesContext*)ifilter->hw_frames_ctx->data);
+ else if (ifilter->filter && ifilter->filter->hw_device_ctx) {
+ AVHWDeviceContext* devCtx = (AVHWDeviceContext*)ifilter->filter->hw_device_ctx->data;
+ print_hwdevicecontext(w, devCtx);
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_INPUT
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_INPUTS
+
+
+ avtext_print_section_header(w, NULL, SECTION_ID_OUTPUTS);
+
+ for (int i = 0; i < fg->nb_outputs; i++) {
+ OutputFilterPriv *ofilter = ofp_from_ofilter(fg->outputs[i]);
+ enum AVMediaType mediaType = AVMEDIA_TYPE_UNKNOWN;
+
+ avtext_print_section_header(w, NULL, SECTION_ID_OUTPUT);
+ print_str("Name1", ofilter->name);
+
+ if (ofilter->filter) {
+ print_str("Name2", ofilter->filter->name);
+ print_str("Name3", ofilter->filter->filter->name);
+ print_str("Description", ofilter->filter->filter->description);
+
+ if (ofilter->filter->nb_inputs > 0)
+ mediaType = ofilter->filter->inputs[0]->type;
+ }
+
+ print_str("MediaType", av_get_media_type_string(mediaType));
+ print_int("MediaTypeId", mediaType);
+
+ switch (ofilter->ofilter.type) {
+ case AVMEDIA_TYPE_VIDEO:
+ case AVMEDIA_TYPE_SUBTITLE:
+ print_str("Format", av_x_if_null(av_get_pix_fmt_name(ofilter->format), "?"));
+ print_int("Width", ofilter->width);
+ print_int("Height", ofilter->height);
+ break;
+ case AVMEDIA_TYPE_AUDIO:
+
+ av_channel_layout_describe(&ofilter->ch_layout, layoutString, sizeof(layoutString));
+ print_str("ChannelString", layoutString);
+ print_int("SampleRate", ofilter->sample_rate);
+ break;
+ case AVMEDIA_TYPE_ATTACHMENT:
+ case AVMEDIA_TYPE_DATA:
+ break;
+ }
+
+ if (ofilter->filter && ofilter->filter->hw_device_ctx) {
+ AVHWDeviceContext* devCtx = (AVHWDeviceContext*)ofilter->filter->hw_device_ctx->data;
+ print_hwdevicecontext(w, devCtx);
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_OUTPUT
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_OUTPUTS
+
+
+ avtext_print_section_header(w, NULL, SECTION_ID_FILTERS);
+
+ if (graph) {
+ for (unsigned i = 0; i < graph->nb_filters; i++) {
+ AVFilterContext *filter = graph->filters[i];
+ avtext_print_section_header(w, NULL, SECTION_ID_FILTER);
+
+ print_filter(w, filter);
+
+ avtext_print_section_footer(w); // SECTION_ID_FILTER
+ }
+ }
+
+ avtext_print_section_footer(w); // SECTION_ID_FILTERS
+}
+
+int print_filtergraph(FilterGraph *fg, AVFilterGraph *graph)
+{
+ const AVTextFormatter *text_formatter;
+ AVTextFormatContext *tctx;
+ AVTextWriterContext *wctx;
+ char *w_name, *w_args;
+ int ret;
+ FilterGraphPriv *fgp = fgp_from_fg(fg);
+ AVBPrint *target_buf = &fgp->graph_print_buf;
+
+ init_sections();
+
+ if (target_buf->len)
+ av_bprint_finalize(target_buf, NULL);
+
+ av_bprint_init(target_buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ if (!print_graphs_format)
+ print_graphs_format = av_strdup("default");
+ if (!print_graphs_format)
+ return AVERROR(ENOMEM);
+
+ w_name = av_strtok(print_graphs_format, "=", &w_args);
+ if (!w_name) {
+ av_log(NULL, AV_LOG_ERROR, "No name specified for the filter graph output format\n");
+ return AVERROR(EINVAL);
+ }
+
+ text_formatter = avtext_get_formatter_by_name(w_name);
+ if (text_formatter == NULL) {
+ av_log(NULL, AV_LOG_ERROR, "Unknown filter graph output format with name '%s'\n", w_name);
+ return AVERROR(EINVAL);
+ }
+
+ ret = avtextwriter_create_buffer(&wctx, target_buf);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "avtextwriter_create_buffer failed. Error code %d\n", ret);
+ return AVERROR(EINVAL);
+ }
+
+ if ((ret = avtext_context_open(&tctx, text_formatter, wctx, w_args, sections, FF_ARRAY_ELEMS(sections), 0, 0, 0, 0, -1, NULL)) >= 0) {
+ avtext_print_section_header(tctx, NULL, SECTION_ID_ROOT);
+ avtext_print_section_header(tctx, NULL, SECTION_ID_FILTERGRAPHS);
+ avtext_print_section_header(tctx, NULL, SECTION_ID_FILTERGRAPH);
+
+ av_bprint_clear(target_buf);
+
+ print_filtergraph_single(tctx, fg, graph);
+
+ avtext_context_close(&tctx);
+ avtextwriter_context_close(&wctx);
+ } else
+ return ret;
+
+ return 0;
+}
+
+int print_filtergraphs(FilterGraph **graphs, int nb_graphs, OutputFile **ofiles, int nb_ofiles)
+{
+ const AVTextFormatter *text_formatter;
+ AVTextFormatContext *tctx;
+ AVTextWriterContext *wctx;
+ AVBPrint target_buf;
+ char *buf, *w_name, *w_args;
+ int ret;
+
+ init_sections();
+
+ if (!print_graphs_format)
+ print_graphs_format = av_strdup("default");
+ if (!print_graphs_format) {
+ return AVERROR(ENOMEM);
+ }
+
+ w_name = av_strtok(print_graphs_format, "=", &buf);
+ if (!w_name) {
+ av_log(NULL, AV_LOG_ERROR, "No name specified for the filter graph output format\n");
+ return AVERROR(EINVAL);
+ }
+ w_args = buf;
+
+ text_formatter = avtext_get_formatter_by_name(w_name);
+ if (text_formatter == NULL) {
+ av_log(NULL, AV_LOG_ERROR, "Unknown filter graph output format with name '%s'\n", w_name);
+ return AVERROR(EINVAL);
+ }
+
+ av_bprint_init(&target_buf, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ ret = avtextwriter_create_buffer(&wctx, &target_buf);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "avtextwriter_create_buffer failed. Error code %d\n", ret);
+ return AVERROR(EINVAL);
+ }
+
+ if ((ret = avtext_context_open(&tctx, text_formatter, wctx, w_args, sections, FF_ARRAY_ELEMS(sections), 0, 0, 0, 0, -1, NULL)) >= 0) {
+ avtext_print_section_header(tctx, NULL, SECTION_ID_ROOT);
+
+ avtext_print_section_header(tctx, NULL, SECTION_ID_FILTERGRAPHS);
+
+ for (int i = 0; i < nb_graphs; i++) {
+
+ FilterGraphPriv *fgp = fgp_from_fg(graphs[i]);
+ AVBPrint *graph_buf = &fgp->graph_print_buf;
+
+ if (graph_buf->len > 0) {
+ avtext_print_section_header(tctx, NULL, SECTION_ID_FILTERGRAPH);
+
+ av_bprint_append_data(&target_buf, graph_buf->str, graph_buf->len);
+ av_bprint_finalize(graph_buf, NULL);
+
+ avtext_print_section_footer(tctx); // SECTION_ID_FILTERGRAPH
+ }
+ }
+
+ for (int n = 0; n < nb_ofiles; n++) {
+ OutputFile *of = ofiles[n];
+
+ for (int i = 0; i < of->nb_streams; i++) {
+ OutputStream *ost = of->streams[i];
+
+ if (ost->fg_simple) {
+ FilterGraphPriv *fgp = fgp_from_fg(ost->fg_simple);
+ AVBPrint *graph_buf = &fgp->graph_print_buf;
+
+ if (graph_buf->len > 0) {
+ avtext_print_section_header(tctx, NULL, SECTION_ID_FILTERGRAPH);
+
+ av_bprint_append_data(&target_buf, graph_buf->str, graph_buf->len);
+ av_bprint_finalize(graph_buf, NULL);
+
+ avtext_print_section_footer(tctx); // SECTION_ID_FILTERGRAPH
+ }
+ }
+ }
+ }
+
+ avtext_print_section_footer(tctx); // SECTION_ID_FILTERGRAPHS
+ avtext_print_section_footer(tctx); // SECTION_ID_ROOT
+
+ if (print_graphs_file) {
+ AVIOContext *avio = NULL;
+
+ ret = avio_open2(&avio, print_graphs_file, AVIO_FLAG_WRITE, NULL, NULL);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "Failed to open graph output file, \"%s\": %s\n",
+ print_graphs_file, av_err2str(ret));
+ return ret;
+ }
+
+ avio_write(avio, (const unsigned char*)target_buf.str, FFMIN(target_buf.len, target_buf.size - 1));
+ avio_flush(avio);
+
+ if ((ret = avio_closep(&avio)) < 0)
+ av_log(NULL, AV_LOG_ERROR, "Error closing graph output file, loss of information possible: %s\n", av_err2str(ret));
+ }
+
+ if (print_graphs) {
+ printf("%s", target_buf.str);
+ av_log(NULL, AV_LOG_INFO, "%s %c", target_buf.str, '\n');
+ }
+
+ avtext_context_close(&tctx);
+ avtextwriter_context_close(&wctx);
+ }
+
+ return 0;
+}
diff --git a/fftools/ffmpeg_graphprint.h b/fftools/ffmpeg_graphprint.h
new file mode 100644
index 0000000000..5c2635765c
--- /dev/null
+++ b/fftools/ffmpeg_graphprint.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 - softworkz
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FFTOOLS_FFMPEG_GRAPHPRINT_H
+#define FFTOOLS_FFMPEG_GRAPHPRINT_H
+
+#include <stdint.h>
+
+#include "config.h"
+#include "ffmpeg.h"
+#include "libavutil/avutil.h"
+#include "libavutil/bprint.h"
+#include "textformat/avtextformat.h"
+
+typedef enum {
+ SECTION_ID_ROOT,
+ SECTION_ID_PROGRAM_VERSION,
+ SECTION_ID_FILTERGRAPHS,
+ SECTION_ID_FILTERGRAPH,
+ SECTION_ID_INPUTS,
+ SECTION_ID_INPUT,
+ SECTION_ID_OUTPUTS,
+ SECTION_ID_OUTPUT,
+ SECTION_ID_FILTERS,
+ SECTION_ID_FILTER,
+ SECTION_ID_HWDEViCECONTEXT,
+ SECTION_ID_HWFRAMESCONTEXT,
+ SECTION_ID_ERROR,
+ SECTION_ID_LOG,
+ SECTION_ID_LOGS,
+
+} SectionID;
+
+static struct AVTextFormatSection sections[] = {
+ [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "GraphDescription", AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER,
+ { SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_FILTERGRAPHS, SECTION_ID_LOGS, -1} },
+ [SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION, "ProgramVersion", 0, { -1 } },
+
+ [SECTION_ID_FILTERGRAPHS] = { SECTION_ID_FILTERGRAPHS, "Graphs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FILTERGRAPH, -1 } },
+ [SECTION_ID_FILTERGRAPH] = { SECTION_ID_FILTERGRAPH, "Graph", 0, { SECTION_ID_INPUTS, SECTION_ID_OUTPUTS, SECTION_ID_FILTERS, -1 }, },
+
+ [SECTION_ID_INPUTS] = { SECTION_ID_INPUTS, "Inputs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_INPUT, SECTION_ID_ERROR, -1 } },
+ [SECTION_ID_INPUT] = { SECTION_ID_INPUT, "Input", 0, { SECTION_ID_HWFRAMESCONTEXT, SECTION_ID_ERROR, -1 }, },
+
+ [SECTION_ID_OUTPUTS] = { SECTION_ID_OUTPUTS, "Outputs", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_OUTPUT, SECTION_ID_ERROR, -1 } },
+ [SECTION_ID_OUTPUT] = { SECTION_ID_OUTPUT, "Output", 0, { SECTION_ID_HWFRAMESCONTEXT, SECTION_ID_ERROR, -1 }, },
+
+ [SECTION_ID_FILTERS] = { SECTION_ID_FILTERS, "Filters", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_FILTER, SECTION_ID_ERROR, -1 } },
+ [SECTION_ID_FILTER] = { SECTION_ID_FILTER, "Filter", 0, { SECTION_ID_HWDEViCECONTEXT, SECTION_ID_ERROR, -1 }, },
+
+ [SECTION_ID_HWDEViCECONTEXT] = { SECTION_ID_HWDEViCECONTEXT, "HwDeviceContext", 0, { SECTION_ID_ERROR, -1 }, },
+ [SECTION_ID_HWFRAMESCONTEXT] = { SECTION_ID_HWFRAMESCONTEXT, "HwFramesContext", 0, { SECTION_ID_ERROR, -1 }, },
+
+ [SECTION_ID_ERROR] = { SECTION_ID_ERROR, "Error", 0, { -1 } },
+ [SECTION_ID_LOGS] = { SECTION_ID_LOGS, "Log", AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY, { SECTION_ID_LOG, -1 } },
+ [SECTION_ID_LOG] = { SECTION_ID_LOG, "LogEntry", 0, { -1 }, },
+};
+
+int print_filtergraphs(FilterGraph **graphs, int nb_graphs, OutputFile **output_files, int nb_output_files);
+int print_filtergraph(FilterGraph *fg, AVFilterGraph *graph);
+
+#endif /* FFTOOLS_FFMPEG_GRAPHPRINT_H */
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 27a9fc9e42..f63d253f53 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -75,6 +75,9 @@ float max_error_rate = 2.0/3;
char *filter_nbthreads;
int filter_complex_nbthreads = 0;
int vstats_version = 2;
+int print_graphs = 0;
+char* print_graphs_file = NULL;
+char* print_graphs_format = NULL;
int auto_conversion_filters = 1;
int64_t stats_period = 500000;
@@ -1733,6 +1736,15 @@ const OptionDef options[] = {
{ .func_arg = opt_filter_complex_script },
"deprecated, use -/filter_complex instead", "filename" },
#endif
+ { "print_graphs", OPT_TYPE_BOOL, 0,
+ { &print_graphs },
+ "prints filtergraph details to stderr" },
+ { "print_graphs_file", OPT_TYPE_STRING, 0,
+ { &print_graphs_file },
+ "writes graph details to a file", "filename" },
+ { "print_graphs_format", OPT_TYPE_STRING, 0,
+ { &print_graphs_format },
+ "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" },
{ "auto_conversion_filters", OPT_TYPE_BOOL, OPT_EXPERT,
{ &auto_conversion_filters },
"enable automatic conversion filters globally" },
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread
* [FFmpeg-devel] [PATCH v3 7/7] fftools: Enable filtergraph printing and update docs
2025-03-01 10:01 ` [FFmpeg-devel] [PATCH v3 0/7] print_graphs: Complete Filtergraph Printing ffmpegagent
` (5 preceding siblings ...)
2025-03-01 10:02 ` [FFmpeg-devel] [PATCH v3 6/7] fftools/ffmpeg_graphprint: Add options for filtergraph printing softworkz
@ 2025-03-01 10:02 ` softworkz
6 siblings, 0 replies; 27+ messages in thread
From: softworkz @ 2025-03-01 10:02 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Soft Works, softworkz, Andreas Rheinhardt
From: softworkz <softworkz@hotmail.com>
Enables filtergraph printing and adds the options to the docs
Signed-off-by: softworkz <softworkz@hotmail.com>
---
doc/ffmpeg.texi | 10 ++++++++++
fftools/ffmpeg.c | 4 ++++
fftools/ffmpeg_filter.c | 5 +++++
3 files changed, 19 insertions(+)
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index fca220a334..0f1a253183 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -1388,6 +1388,16 @@ It is on by default, to explicitly disable it you need to specify @code{-nostats
@item -stats_period @var{time} (@emph{global})
Set period at which encoding progress/statistics are updated. Default is 0.5 seconds.
+@item -print_graphs (@emph{global})
+Prints filtergraph details to stderr in the format set via -print_graphs_format.
+
+@item -print_graphs_file @var{filename} (@emph{global})
+Writes filtergraph details to the specified file in the format set via -print_graphs_format.
+
+@item -print_graphs_format @var{format} (@emph{global})
+Sets the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)
+The default format is json.
+
@item -progress @var{url} (@emph{global})
Send program-friendly progress information to @var{url}.
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index dc321fb4a2..b4bc76d788 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -81,6 +81,7 @@
#include "ffmpeg.h"
#include "ffmpeg_sched.h"
#include "ffmpeg_utils.h"
+#include "ffmpeg_graphprint.h"
const char program_name[] = "ffmpeg";
const int program_birth_year = 2000;
@@ -308,6 +309,9 @@ const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL };
static void ffmpeg_cleanup(int ret)
{
+ if (print_graphs || print_graphs_file)
+ print_filtergraphs(filtergraphs, nb_filtergraphs, output_files, nb_output_files);
+
if (do_benchmark) {
int64_t maxrss = getmaxrss() / 1024;
av_log(NULL, AV_LOG_INFO, "bench: maxrss=%"PRId64"KiB\n", maxrss);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 6de4e87ade..7198416ae9 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -22,6 +22,7 @@
#include "ffmpeg.h"
#include "ffmpeg_filter.h"
+#include "ffmpeg_graphprint.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
@@ -2970,6 +2971,10 @@ read_frames:
}
finish:
+
+ if (print_graphs || print_graphs_file)
+ print_filtergraph(fg, fgt.graph);
+
// EOF is normal termination
if (ret == AVERROR_EOF)
ret = 0;
--
ffmpeg-codebot
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 27+ messages in thread