Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API
@ 2025-02-27 14:01 ffmpegagent
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 1/8] ffprobe/avtextformat: Rename and move writer structs softworkz
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: ffmpegagent @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

Hello everybody,

this is an effort to pull out the text writing code from ffprobe and shape
it as an independent API. To avoid any other big refacoring in the future,
this patchset is building it up as a public API in avutil, but it's not that
I would be insisting this to happen (or maybe not yet). It could very well
remain private and only shared by ffprobe and ffmpeg (filtergraph printing)
for the time being. Would like to know what people are thinking about -
specifically and in general.

This patchset doesn't aim to improve or change the API itself - that would
be a bit too much at once. The primary functional changes made were those
which were required to gain independence of the specific way of use inside
ffprobe - without causing any change to the output. Locally, I had some
failing FATE tests locally but the CI build with FATE (here on GitHub)
finished without error.

Non-functional changes are naming, made in the spirit of ramping up for
becoming a public API (possibly), and there's also a separation of
functionality: Currently, everything is called "writer" in some way, without
making any distinctions. The main change is that there are now:

 * Text Formatters: AVTextFormatter and AVTextFormatContext These are
   obviously in charge of formatting the output (csv, json, xml, compact,
   etc.)
 * Text Writers: AVTextWriter and AVTextWriterContext Responsible for
   writing the formatted text somewhere. Currently 3 writers exist
   * stdouwriter
   * aviowriter - can output to a file or to an existing avio context
   * bufferwriter - writes to a provided AVBprint buffer

I'll be happy about any hints or comments!

Known Issues

 * Mixes avutil and fftool changes in commits (I kept it preliminarily like
   that as it's easier to follow. Otherwise it would be a huge single commit
   to ffprobe.c)
 * No changelog, ApiChange entries and version bumps
 * Public API isn't documented yet
 * Some variables haven't been renamed yet (like wctx to tctx)
 * Not sure whether every single commit builds successfully, the final one
   does

Open Questions

 * I'm not sure about the best way to expose the formatters and writers to
   consumers Currently, there's still the register_formatter() way in
   ffprobe.c and writers are accessible only via creator functions (like
   avtextwriter_create_avio)
 * There are two header files (avtextformat.h, avtextwriters.h), reflecting
   the separation betwen formatters and writers and a separate code file for
   each formatter and writer, all in a subfolder named textformat. Not sure
   what the preferences are, it was hard to find a comparable precedence.
   Will change when asked to.
 * Do we want flags to be #defines always or can they be enums?
 * Will it need a public/private separation (like recently introduced for
   other entities)?

Thanks

softworkz (8):
  ffprobe/avtextformat: Rename and move writer structs
  ffprobe/avtextformat: Rename and move writer functions and options
  ffprobe/avtextformat: Generalize ffprobe specifics
  ffprobe/avtextformat: Rename flags and enums
  ffprobe/avtextformat: Move flags
  ffprobe/avtextformat: Rename writer to formatter
  ffprobe/avtextformat: Move formatters to avutil
  ffprobe/avtextformat: Split out text writers as independent classes

 fftools/ffprobe.c                   | 2188 ++++-----------------------
 libavutil/Makefile                  |   12 +
 libavutil/avtextformat.h            |  170 +++
 libavutil/avtextwriters.h           |   68 +
 libavutil/textformat/avtextformat.c |  641 ++++++++
 libavutil/textformat/tf_compact.c   |  285 ++++
 libavutil/textformat/tf_default.c   |  150 ++
 libavutil/textformat/tf_flat.c      |  177 +++
 libavutil/textformat/tf_ini.c       |  165 ++
 libavutil/textformat/tf_json.c      |  220 +++
 libavutil/textformat/tf_xml.c       |  224 +++
 libavutil/textformat/tw_avio.c      |  129 ++
 libavutil/textformat/tw_buffer.c    |   91 ++
 libavutil/textformat/tw_stdout.c    |   82 +
 14 files changed, 2737 insertions(+), 1865 deletions(-)
 create mode 100644 libavutil/avtextformat.h
 create mode 100644 libavutil/avtextwriters.h
 create mode 100644 libavutil/textformat/avtextformat.c
 create mode 100644 libavutil/textformat/tf_compact.c
 create mode 100644 libavutil/textformat/tf_default.c
 create mode 100644 libavutil/textformat/tf_flat.c
 create mode 100644 libavutil/textformat/tf_ini.c
 create mode 100644 libavutil/textformat/tf_json.c
 create mode 100644 libavutil/textformat/tf_xml.c
 create mode 100644 libavutil/textformat/tw_avio.c
 create mode 100644 libavutil/textformat/tw_buffer.c
 create mode 100644 libavutil/textformat/tw_stdout.c


base-commit: 99e2af4e7837ca09b97d93a562dc12947179fc48
Published-As: https://github.com/ffstaging/FFmpeg/releases/tag/pr-ffstaging-56%2Fsoftworkz%2Fsubmit_textformat-v1
Fetch-It-Via: git fetch https://github.com/ffstaging/FFmpeg pr-ffstaging-56/softworkz/submit_textformat-v1
Pull-Request: https://github.com/ffstaging/FFmpeg/pull/56
-- 
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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 1/8] ffprobe/avtextformat: Rename and move writer structs
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 2/8] ffprobe/avtextformat: Rename and move writer functions and options softworkz
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c        | 356 +++++++++++++++------------------------
 libavutil/Makefile       |   1 +
 libavutil/avtextformat.h | 120 +++++++++++++
 3 files changed, 257 insertions(+), 220 deletions(-)
 create mode 100644 libavutil/avtextformat.h

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 7341731d2f..76848c3cf4 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -36,6 +36,7 @@
 #include "libavutil/ambient_viewing_environment.h"
 #include "libavutil/avassert.h"
 #include "libavutil/avstring.h"
+#include "libavutil/avtextformat.h"
 #include "libavutil/bprint.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/display.h"
@@ -156,8 +157,6 @@ static int find_stream_info  = 1;
 
 /* section structure definition */
 
-#define SECTION_MAX_NB_CHILDREN 11
-
 typedef enum {
     SECTION_ID_NONE = -1,
     SECTION_ID_CHAPTER,
@@ -228,25 +227,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,7 +250,7 @@ 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[] = {
+static struct AVTextFormatSection sections[] = {
     [SECTION_ID_CHAPTERS] =           { SECTION_ID_CHAPTERS, "chapters", 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" },
@@ -501,77 +481,13 @@ static char *value_string(char *buf, int buf_size, struct unit_value uv)
 
 /* 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;
+    AVTextFormatContext *wctx = p;
     return wctx->writer->name;
 }
 
-#define OFFSET(x) offsetof(WriterContext, x)
+#define OFFSET(x) offsetof(AVTextFormatContext, x)
 
 static const AVOption writer_options[] = {
     { "string_validation", "set string validation mode",
@@ -588,21 +504,21 @@ static const AVOption writer_options[] = {
 
 static void *writer_child_next(void *obj, void *prev)
 {
-    WriterContext *ctx = obj;
+    AVTextFormatContext *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",
+    .class_name = "AVTextFormatter",
     .item_name  = writer_get_name,
     .option     = writer_options,
     .version    = LIBAVUTIL_VERSION_INT,
     .child_next = writer_child_next,
 };
 
-static int writer_close(WriterContext **wctx)
+static int writer_close(AVTextFormatContext **wctx)
 {
     int i;
     int ret = 0;
@@ -634,17 +550,17 @@ static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
         av_bprintf(bp, "%02X", ubuf[i]);
 }
 
-static inline void writer_w8_avio(WriterContext *wctx, int b)
+static inline void writer_w8_avio(AVTextFormatContext *wctx, int b)
 {
     avio_w8(wctx->avio, b);
 }
 
-static inline void writer_put_str_avio(WriterContext *wctx, const char *str)
+static inline void writer_put_str_avio(AVTextFormatContext *wctx, const char *str)
 {
     avio_write(wctx->avio, str, strlen(str));
 }
 
-static inline void writer_printf_avio(WriterContext *wctx, const char *fmt, ...)
+static inline void writer_printf_avio(AVTextFormatContext *wctx, const char *fmt, ...)
 {
     va_list ap;
 
@@ -653,17 +569,17 @@ static inline void writer_printf_avio(WriterContext *wctx, const char *fmt, ...)
     va_end(ap);
 }
 
-static inline void writer_w8_printf(WriterContext *wctx, int b)
+static inline void writer_w8_printf(AVTextFormatContext *wctx, int b)
 {
     printf("%c", b);
 }
 
-static inline void writer_put_str_printf(WriterContext *wctx, const char *str)
+static inline void writer_put_str_printf(AVTextFormatContext *wctx, const char *str)
 {
     printf("%s", str);
 }
 
-static inline void writer_printf_printf(WriterContext *wctx, const char *fmt, ...)
+static inline void writer_printf_printf(AVTextFormatContext *wctx, const char *fmt, ...)
 {
     va_list ap;
 
@@ -672,12 +588,12 @@ static inline void writer_printf_printf(WriterContext *wctx, const char *fmt, ..
     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)
+static int writer_open(AVTextFormatContext **wctx, const AVTextFormatter *writer, const char *args,
+                       const struct AVTextFormatSection *sections, int nb_sections, const char *output)
 {
     int i, ret = 0;
 
-    if (!(*wctx = av_mallocz(sizeof(WriterContext)))) {
+    if (!(*wctx = av_mallocz(sizeof(AVTextFormatContext)))) {
         ret = AVERROR(ENOMEM);
         goto fail;
     }
@@ -774,7 +690,7 @@ fail:
     return ret;
 }
 
-static inline void writer_print_section_header(WriterContext *wctx,
+static inline void writer_print_section_header(AVTextFormatContext *wctx,
                                                const void *data,
                                                int section_id)
 {
@@ -799,7 +715,7 @@ static inline void writer_print_section_header(WriterContext *wctx,
         wctx->writer->print_section_header(wctx, data);
 }
 
-static inline void writer_print_section_footer(WriterContext *wctx)
+static inline void writer_print_section_footer(AVTextFormatContext *wctx)
 {
     int section_id = wctx->section[wctx->level]->id;
     int parent_section_id = wctx->level ?
@@ -816,10 +732,10 @@ static inline void writer_print_section_footer(WriterContext *wctx)
     wctx->level--;
 }
 
-static inline void writer_print_integer(WriterContext *wctx,
+static inline void writer_print_integer(AVTextFormatContext *wctx,
                                         const char *key, int64_t val)
 {
-    const struct section *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *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);
@@ -827,7 +743,7 @@ static inline void writer_print_integer(WriterContext *wctx,
     }
 }
 
-static inline int validate_string(WriterContext *wctx, char **dstp, const char *src)
+static inline int validate_string(AVTextFormatContext *wctx, char **dstp, const char *src)
 {
     const uint8_t *p, *endp;
     AVBPrint dstbuf;
@@ -885,10 +801,10 @@ end:
 #define PRINT_STRING_OPT      1
 #define PRINT_STRING_VALIDATE 2
 
-static inline int writer_print_string(WriterContext *wctx,
+static inline int writer_print_string(AVTextFormatContext *wctx,
                                       const char *key, const char *val, int flags)
 {
-    const struct section *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
     int ret = 0;
 
     if (show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
@@ -923,7 +839,7 @@ static inline int writer_print_string(WriterContext *wctx,
     return ret;
 }
 
-static inline void writer_print_rational(WriterContext *wctx,
+static inline void writer_print_rational(AVTextFormatContext *wctx,
                                          const char *key, AVRational q, char sep)
 {
     AVBPrint buf;
@@ -932,7 +848,7 @@ static inline void writer_print_rational(WriterContext *wctx,
     writer_print_string(wctx, key, buf.str, 0);
 }
 
-static void writer_print_time(WriterContext *wctx, const char *key,
+static void writer_print_time(AVTextFormatContext *wctx, const char *key,
                               int64_t ts, const AVRational *time_base, int is_duration)
 {
     char buf[128];
@@ -949,7 +865,7 @@ static void writer_print_time(WriterContext *wctx, const char *key,
     }
 }
 
-static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration)
+static void writer_print_ts(AVTextFormatContext *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);
@@ -958,7 +874,7 @@ static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, in
     }
 }
 
-static void writer_print_data(WriterContext *wctx, const char *name,
+static void writer_print_data(AVTextFormatContext *wctx, const char *name,
                               const uint8_t *data, int size)
 {
     AVBPrint bp;
@@ -986,7 +902,7 @@ static void writer_print_data(WriterContext *wctx, const char *name,
     av_bprint_finalize(&bp, NULL);
 }
 
-static void writer_print_data_hash(WriterContext *wctx, const char *name,
+static void writer_print_data_hash(AVTextFormatContext *wctx, const char *name,
                                    const uint8_t *data, int size)
 {
     char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
@@ -1001,7 +917,7 @@ static void writer_print_data_hash(WriterContext *wctx, const char *name,
     writer_print_string(wctx, name, buf, 0);
 }
 
-static void writer_print_integers(WriterContext *wctx, const char *name,
+static void writer_print_integers(AVTextFormatContext *wctx, const char *name,
                                   uint8_t *data, int size, const char *format,
                                   int columns, int bytes, int offset_add)
 {
@@ -1033,9 +949,9 @@ static void writer_print_integers(WriterContext *wctx, const char *name,
 
 #define MAX_REGISTERED_WRITERS_NB 64
 
-static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
+static const AVTextFormatter *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
 
-static int writer_register(const Writer *writer)
+static int writer_register(const AVTextFormatter *writer)
 {
     static int next_registered_writer_idx = 0;
 
@@ -1046,7 +962,7 @@ static int writer_register(const Writer *writer)
     return 0;
 }
 
-static const Writer *writer_get_by_name(const char *name)
+static const AVTextFormatter *writer_get_by_name(const char *name)
 {
     int i;
 
@@ -1103,12 +1019,12 @@ static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
     return dst;
 }
 
-static void default_print_section_header(WriterContext *wctx, const void *data)
+static void default_print_section_header(AVTextFormatContext *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 ?
+    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]);
@@ -1128,10 +1044,10 @@ static void default_print_section_header(WriterContext *wctx, const void *data)
         writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name));
 }
 
-static void default_print_section_footer(WriterContext *wctx)
+static void default_print_section_footer(AVTextFormatContext *wctx)
 {
     DefaultContext *def = wctx->priv;
-    const struct section *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
     char buf[32];
 
     if (def->noprint_wrappers || def->nested_section[wctx->level])
@@ -1141,7 +1057,7 @@ static void default_print_section_footer(WriterContext *wctx)
         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)
+static void default_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
 {
     DefaultContext *def = wctx->priv;
 
@@ -1150,7 +1066,7 @@ static void default_print_str(WriterContext *wctx, const char *key, const char *
     writer_printf(wctx, "%s\n", value);
 }
 
-static void default_print_int(WriterContext *wctx, const char *key, int64_t value)
+static void default_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
 {
     DefaultContext *def = wctx->priv;
 
@@ -1159,7 +1075,7 @@ static void default_print_int(WriterContext *wctx, const char *key, int64_t valu
     writer_printf(wctx, "%"PRId64"\n", value);
 }
 
-static const Writer default_writer = {
+static const AVTextFormatter default_writer = {
     .name                  = "default",
     .priv_size             = sizeof(DefaultContext),
     .print_section_header  = default_print_section_header,
@@ -1251,7 +1167,7 @@ static const AVOption compact_options[]= {
 
 DEFINE_WRITER_CLASS(compact);
 
-static av_cold int compact_init(WriterContext *wctx)
+static av_cold int compact_init(AVTextFormatContext *wctx)
 {
     CompactContext *compact = wctx->priv;
 
@@ -1273,11 +1189,11 @@ static av_cold int compact_init(WriterContext *wctx)
     return 0;
 }
 
-static void compact_print_section_header(WriterContext *wctx, const void *data)
+static void compact_print_section_header(AVTextFormatContext *wctx, const void *data)
 {
     CompactContext *compact = wctx->priv;
-    const struct section *section = wctx->section[wctx->level];
-    const struct section *parent_section = wctx->level ?
+    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;
@@ -1325,7 +1241,7 @@ static void compact_print_section_header(WriterContext *wctx, const void *data)
     }
 }
 
-static void compact_print_section_footer(WriterContext *wctx)
+static void compact_print_section_footer(AVTextFormatContext *wctx)
 {
     CompactContext *compact = wctx->priv;
 
@@ -1335,7 +1251,7 @@ static void compact_print_section_footer(WriterContext *wctx)
         writer_w8(wctx, '\n');
 }
 
-static void compact_print_str(WriterContext *wctx, const char *key, const char *value)
+static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
 {
     CompactContext *compact = wctx->priv;
     AVBPrint buf;
@@ -1348,7 +1264,7 @@ static void compact_print_str(WriterContext *wctx, const char *key, const char *
     av_bprint_finalize(&buf, NULL);
 }
 
-static void compact_print_int(WriterContext *wctx, const char *key, int64_t value)
+static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
 {
     CompactContext *compact = wctx->priv;
 
@@ -1358,7 +1274,7 @@ static void compact_print_int(WriterContext *wctx, const char *key, int64_t valu
     writer_printf(wctx, "%"PRId64, value);
 }
 
-static const Writer compact_writer = {
+static const AVTextFormatter compact_formatter = {
     .name                 = "compact",
     .priv_size            = sizeof(CompactContext),
     .init                 = compact_init,
@@ -1389,7 +1305,7 @@ static const AVOption csv_options[] = {
 
 DEFINE_WRITER_CLASS(csv);
 
-static const Writer csv_writer = {
+static const AVTextFormatter csv_formatter = {
     .name                 = "csv",
     .priv_size            = sizeof(CompactContext),
     .init                 = compact_init,
@@ -1423,7 +1339,7 @@ static const AVOption flat_options[]= {
 
 DEFINE_WRITER_CLASS(flat);
 
-static av_cold int flat_init(WriterContext *wctx)
+static av_cold int flat_init(AVTextFormatContext *wctx)
 {
     FlatContext *flat = wctx->priv;
 
@@ -1470,12 +1386,12 @@ static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
     return dst->str;
 }
 
-static void flat_print_section_header(WriterContext *wctx, const void *data)
+static void flat_print_section_header(AVTextFormatContext *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 ?
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
 
     /* build section header */
@@ -1496,12 +1412,12 @@ static void flat_print_section_header(WriterContext *wctx, const void *data)
     }
 }
 
-static void flat_print_int(WriterContext *wctx, const char *key, int64_t value)
+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(WriterContext *wctx, const char *key, const char *value)
+static void flat_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
 {
     FlatContext *flat = wctx->priv;
     AVBPrint buf;
@@ -1514,7 +1430,7 @@ static void flat_print_str(WriterContext *wctx, const char *key, const char *val
     av_bprint_finalize(&buf, NULL);
 }
 
-static const Writer flat_writer = {
+static const AVTextFormatter flat_formatter = {
     .name                  = "flat",
     .priv_size             = sizeof(FlatContext),
     .init                  = flat_init,
@@ -1570,12 +1486,12 @@ static char *ini_escape_str(AVBPrint *dst, const char *src)
     return dst->str;
 }
 
-static void ini_print_section_header(WriterContext *wctx, const void *data)
+static void ini_print_section_header(AVTextFormatContext *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 ?
+    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);
@@ -1603,7 +1519,7 @@ static void ini_print_section_header(WriterContext *wctx, const void *data)
         writer_printf(wctx, "[%s]\n", buf->str);
 }
 
-static void ini_print_str(WriterContext *wctx, const char *key, const char *value)
+static void ini_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
 {
     AVBPrint buf;
 
@@ -1614,12 +1530,12 @@ static void ini_print_str(WriterContext *wctx, const char *key, const char *valu
     av_bprint_finalize(&buf, NULL);
 }
 
-static void ini_print_int(WriterContext *wctx, const char *key, int64_t value)
+static void ini_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
 {
     writer_printf(wctx, "%s=%"PRId64"\n", key, value);
 }
 
-static const Writer ini_writer = {
+static const AVTextFormatter ini_formatter = {
     .name                  = "ini",
     .priv_size             = sizeof(INIContext),
     .print_section_header  = ini_print_section_header,
@@ -1649,7 +1565,7 @@ static const AVOption json_options[]= {
 
 DEFINE_WRITER_CLASS(json);
 
-static av_cold int json_init(WriterContext *wctx)
+static av_cold int json_init(AVTextFormatContext *wctx)
 {
     JSONContext *json = wctx->priv;
 
@@ -1681,12 +1597,12 @@ static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx
 
 #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
 
-static void json_print_section_header(WriterContext *wctx, const void *data)
+static void json_print_section_header(AVTextFormatContext *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 ?
+    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])
@@ -1720,10 +1636,10 @@ static void json_print_section_header(WriterContext *wctx, const void *data)
     }
 }
 
-static void json_print_section_footer(WriterContext *wctx)
+static void json_print_section_footer(AVTextFormatContext *wctx)
 {
     JSONContext *json = wctx->priv;
-    const struct section *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
 
     if (wctx->level == 0) {
         json->indent_level--;
@@ -1742,7 +1658,7 @@ static void json_print_section_footer(WriterContext *wctx)
     }
 }
 
-static inline void json_print_item_str(WriterContext *wctx,
+static inline void json_print_item_str(AVTextFormatContext *wctx,
                                        const char *key, const char *value)
 {
     AVBPrint buf;
@@ -1754,10 +1670,10 @@ static inline void json_print_item_str(WriterContext *wctx,
     av_bprint_finalize(&buf, NULL);
 }
 
-static void json_print_str(WriterContext *wctx, const char *key, const char *value)
+static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
 {
     JSONContext *json = wctx->priv;
-    const struct section *parent_section = wctx->level ?
+    const struct AVTextFormatSection *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))
@@ -1767,10 +1683,10 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val
     json_print_item_str(wctx, key, value);
 }
 
-static void json_print_int(WriterContext *wctx, const char *key, int64_t value)
+static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
 {
     JSONContext *json = wctx->priv;
-    const struct section *parent_section = wctx->level ?
+    const struct AVTextFormatSection *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
     AVBPrint buf;
 
@@ -1784,7 +1700,7 @@ static void json_print_int(WriterContext *wctx, const char *key, int64_t value)
     av_bprint_finalize(&buf, NULL);
 }
 
-static const Writer json_writer = {
+static const AVTextFormatter json_formatter = {
     .name                 = "json",
     .priv_size            = sizeof(JSONContext),
     .init                 = json_init,
@@ -1819,7 +1735,7 @@ static const AVOption xml_options[] = {
 
 DEFINE_WRITER_CLASS(xml);
 
-static av_cold int xml_init(WriterContext *wctx)
+static av_cold int xml_init(AVTextFormatContext *wctx)
 {
     XMLContext *xml = wctx->priv;
 
@@ -1842,11 +1758,11 @@ static av_cold int xml_init(WriterContext *wctx)
 
 #define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ')
 
-static void xml_print_section_header(WriterContext *wctx, const void *data)
+static void xml_print_section_header(AVTextFormatContext *wctx, const void *data)
 {
     XMLContext *xml = wctx->priv;
-    const struct section *section = wctx->section[wctx->level];
-    const struct section *parent_section = wctx->level ?
+    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) {
@@ -1888,10 +1804,10 @@ static void xml_print_section_header(WriterContext *wctx, const void *data)
     }
 }
 
-static void xml_print_section_footer(WriterContext *wctx)
+static void xml_print_section_footer(AVTextFormatContext *wctx)
 {
     XMLContext *xml = wctx->priv;
-    const struct section *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
 
     if (wctx->level == 0) {
         writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
@@ -1905,12 +1821,12 @@ static void xml_print_section_footer(WriterContext *wctx)
     }
 }
 
-static void xml_print_value(WriterContext *wctx, const char *key,
+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 section *section = wctx->section[wctx->level];
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
 
     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
@@ -1947,16 +1863,16 @@ static void xml_print_value(WriterContext *wctx, const char *key,
     av_bprint_finalize(&buf, NULL);
 }
 
-static inline void xml_print_str(WriterContext *wctx, const char *key, const char *value) {
+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(WriterContext *wctx, const char *key, int64_t value)
+static void xml_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
 {
     xml_print_value(wctx, key, NULL, value, 1);
 }
 
-static Writer xml_writer = {
+static AVTextFormatter xml_formatter = {
     .name                 = "xml",
     .priv_size            = sizeof(XMLContext),
     .init                 = xml_init,
@@ -2031,7 +1947,7 @@ static void writer_register_all(void)
     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;
@@ -2049,7 +1965,7 @@ static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id
     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;
@@ -2211,7 +2127,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;
@@ -2310,7 +2226,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;
@@ -2380,7 +2296,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)
@@ -2391,7 +2307,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;
@@ -2516,7 +2432,7 @@ 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)
@@ -2638,7 +2554,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)) {
@@ -2651,7 +2567,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) {
@@ -2661,7 +2577,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) {
@@ -2671,7 +2587,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) {
@@ -2681,7 +2597,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) {
@@ -2691,7 +2607,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) {
@@ -2717,7 +2633,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);
@@ -2752,7 +2668,7 @@ static int show_log(WriterContext *w, int section_ids, int section_id, int log_l
     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)
 {
     char val_str[128];
     AVStream *st = ifile->streams[pkt->stream_index].st;
@@ -2810,7 +2726,7 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p
     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;
@@ -2833,7 +2749,7 @@ static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream,
     fflush(stdout);
 }
 
-static void print_frame_side_data(WriterContext *w,
+static void print_frame_side_data(AVTextFormatContext *w,
                                   const AVFrame *frame,
                                   const AVStream *stream)
 {
@@ -2919,7 +2835,7 @@ static void print_frame_side_data(WriterContext *w,
     writer_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;
@@ -3007,7 +2923,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream,
     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)
@@ -3104,7 +3020,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;
@@ -3229,7 +3145,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;
@@ -3249,7 +3165,7 @@ 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);
     for (int i = 0; i < sizeof(disposition) * CHAR_BIT; i++) {
@@ -3264,7 +3180,7 @@ static void print_dispositions(WriterContext *w, uint32_t disposition, SectionID
 #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;
@@ -3487,7 +3403,7 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
     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;
@@ -3504,7 +3420,7 @@ static int show_streams(WriterContext *w, InputFile *ifile)
     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;
@@ -3535,7 +3451,7 @@ end:
     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;
@@ -3553,7 +3469,7 @@ static int show_programs(WriterContext *w, InputFile *ifile)
     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);
@@ -3576,7 +3492,7 @@ static void print_tile_grid_params(WriterContext *w, const AVStreamGroup *stg,
     writer_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;
@@ -3631,7 +3547,7 @@ static void print_iamf_param_definition(WriterContext *w, const char *name,
     writer_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);
@@ -3662,7 +3578,7 @@ static void print_iamf_audio_element_params(WriterContext *w, const AVStreamGrou
     writer_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);
     print_int("nb_elements",    submix->nb_elements);
@@ -3709,7 +3625,7 @@ static void print_iamf_submix_params(WriterContext *w, const AVIAMFSubmix *submi
     writer_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);
@@ -3728,7 +3644,7 @@ static void print_iamf_mix_presentation_params(WriterContext *w, const AVStreamG
     writer_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);
     if (stg->type == AV_STREAM_GROUP_PARAMS_TILE_GRID)
@@ -3740,7 +3656,7 @@ static void print_stream_group_params(WriterContext *w, AVStreamGroup *stg)
     writer_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;
@@ -3784,7 +3700,7 @@ end:
     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;
@@ -3801,7 +3717,7 @@ static int show_stream_groups(WriterContext *w, InputFile *ifile)
     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;
@@ -3826,7 +3742,7 @@ static int show_chapters(WriterContext *w, InputFile *ifile)
     return ret;
 }
 
-static int show_format(WriterContext *w, InputFile *ifile)
+static int show_format(AVTextFormatContext *w, InputFile *ifile)
 {
     AVFormatContext *fmt_ctx = ifile->fmt_ctx;
     char val_str[128];
@@ -3858,7 +3774,7 @@ static int show_format(WriterContext *w, InputFile *ifile)
     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);
     print_int("code", err);
@@ -4005,7 +3921,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(AVTextFormatContext *wctx, const char *filename,
                       const char *print_filename)
 {
     InputFile ifile = { 0 };
@@ -4103,7 +4019,7 @@ 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);
@@ -4134,7 +4050,7 @@ static void ffprobe_show_program_version(WriterContext *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);
     SHOW_LIB_VERSION(avutil,     AVUTIL);
@@ -4153,7 +4069,7 @@ 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;
@@ -4229,11 +4145,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 = &sections[section_id];
+    struct AVTextFormatSection *section = &sections[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(&section->entries_to_show, entries, 0);
@@ -4246,7 +4162,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 = &sections[i];
+        const struct AVTextFormatSection *section = &sections[i];
         if (!strcmp(section_name, section->name) ||
             (section->unique_name && !strcmp(section_name, section->unique_name))) {
             av_log(NULL, AV_LOG_DEBUG,
@@ -4518,8 +4434,8 @@ 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 = &sections[id];
+    const int *pid;
+    const struct AVTextFormatSection *section = &sections[id];
     printf("%c%c%c%c",
            section->flags & SECTION_FLAG_IS_WRAPPER           ? 'W' : '.',
            section->flags & SECTION_FLAG_IS_ARRAY             ? 'A' : '.',
@@ -4627,10 +4543,10 @@ static const OptionDef real_options[] = {
 
 static inline int check_section_show_entries(int section_id)
 {
-    struct section *section = &sections[section_id];
+    struct AVTextFormatSection *section = &sections[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,8 +4559,8 @@ static inline int check_section_show_entries(int section_id)
 
 int main(int argc, char **argv)
 {
-    const Writer *w;
-    WriterContext *wctx;
+    const AVTextFormatter *w;
+    AVTextFormatContext *wctx;
     char *buf;
     char *w_name = NULL, *w_args = NULL;
     int ret, input_ret, i;
@@ -4749,7 +4665,7 @@ int main(int argc, char **argv)
 
     if ((ret = writer_open(&wctx, w, w_args,
                            sections, FF_ARRAY_ELEMS(sections), output_filename)) >= 0) {
-        if (w == &xml_writer)
+        if (w == &xml_formatter)
             wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
 
         writer_print_section_header(wctx, NULL, SECTION_ID_ROOT);
diff --git a/libavutil/Makefile b/libavutil/Makefile
index b2b3b9c156..ad5aab7d40 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -9,6 +9,7 @@ HEADERS = adler32.h                                                     \
           audio_fifo.h                                                  \
           avassert.h                                                    \
           avstring.h                                                    \
+          avtextformat.h                                                \
           avutil.h                                                      \
           base64.h                                                      \
           blowfish.h                                                    \
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
new file mode 100644
index 0000000000..1740a73249
--- /dev/null
+++ b/libavutil/avtextformat.h
@@ -0,0 +1,120 @@
+/*
+ * 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 AVUTIL_AVTEXTFORMAT_H
+#define AVUTIL_AVTEXTFORMAT_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "attributes.h"
+#include "dict.h"
+#include <libavformat/avio.h>
+#include "bprint.h"
+#include "rational.h"
+
+#define SECTION_MAX_NB_CHILDREN 11
+
+
+struct AVTextFormatSection {
+    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 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;
+};
+
+typedef struct AVTextFormatContext AVTextFormatContext;
+
+#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 AVTextFormatter {
+    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)  (AVTextFormatContext *wctx);
+    void (*uninit)(AVTextFormatContext *wctx);
+
+    void (*print_section_header)(AVTextFormatContext *wctx, const void *data);
+    void (*print_section_footer)(AVTextFormatContext *wctx);
+    void (*print_integer)       (AVTextFormatContext *wctx, const char *, int64_t);
+    void (*print_rational)      (AVTextFormatContext *wctx, AVRational *q, char *sep);
+    void (*print_string)        (AVTextFormatContext *wctx, const char *, const char *);
+    int flags;                  ///< a combination or WRITER_FLAG_*
+} AVTextFormatter;
+
+#define SECTION_MAX_NB_LEVELS 12
+
+struct AVTextFormatContext {
+    const AVClass *class;           ///< class of the writer
+    const AVTextFormatter *writer;           ///< the AVTextFormatter of which this is an instance
+    AVIOContext *avio;              ///< the I/O context used to write
+
+    void (* writer_w8)(AVTextFormatContext *wctx, int b);
+    void (* writer_put_str)(AVTextFormatContext *wctx, const char *str);
+    void (* writer_printf)(AVTextFormatContext *wctx, const char *fmt, ...);
+
+    char *name;                     ///< name of this writer 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];
+
+    /** 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 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;
+};
+
+
+
+#endif /* AVUTIL_AVTEXTFORMAT_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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 2/8] ffprobe/avtextformat: Rename and move writer functions and options
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 1/8] ffprobe/avtextformat: Rename and move writer structs softworkz
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 3/8] ffprobe/avtextformat: Generalize ffprobe specifics softworkz
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c                   | 910 ++++++----------------------
 libavutil/Makefile                  |   1 +
 libavutil/avtextformat.h            |  50 ++
 libavutil/textformat/avtextformat.c | 620 +++++++++++++++++++
 4 files changed, 848 insertions(+), 733 deletions(-)
 create mode 100644 libavutil/textformat/avtextformat.c

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 76848c3cf4..837e21c0e2 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -41,7 +41,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"
@@ -131,6 +130,7 @@ static int show_value_unit              = 0;
 static int use_value_prefix             = 0;
 static int use_byte_value_binary_prefix = 0;
 static int use_value_sexagesimal_format = 0;
+
 static int show_private_data            = 1;
 
 #define SHOW_OPTIONAL_FIELDS_AUTO       -1
@@ -158,7 +158,6 @@ static int find_stream_info  = 1;
 /* section structure definition */
 
 typedef enum {
-    SECTION_ID_NONE = -1,
     SECTION_ID_CHAPTER,
     SECTION_ID_CHAPTER_TAGS,
     SECTION_ID_CHAPTERS,
@@ -330,22 +329,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" ;
@@ -421,527 +404,9 @@ 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 */
 
-static const char *writer_get_name(void *p)
-{
-    AVTextFormatContext *wctx = p;
-    return wctx->writer->name;
-}
-
-#define OFFSET(x) offsetof(AVTextFormatContext, 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)
-{
-    AVTextFormatContext *ctx = obj;
-    if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
-        return ctx->priv;
-    return NULL;
-}
-
-static const AVClass writer_class = {
-    .class_name = "AVTextFormatter",
-    .item_name  = writer_get_name,
-    .option     = writer_options,
-    .version    = LIBAVUTIL_VERSION_INT,
-    .child_next = writer_child_next,
-};
-
-static int writer_close(AVTextFormatContext **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(AVTextFormatContext *wctx, int b)
-{
-    avio_w8(wctx->avio, b);
-}
-
-static inline void writer_put_str_avio(AVTextFormatContext *wctx, const char *str)
-{
-    avio_write(wctx->avio, str, strlen(str));
-}
-
-static inline void writer_printf_avio(AVTextFormatContext *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(AVTextFormatContext *wctx, int b)
-{
-    printf("%c", b);
-}
-
-static inline void writer_put_str_printf(AVTextFormatContext *wctx, const char *str)
-{
-    printf("%s", str);
-}
-
-static inline void writer_printf_printf(AVTextFormatContext *wctx, const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start(ap, fmt);
-    vprintf(fmt, ap);
-    va_end(ap);
-}
-
-static int writer_open(AVTextFormatContext **wctx, const AVTextFormatter *writer, const char *args,
-                       const struct AVTextFormatSection *sections, int nb_sections, const char *output)
-{
-    int i, ret = 0;
-
-    if (!(*wctx = av_mallocz(sizeof(AVTextFormatContext)))) {
-        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(AVTextFormatContext *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(AVTextFormatContext *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(AVTextFormatContext *wctx,
-                                        const char *key, int64_t val)
-{
-    const struct AVTextFormatSection *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(AVTextFormatContext *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(AVTextFormatContext *wctx,
-                                      const char *key, const char *val, int flags)
-{
-    const struct AVTextFormatSection *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(AVTextFormatContext *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(AVTextFormatContext *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(AVTextFormatContext *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(AVTextFormatContext *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(AVTextFormatContext *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(AVTextFormatContext *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_)
@@ -1075,7 +540,7 @@ static void default_print_int(AVTextFormatContext *wctx, const char *key, int64_
     writer_printf(wctx, "%"PRId64"\n", value);
 }
 
-static const AVTextFormatter default_writer = {
+static const AVTextFormatter default_formatter = {
     .name                  = "default",
     .priv_size             = sizeof(DefaultContext),
     .print_section_header  = default_print_section_header,
@@ -1749,8 +1214,8 @@ static av_cold int xml_init(AVTextFormatContext *wctx)
             return AVERROR(EINVAL);                                     \
         }
         CHECK_COMPLIANCE(show_private_data, "private");
-        CHECK_COMPLIANCE(show_value_unit,   "unit");
-        CHECK_COMPLIANCE(use_value_prefix,  "prefix");
+        CHECK_COMPLIANCE(wctx->show_value_unit,   "unit");
+        CHECK_COMPLIANCE(wctx->use_value_prefix,  "prefix");
     }
 
     return 0;
@@ -1892,19 +1357,19 @@ static void writer_register_all(void)
         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);
+    writer_register(&default_formatter);
+    writer_register(&compact_formatter);
+    writer_register(&csv_formatter);
+    writer_register(&flat_formatter);
+    writer_register(&ini_formatter);
+    writer_register(&json_formatter);
+    writer_register(&xml_formatter);
 }
 
 #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 {    \
@@ -1916,28 +1381,23 @@ static void writer_register_all(void)
             av_bprintf(&pbuf, f, __VA_ARGS__);  \
         }                                       \
     }                                           \
-    writer_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); \
+    avtext_print_string(w, k, pbuf.str, 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, PRINT_STRING_OPT)
+#define print_str_validate(k, v) avtext_print_string(w, k, v, 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 print_section_header(s) avtext_print_section_header(w, NULL, s)
+#define print_section_header_data(s, d) avtext_print_section_header(w, d, s)
+#define print_section_footer(s) avtext_print_section_footer(w, s)
 
 #define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n)                        \
 {                                                                       \
@@ -1954,13 +1414,13 @@ static inline int show_tags(AVTextFormatContext *w, AVDictionary *tags, int sect
 
     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;
 }
@@ -2020,15 +1480,15 @@ static void print_dovi_metadata(AVTextFormatContext *w, const AVDOVIMetadata *do
         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;
 
@@ -2046,7 +1506,7 @@ static void print_dovi_metadata(AVTextFormatContext *w, const AVDOVIMetadata *do
                 }
                 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:
@@ -2070,11 +1530,11 @@ static void print_dovi_metadata(AVTextFormatContext *w, const AVDOVIMetadata *do
                 }
 
                 // 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];
@@ -2090,11 +1550,11 @@ static void print_dovi_metadata(AVTextFormatContext *w, const AVDOVIMetadata *do
             }
 
             // 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);
@@ -2353,10 +1813,10 @@ static void print_film_grain_params(AVTextFormatContext *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]);
@@ -2364,14 +1824,14 @@ static void print_film_grain_params(AVTextFormatContext *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]);
@@ -2382,11 +1842,11 @@ static void print_film_grain_params(AVTextFormatContext *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: {
@@ -2395,36 +1855,36 @@ static void print_film_grain_params(AVTextFormatContext *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;
     }
     }
@@ -2439,13 +1899,13 @@ static void print_pkt_side_data(AVTextFormatContext *w,
 {
         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);
             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;
@@ -2542,8 +2002,8 @@ static void print_pkt_side_data(AVTextFormatContext *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));
@@ -2641,11 +2101,11 @@ static int show_log(AVTextFormatContext *w, int section_ids, int section_id, int
         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);
@@ -2657,27 +2117,26 @@ static int show_log(AVTextFormatContext *w, int section_ids, int section_id, int
                 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(AVTextFormatContext *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;
 
     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);
@@ -2696,8 +2155,8 @@ static void show_packet(AVTextFormatContext *w, InputFile *ifile, AVPacket *pkt,
               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;
@@ -2711,16 +2170,16 @@ static void show_packet(AVTextFormatContext *w, InputFile *ifile, AVPacket *pkt,
             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);
@@ -2733,7 +2192,7 @@ static void show_subtitle(AVTextFormatContext *w, AVSubtitle *sub, AVStream *str
 
     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);
@@ -2743,7 +2202,7 @@ static void show_subtitle(AVTextFormatContext *w, AVSubtitle *sub, AVStream *str
     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);
@@ -2753,20 +2212,20 @@ 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) {
             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);
@@ -2777,15 +2236,15 @@ static void print_frame_side_data(AVTextFormatContext *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;
 
@@ -2830,9 +2289,9 @@ static void print_frame_side_data(AVTextFormatContext *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(AVTextFormatContext *w, AVFrame *frame, AVStream *stream,
@@ -2845,7 +2304,7 @@ static void show_frame(AVTextFormatContext *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);
@@ -2917,7 +2376,7 @@ static void show_frame(AVTextFormatContext *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);
@@ -3167,14 +2626,14 @@ static int read_packets(AVTextFormatContext *w, InputFile *ifile)
 
 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
@@ -3212,7 +2671,7 @@ static int show_stream(AVTextFormatContext *w, AVFormatContext *fmt_ctx, int str
 
     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);
 
@@ -3366,12 +2825,12 @@ static int show_stream(AVTextFormatContext *w, AVFormatContext *fmt_ctx, int str
     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);
     }
 
@@ -3387,16 +2846,16 @@ static int show_stream(AVTextFormatContext *w, AVFormatContext *fmt_ctx, int str
     }
 
     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);
 
@@ -3408,14 +2867,14 @@ 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;
 }
@@ -3425,7 +2884,7 @@ static int show_program(AVTextFormatContext *w, InputFile *ifile, AVProgram *pro
     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);
@@ -3436,7 +2895,7 @@ static int show_program(AVTextFormatContext *w, InputFile *ifile, AVProgram *pro
     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);
@@ -3444,10 +2903,10 @@ static int show_program(AVTextFormatContext *w, InputFile *ifile, AVProgram *pro
                 break;
         }
     }
-    writer_print_section_footer(w);
+    avtext_print_section_footer(w);
 
 end:
-    writer_print_section_footer(w);
+    avtext_print_section_footer(w);
     return ret;
 }
 
@@ -3456,7 +2915,7 @@ 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)
@@ -3465,14 +2924,14 @@ static int show_programs(AVTextFormatContext *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(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);
@@ -3480,16 +2939,16 @@ static void print_tile_grid_params(AVTextFormatContext *w, const AVStreamGroup *
     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(AVTextFormatContext *w, const char *name,
@@ -3500,7 +2959,7 @@ static void print_iamf_param_definition(AVTextFormatContext *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);
@@ -3509,56 +2968,56 @@ static void print_iamf_param_definition(AVTextFormatContext *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(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) {
@@ -3566,7 +3025,7 @@ static void print_iamf_audio_element_params(AVTextFormatContext *w, const AVStre
             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,
@@ -3574,36 +3033,36 @@ static void print_iamf_audio_element_params(AVTextFormatContext *w, const AVStre
     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(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,
@@ -3611,7 +3070,7 @@ static void print_iamf_submix_params(AVTextFormatContext *w, const AVIAMFSubmix
     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, '/');
@@ -3619,41 +3078,41 @@ static void print_iamf_submix_params(AVTextFormatContext *w, const AVIAMFSubmix
         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(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(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(AVTextFormatContext *w, InputFile *ifile, AVStreamGroup *stg)
@@ -3663,7 +3122,7 @@ static int show_stream_group(AVTextFormatContext *w, InputFile *ifile, AVStreamG
     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");
@@ -3684,7 +3143,7 @@ static int show_stream_group(AVTextFormatContext *w, InputFile *ifile, AVStreamG
     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);
@@ -3692,11 +3151,11 @@ static int show_stream_group(AVTextFormatContext *w, InputFile *ifile, AVStreamG
                 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;
 }
 
@@ -3705,7 +3164,7 @@ 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];
 
@@ -3713,7 +3172,7 @@ static int show_stream_groups(AVTextFormatContext *w, InputFile *ifile)
         if (ret < 0)
             break;
     }
-    writer_print_section_footer(w);
+    avtext_print_section_footer(w);
     return ret;
 }
 
@@ -3722,11 +3181,11 @@ 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);
@@ -3735,9 +3194,9 @@ static int show_chapters(AVTextFormatContext *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;
 }
@@ -3745,11 +3204,10 @@ static int show_chapters(AVTextFormatContext *w, InputFile *ifile)
 static int show_format(AVTextFormatContext *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;
 
-    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);
@@ -3769,17 +3227,17 @@ static int show_format(AVTextFormatContext *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(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,
@@ -3970,10 +3428,10 @@ static int probe_file(AVTextFormatContext *wctx, 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(wctx, NULL, section_id);
+            avtext_print_section_header(wctx, NULL, section_id);
         ret = read_packets(wctx, &ifile);
         if (do_show_frames || do_show_packets)
-            writer_print_section_footer(wctx);
+            avtext_print_section_footer(wctx);
         CHECK_END;
     }
 
@@ -4024,13 +3482,13 @@ 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);
 }
@@ -4039,20 +3497,20 @@ static void ffprobe_show_program_version(AVTextFormatContext *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(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);
@@ -4061,7 +3519,7 @@ static void ffprobe_show_library_versions(AVTextFormatContext *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)                                \
@@ -4074,9 +3532,9 @@ 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)) {
@@ -4090,7 +3548,7 @@ static void ffprobe_show_pixel_formats(AVTextFormatContext *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");
@@ -4098,21 +3556,21 @@ static void ffprobe_show_pixel_formats(AVTextFormatContext *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)
@@ -4641,21 +4099,6 @@ int main(int argc, char **argv)
     }
     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;
-        }
-    }
-
     w = writer_get_by_name(w_name);
     if (!w) {
         av_log(NULL, AV_LOG_ERROR, "Unknown output format with name '%s'\n", w_name);
@@ -4663,12 +4106,14 @@ int main(int argc, char **argv)
         goto end;
     }
 
-    if ((ret = writer_open(&wctx, w, w_args,
-                           sections, FF_ARRAY_ELEMS(sections), output_filename)) >= 0) {
+    if ((ret = avtext_context_open(&wctx, w, w_args,
+                           sections, FF_ARRAY_ELEMS(sections), output_filename, show_value_unit,
+                            use_value_prefix, use_byte_value_binary_prefix, use_value_sexagesimal_format,
+                            show_optional_fields, show_data_hash)) >= 0) {
         if (w == &xml_formatter)
             wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
 
-        writer_print_section_header(wctx, NULL, SECTION_ID_ROOT);
+        avtext_print_section_header(wctx, NULL, SECTION_ID_ROOT);
 
         if (do_show_program_version)
             ffprobe_show_program_version(wctx);
@@ -4692,8 +4137,8 @@ int main(int argc, char **argv)
 
         input_ret = ret;
 
-        writer_print_section_footer(wctx);
-        ret = writer_close(&wctx);
+        avtext_print_section_footer(wctx);
+        ret = avtext_context_close(&wctx);
         if (ret < 0)
             av_log(NULL, AV_LOG_ERROR, "Writing output failed: %s\n", av_err2str(ret));
 
@@ -4706,7 +4151,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++)
diff --git a/libavutil/Makefile b/libavutil/Makefile
index ad5aab7d40..78c2c0d707 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -115,6 +115,7 @@ OBJS = adler32.o                                                        \
        audio_fifo.o                                                     \
        avstring.o                                                       \
        avsscanf.o                                                       \
+       textformat/avtextformat.o                                        \
        base64.o                                                         \
        blowfish.o                                                       \
        bprint.o                                                         \
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index 1740a73249..e1aa227aff 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -28,6 +28,7 @@
 #include <libavformat/avio.h>
 #include "bprint.h"
 #include "rational.h"
+#include "libavutil/hash.h"
 
 #define SECTION_MAX_NB_CHILDREN 11
 
@@ -110,11 +111,60 @@ struct AVTextFormatContext {
     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 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 SHOW_OPTIONAL_FIELDS_AUTO       -1
+#define SHOW_OPTIONAL_FIELDS_NEVER       0
+#define SHOW_OPTIONAL_FIELDS_ALWAYS      1
+
+#define PRINT_STRING_OPT      1
+#define PRINT_STRING_VALIDATE 2
+
+int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writer, const char *args,
+                        const struct AVTextFormatSection *sections, int nb_sections,
+                        const char *output_filename,
+                        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 **wctx);
+
+
+void avtext_print_section_header(AVTextFormatContext *wctx, const void *data, int section_id);
+
+void avtext_print_section_footer(AVTextFormatContext *wctx);
+
+void avtext_print_integer(AVTextFormatContext *wctx, const char *key, int64_t val);
+
+int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *val, int flags);
+
+void avtext_print_unit_int(AVTextFormatContext *wctx, const char *key, int value, const char *unit);
+
+void avtext_print_rational(AVTextFormatContext *wctx, const char *key, AVRational q, char sep);
+
+void avtext_print_time(AVTextFormatContext *wctx, const char *key, int64_t ts, const AVRational *time_base, int is_duration);
+
+void avtext_print_ts(AVTextFormatContext *wctx, const char *key, int64_t ts, int is_duration);
+
+void avtext_print_data(AVTextFormatContext *wctx, const char *name, const uint8_t *data, int size);
+
+void avtext_print_data_hash(AVTextFormatContext *wctx, const char *name, const uint8_t *data, int size);
 
+void avtext_print_integers(AVTextFormatContext *wctx, const char *name, uint8_t *data, int size, 
+                           const char *format, int columns, int bytes, int offset_add);
 
 #endif /* AVUTIL_AVTEXTFORMAT_H */
diff --git a/libavutil/textformat/avtextformat.c b/libavutil/textformat/avtextformat.c
new file mode 100644
index 0000000000..2b67acae65
--- /dev/null
+++ b/libavutil/textformat/avtextformat.c
@@ -0,0 +1,620 @@
+/*
+ * 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 "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+#define SECTION_ID_NONE -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_writer_name(void *p)
+{
+    AVTextFormatContext *wctx = p;
+    return wctx->writer->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=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"}},
+};
+
+static void *trextcontext_child_next(void *obj, void *prev)
+{
+    AVTextFormatContext *ctx = obj;
+    if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
+        return ctx->priv;
+    return NULL;
+}
+
+static const AVClass textcontext_class = {
+    .class_name = "AVTextContext",
+    .item_name  = avtext_context_get_writer_name,
+    .option     = textcontext_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+    .child_next = trextcontext_child_next,
+};
+
+
+static inline void textoutput_w8_avio(AVTextFormatContext *wctx, int b)
+{
+    avio_w8(wctx->avio, b);
+}
+
+static inline void textoutput_put_str_avio(AVTextFormatContext *wctx, const char *str)
+{
+    avio_write(wctx->avio, str, strlen(str));
+}
+
+static inline void textoutput_printf_avio(AVTextFormatContext *wctx, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    avio_vprintf(wctx->avio, fmt, ap);
+    va_end(ap);
+}
+
+static inline void textoutput_w8_printf(AVTextFormatContext *wctx, int b)
+{
+    printf("%c", b);
+}
+
+static inline void textoutput_put_str_printf(AVTextFormatContext *wctx, const char *str)
+{
+    printf("%s", str);
+}
+
+static inline void textoutput_printf_printf(AVTextFormatContext *wctx, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vprintf(fmt, ap);
+    va_end(ap);
+}
+
+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 **pwctx)
+{
+    AVTextFormatContext *wctx = *pwctx;
+    int i;
+    int ret = 0;
+
+    if (!wctx)
+        return -1;
+
+    av_hash_freep(&wctx->hash);
+
+    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(pwctx);
+    return ret;
+}
+
+
+int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writer, const char *args,
+                        const struct AVTextFormatSection *sections, int nb_sections,
+                        const char *output_filename,
+                        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 *wctx;
+    int i, ret = 0;
+
+    if (!(wctx = av_mallocz(sizeof(AVTextFormatContext)))) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    if (!(wctx->priv = av_mallocz(writer->priv_size))) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    wctx->show_value_unit = show_value_unit;
+    wctx->use_value_prefix = use_value_prefix;
+    wctx->use_byte_value_binary_prefix = use_byte_value_binary_prefix;
+    wctx->use_value_sexagesimal_format = use_value_sexagesimal_format;
+    wctx->show_optional_fields = show_optional_fields;
+
+    wctx->class = &textcontext_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);
+    }
+
+    if (show_data_hash) {
+        if ((ret = av_hash_alloc(&wctx->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 = 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 = textoutput_w8_printf;
+        wctx->writer_put_str = textoutput_put_str_printf;
+        wctx->writer_printf = textoutput_printf_printf;
+    } else {
+        if ((ret = avio_open(&wctx->avio, output_filename, AVIO_FLAG_WRITE)) < 0) {
+            av_log(wctx, AV_LOG_ERROR,
+                   "Failed to open output '%s' with error: %s\n", output_filename, av_err2str(ret));
+            goto fail;
+        }
+        wctx->writer_w8 = textoutput_w8_avio;
+        wctx->writer_put_str = textoutput_put_str_avio;
+        wctx->writer_printf = textoutput_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;
+
+    *pwctx = wctx;
+
+    return 0;
+
+fail:
+    avtext_context_close(&wctx);
+    return ret;
+}
+
+/* Temporary definitions during refactoring */
+#define SECTION_ID_PACKETS_AND_FRAMES     24
+#define SECTION_ID_PACKET                 21
+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 *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);
+}
+
+void avtext_print_section_footer(AVTextFormatContext *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--;
+}
+
+void avtext_print_integer(AVTextFormatContext *wctx,
+                                        const char *key, int64_t val)
+{
+    const struct AVTextFormatSection *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(AVTextFormatContext *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;
+}
+
+struct unit_value {
+    union { double d; int64_t i; } val;
+    const char *unit;
+};
+
+static char *value_string(AVTextFormatContext *wctx, 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 && wctx->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 (wctx->use_value_prefix && vald > 1) {
+            int64_t index;
+
+            if (uv.unit == unit_byte_str && wctx->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 || (wctx->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 || wctx->show_value_unit ? " " : "",
+                 prefix_string, wctx->show_value_unit ? uv.unit : "");
+    }
+
+    return buf;
+}
+
+
+void avtext_print_unit_int(AVTextFormatContext *wctx, 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(wctx, key, value_string(wctx, val_str, sizeof(val_str), uv), 0);
+}
+
+
+int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *val, int flags)
+{
+    const struct AVTextFormatSection *section = wctx->section[wctx->level];
+    int ret = 0;
+
+    if (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
+        (wctx->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;
+}
+
+void avtext_print_rational(AVTextFormatContext *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);
+    avtext_print_string(wctx, key, buf.str, 0);
+}
+
+void avtext_print_time(AVTextFormatContext *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)) {
+        avtext_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(wctx, buf, sizeof(buf), uv);
+        avtext_print_string(wctx, key, buf, 0);
+    }
+}
+
+void avtext_print_ts(AVTextFormatContext *wctx, const char *key, int64_t ts, int is_duration)
+{
+    if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
+        avtext_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
+    } else {
+        avtext_print_integer(wctx, key, ts);
+    }
+}
+
+void avtext_print_data(AVTextFormatContext *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;
+    }
+    avtext_print_string(wctx, name, bp.str, 0);
+    av_bprint_finalize(&bp, NULL);
+}
+
+void avtext_print_data_hash(AVTextFormatContext *wctx, const char *name,
+                                   const uint8_t *data, int size)
+{
+    char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
+
+    if (!wctx->hash)
+        return;
+    av_hash_init(wctx->hash);
+    av_hash_update(wctx->hash, data, size);
+    snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(wctx->hash));
+    p = buf + strlen(buf);
+    av_hash_final_hex(wctx->hash, p, buf + sizeof(buf) - p);
+    avtext_print_string(wctx, name, buf, 0);
+}
+
+void avtext_print_integers(AVTextFormatContext *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;
+    }
+    avtext_print_string(wctx, name, bp.str, 0);
+    av_bprint_finalize(&bp, NULL);
+}
-- 
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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 3/8] ffprobe/avtextformat: Generalize ffprobe specifics
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 1/8] ffprobe/avtextformat: Rename and move writer structs softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 2/8] ffprobe/avtextformat: Rename and move writer functions and options softworkz
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 4/8] ffprobe/avtextformat: Rename flags and enums softworkz
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c                   | 34 +++++++++++++++--------------
 libavutil/avtextformat.h            | 13 +++++------
 libavutil/textformat/avtextformat.c | 30 ++++++++++---------------
 3 files changed, 35 insertions(+), 42 deletions(-)

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 837e21c0e2..5560f6b87e 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -272,7 +272,7 @@ static struct AVTextFormatSection sections[] = {
     [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", 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_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY | 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" },
@@ -547,7 +547,7 @@ static const AVTextFormatter default_formatter = {
     .print_section_footer  = default_print_section_footer,
     .print_integer         = default_print_int,
     .print_string          = default_print_str,
-    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
     .priv_class            = &default_class,
 };
 
@@ -747,7 +747,7 @@ static const AVTextFormatter compact_formatter = {
     .print_section_footer = compact_print_section_footer,
     .print_integer        = compact_print_int,
     .print_string         = compact_print_str,
-    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
     .priv_class           = &compact_class,
 };
 
@@ -778,7 +778,7 @@ static const AVTextFormatter csv_formatter = {
     .print_section_footer = compact_print_section_footer,
     .print_integer        = compact_print_int,
     .print_string         = compact_print_str,
-    .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
     .priv_class           = &csv_class,
 };
 
@@ -870,8 +870,9 @@ static void flat_print_section_header(AVTextFormatContext *wctx, const void *dat
         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];
+            int n = parent_section->flags & 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);
         }
     }
@@ -902,7 +903,7 @@ static const AVTextFormatter flat_formatter = {
     .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,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
     .priv_class            = &flat_class,
 };
 
@@ -974,8 +975,9 @@ static void ini_print_section_header(AVTextFormatContext *wctx, const void *data
         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];
+            int n = parent_section->flags & 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);
         }
     }
@@ -1006,7 +1008,7 @@ static const AVTextFormatter ini_formatter = {
     .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,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
     .priv_class            = &ini_class,
 };
 
@@ -1090,7 +1092,7 @@ static void json_print_section_header(AVTextFormatContext *wctx, const void *dat
             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 (parent_section && parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE) {
                 if (!json->compact)
                     JSON_INDENT();
                 writer_printf(wctx, "\"type\": \"%s\"", section->name);
@@ -1141,7 +1143,7 @@ static void json_print_str(AVTextFormatContext *wctx, const char *key, const cha
     const struct AVTextFormatSection *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))
+    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE))
         writer_put_str(wctx, json->item_sep);
     if (!json->compact)
         JSON_INDENT();
@@ -1155,7 +1157,7 @@ static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t v
         wctx->section[wctx->level-1] : NULL;
     AVBPrint buf;
 
-    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES))
+    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE))
         writer_put_str(wctx, json->item_sep);
     if (!json->compact)
         JSON_INDENT();
@@ -1173,7 +1175,7 @@ static const AVTextFormatter json_formatter = {
     .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,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
     .priv_class           = &json_class,
 };
 
@@ -1345,7 +1347,7 @@ static AVTextFormatter xml_formatter = {
     .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,
+    .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
     .priv_class           = &xml_class,
 };
 
@@ -3421,7 +3423,7 @@ static int probe_file(AVTextFormatContext *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)
+            wctx->writer->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;
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index e1aa227aff..a7f8055783 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -42,6 +42,7 @@ struct AVTextFormatSection {
 #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
+#define 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
@@ -54,8 +55,8 @@ struct AVTextFormatSection {
 
 typedef struct AVTextFormatContext AVTextFormatContext;
 
-#define WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS 1
-#define WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER 2
+#define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS 1
+#define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2
 
 typedef enum {
     WRITER_STRING_VALIDATION_FAIL,
@@ -80,7 +81,8 @@ typedef struct AVTextFormatter {
     int flags;                  ///< a combination or WRITER_FLAG_*
 } AVTextFormatter;
 
-#define SECTION_MAX_NB_LEVELS 12
+#define SECTION_MAX_NB_LEVELS    12
+#define SECTION_MAX_NB_SECTIONS 100
 
 struct AVTextFormatContext {
     const AVClass *class;           ///< class of the writer
@@ -101,16 +103,13 @@ struct AVTextFormatContext {
 
     /** 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 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 show_optional_fields;
     int show_value_unit;
     int use_value_prefix;
diff --git a/libavutil/textformat/avtextformat.c b/libavutil/textformat/avtextformat.c
index 2b67acae65..a2d87e8d42 100644
--- a/libavutil/textformat/avtextformat.c
+++ b/libavutil/textformat/avtextformat.c
@@ -189,6 +189,11 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writ
     wctx->use_byte_value_binary_prefix = use_byte_value_binary_prefix;
     wctx->use_value_sexagesimal_format = use_value_sexagesimal_format;
     wctx->show_optional_fields = show_optional_fields;
+    
+    if (nb_sections > SECTION_MAX_NB_SECTIONS) {
+        av_log(wctx, 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;
+    }
 
     wctx->class = &textcontext_class;
     wctx->writer = writer;
@@ -293,8 +298,6 @@ fail:
 }
 
 /* Temporary definitions during refactoring */
-#define SECTION_ID_PACKETS_AND_FRAMES     24
-#define SECTION_ID_PACKET                 21
 static const char unit_second_str[]         = "s"    ;
 static const char unit_hertz_str[]          = "Hz"   ;
 static const char unit_byte_str[]           = "byte" ;
@@ -305,23 +308,13 @@ void avtext_print_section_header(AVTextFormatContext *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;
+    memset(wctx->nb_item_type[wctx->level], 0, sizeof(wctx->nb_item_type[wctx->level]));
     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);
 }
@@ -332,12 +325,11 @@ void avtext_print_section_footer(AVTextFormatContext *wctx)
     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 (parent_section_id != SECTION_ID_NONE) {
+        wctx->nb_item[wctx->level - 1]++;
+        wctx->nb_item_type[wctx->level - 1][section_id]++;
     }
+
     if (wctx->writer->print_section_footer)
         wctx->writer->print_section_footer(wctx);
     wctx->level--;
@@ -486,7 +478,7 @@ int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *
     if (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
         (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
         && (flags & PRINT_STRING_OPT)
-        && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS)))
+        && !(wctx->writer->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS)))
         return 0;
 
     if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 4/8] ffprobe/avtextformat: Rename flags and enums
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
                   ` (2 preceding siblings ...)
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 3/8] ffprobe/avtextformat: Generalize ffprobe specifics softworkz
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 5/8] ffprobe/avtextformat: Move flags softworkz
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c                   | 156 ++++++++++++++--------------
 libavutil/avtextformat.h            |  24 ++---
 libavutil/textformat/avtextformat.c |  26 ++---
 3 files changed, 103 insertions(+), 103 deletions(-)

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 5560f6b87e..5dc9803d5c 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -250,74 +250,74 @@ static const char *get_stream_group_type(const void *data)
 }
 
 static struct AVTextFormatSection sections[] = {
-    [SECTION_ID_CHAPTERS] =           { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
+    [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_FLAG_NUMBERING_BY_TYPE, { 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 } },
 };
 
@@ -494,7 +494,7 @@ static void default_print_section_header(AVTextFormatContext *wctx, const void *
 
     av_bprint_clear(&wctx->section_pbuf[wctx->level]);
     if (parent_section &&
-        !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
+        !(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,
@@ -505,7 +505,7 @@ static void default_print_section_header(AVTextFormatContext *wctx, const void *
     if (def->noprint_wrappers || def->nested_section[wctx->level])
         return;
 
-    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
+    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));
 }
 
@@ -518,7 +518,7 @@ static void default_print_section_footer(AVTextFormatContext *wctx)
     if (def->noprint_wrappers || def->nested_section[wctx->level])
         return;
 
-    if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
+    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));
 }
 
@@ -665,9 +665,9 @@ static void compact_print_section_header(AVTextFormatContext *wctx, const void *
 
     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))))) {
+        (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 */
@@ -680,7 +680,7 @@ static void compact_print_section_header(AVTextFormatContext *wctx, const void *
         av_bprintf(section_pbuf, "%s%s",
                    wctx->section_pbuf[wctx->level-1].str, element_name);
 
-        if (section->flags & SECTION_FLAG_HAS_TYPE) {
+        if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) {
             // add /TYPE to prefix
             av_bprint_chars(section_pbuf, '/', 1);
 
@@ -697,11 +697,11 @@ static void compact_print_section_header(AVTextFormatContext *wctx, const void *
 
         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)) &&
+        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 & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
+            !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
             writer_printf(wctx, "%s%c", section->name, compact->item_sep);
     }
 }
@@ -712,7 +712,7 @@ static void compact_print_section_footer(AVTextFormatContext *wctx)
 
     if (!compact->nested_section[wctx->level] &&
         compact->terminate_line[wctx->level] &&
-        !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)))
+        !(wctx->section[wctx->level]->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
         writer_w8(wctx, '\n');
 }
 
@@ -866,11 +866,11 @@ static void flat_print_section_header(AVTextFormatContext *wctx, const void *dat
     av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
 
     if (flat->hierarchical ||
-        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
+        !(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 & SECTION_FLAG_IS_ARRAY) {
-            int n = parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE ?
+        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);
@@ -971,18 +971,18 @@ static void ini_print_section_header(AVTextFormatContext *wctx, const void *data
 
     av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
     if (ini->hierarchical ||
-        !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
+        !(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 & SECTION_FLAG_IS_ARRAY) {
-            int n = parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE ?
+        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 & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER)))
+    if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER)))
         writer_printf(wctx, "[%s]\n", buf->str);
 }
 
@@ -1075,7 +1075,7 @@ static void json_print_section_header(AVTextFormatContext *wctx, const void *dat
     if (wctx->level && wctx->nb_item[wctx->level-1])
         writer_put_str(wctx, ",\n");
 
-    if (section->flags & SECTION_FLAG_IS_WRAPPER) {
+    if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
         writer_put_str(wctx, "{\n");
         json->indent_level++;
     } else {
@@ -1084,15 +1084,15 @@ static void json_print_section_header(AVTextFormatContext *wctx, const void *dat
         JSON_INDENT();
 
         json->indent_level++;
-        if (section->flags & SECTION_FLAG_IS_ARRAY) {
+        if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
             writer_printf(wctx, "\"%s\": [\n", buf.str);
-        } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
+        } 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 & SECTION_FLAG_NUMBERING_BY_TYPE) {
+            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);
@@ -1111,7 +1111,7 @@ static void json_print_section_footer(AVTextFormatContext *wctx)
     if (wctx->level == 0) {
         json->indent_level--;
         writer_put_str(wctx, "\n}\n");
-    } else if (section->flags & SECTION_FLAG_IS_ARRAY) {
+    } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
         writer_w8(wctx, '\n');
         json->indent_level--;
         JSON_INDENT();
@@ -1143,7 +1143,7 @@ static void json_print_str(AVTextFormatContext *wctx, const char *key, const cha
     const struct AVTextFormatSection *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
 
-    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE))
+    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();
@@ -1157,7 +1157,7 @@ static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t v
         wctx->section[wctx->level-1] : NULL;
     AVBPrint buf;
 
-    if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & SECTION_FLAG_NUMBERING_BY_TYPE))
+    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();
@@ -1249,15 +1249,15 @@ static void xml_print_section_header(AVTextFormatContext *wctx, const void *data
         writer_put_str(wctx, ">\n");
     }
 
-    if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) &&
+    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 & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_HAS_VARIABLE_FIELDS)) {
+    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 & SECTION_FLAG_HAS_TYPE) {
+        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,
@@ -1297,7 +1297,7 @@ static void xml_print_value(AVTextFormatContext *wctx, const char *key,
 
     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
 
-    if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) {
+    if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS) {
         xml->indent_level++;
         XML_INDENT();
         av_bprint_escape(&buf, key, NULL,
@@ -1389,8 +1389,8 @@ static void writer_register_all(void)
 #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, PRINT_STRING_OPT)
-#define print_str_validate(k, v) avtext_print_string(w, k, v, PRINT_STRING_VALIDATE)
+#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)
@@ -3897,10 +3897,10 @@ static void print_section(SectionID id, int level)
     const int *pid;
     const struct AVTextFormatSection *section = &sections[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);
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index a7f8055783..e40aca1de7 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -37,12 +37,12 @@ struct AVTextFormatSection {
     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.
+#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 SECTION_FLAG_HAS_TYPE        8 ///< the section contains a type to distinguish multiple nested elements
-#define SECTION_FLAG_NUMBERING_BY_TYPE 16 ///< the items in this array section should be numbered individually by type
+#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
@@ -59,10 +59,10 @@ typedef struct AVTextFormatContext AVTextFormatContext;
 #define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2
 
 typedef enum {
-    WRITER_STRING_VALIDATION_FAIL,
-    WRITER_STRING_VALIDATION_REPLACE,
-    WRITER_STRING_VALIDATION_IGNORE,
-    WRITER_STRING_VALIDATION_NB
+    AV_TEXTFORMAT_STRING_VALIDATION_FAIL,
+    AV_TEXTFORMAT_STRING_VALIDATION_REPLACE,
+    AV_TEXTFORMAT_STRING_VALIDATION_IGNORE,
+    AV_TEXTFORMAT_STRING_VALIDATION_NB
 } StringValidation;
 
 typedef struct AVTextFormatter {
@@ -78,7 +78,7 @@ typedef struct AVTextFormatter {
     void (*print_integer)       (AVTextFormatContext *wctx, const char *, int64_t);
     void (*print_rational)      (AVTextFormatContext *wctx, AVRational *q, char *sep);
     void (*print_string)        (AVTextFormatContext *wctx, const char *, const char *);
-    int flags;                  ///< a combination or WRITER_FLAG_*
+    int flags;                  ///< a combination or AV_TEXTFORMAT__FLAG_*
 } AVTextFormatter;
 
 #define SECTION_MAX_NB_LEVELS    12
@@ -127,8 +127,8 @@ struct AVTextFormatContext {
 #define SHOW_OPTIONAL_FIELDS_NEVER       0
 #define SHOW_OPTIONAL_FIELDS_ALWAYS      1
 
-#define PRINT_STRING_OPT      1
-#define PRINT_STRING_VALIDATE 2
+#define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1
+#define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2
 
 int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writer, const char *args,
                         const struct AVTextFormatSection *sections, int nb_sections,
diff --git a/libavutil/textformat/avtextformat.c b/libavutil/textformat/avtextformat.c
index a2d87e8d42..cbac18c706 100644
--- a/libavutil/textformat/avtextformat.c
+++ b/libavutil/textformat/avtextformat.c
@@ -60,12 +60,12 @@ static const char *avtext_context_get_writer_name(void *p)
 
 static const AVOption textcontext_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" },
+      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=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" },
+      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"}},
 };
@@ -373,24 +373,24 @@ static inline int validate_string(AVTextFormatContext *wctx, char **dstp, const
             invalid_chars_nb++;
 
             switch (wctx->string_validation) {
-            case WRITER_STRING_VALIDATION_FAIL:
+            case AV_TEXTFORMAT_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:
+            case AV_TEXTFORMAT_STRING_VALIDATION_REPLACE:
                 av_bprintf(&dstbuf, "%s", wctx->string_validation_replacement);
                 break;
             }
         }
 
-        if (!invalid || wctx->string_validation == WRITER_STRING_VALIDATION_IGNORE)
+        if (!invalid || wctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_IGNORE)
             av_bprint_append_data(&dstbuf, p0, p-p0);
     }
 
-    if (invalid_chars_nb && wctx->string_validation == WRITER_STRING_VALIDATION_REPLACE) {
+    if (invalid_chars_nb && wctx->string_validation == AV_TEXTFORMAT_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);
@@ -477,12 +477,12 @@ int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *
 
     if (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
         (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
-        && (flags & PRINT_STRING_OPT)
+        && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
         && !(wctx->writer->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 & PRINT_STRING_VALIDATE) {
+        if (flags & AV_TEXTFORMAT_PRINT_STRING_VALIDATE) {
             char *key1 = NULL, *val1 = NULL;
             ret = validate_string(wctx, &key1, key);
             if (ret < 0) goto end;
@@ -522,7 +522,7 @@ void avtext_print_time(AVTextFormatContext *wctx, const char *key,
     char buf[128];
 
     if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
-        avtext_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
+        avtext_print_string(wctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
     } else {
         double d = ts * av_q2d(*time_base);
         struct unit_value uv;
@@ -536,7 +536,7 @@ void avtext_print_time(AVTextFormatContext *wctx, const char *key,
 void avtext_print_ts(AVTextFormatContext *wctx, const char *key, int64_t ts, int is_duration)
 {
     if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
-        avtext_print_string(wctx, key, "N/A", PRINT_STRING_OPT);
+        avtext_print_string(wctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
     } else {
         avtext_print_integer(wctx, key, ts);
     }
-- 
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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 5/8] ffprobe/avtextformat: Move flags
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
                   ` (3 preceding siblings ...)
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 4/8] ffprobe/avtextformat: Rename flags and enums softworkz
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 6/8] ffprobe/avtextformat: Rename writer to formatter softworkz
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 libavutil/avtextformat.h            | 4 ----
 libavutil/textformat/avtextformat.c | 4 ++++
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index e40aca1de7..68db9445c9 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -123,10 +123,6 @@ struct AVTextFormatContext {
     unsigned int string_validation_utf8_flags;
 };
 
-#define SHOW_OPTIONAL_FIELDS_AUTO       -1
-#define SHOW_OPTIONAL_FIELDS_NEVER       0
-#define SHOW_OPTIONAL_FIELDS_ALWAYS      1
-
 #define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1
 #define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2
 
diff --git a/libavutil/textformat/avtextformat.c b/libavutil/textformat/avtextformat.c
index cbac18c706..63a07051ce 100644
--- a/libavutil/textformat/avtextformat.c
+++ b/libavutil/textformat/avtextformat.c
@@ -36,6 +36,10 @@
 
 #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;
-- 
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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 6/8] ffprobe/avtextformat: Rename writer to formatter
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
                   ` (4 preceding siblings ...)
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 5/8] ffprobe/avtextformat: Move flags softworkz
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 7/8] ffprobe/avtextformat: Move formatters to avutil softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 8/8] ffprobe/avtextformat: Split out text writers as independent classes softworkz
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

..except for the actrual writers

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c                   | 100 ++++++++++++++--------------
 libavutil/avtextformat.h            |  14 ++--
 libavutil/textformat/avtextformat.c |  48 ++++++-------
 3 files changed, 81 insertions(+), 81 deletions(-)

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 5dc9803d5c..25eb3f0b61 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -412,36 +412,36 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
 #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
+#define MAX_REGISTERED_FORMATTERS_NB 64
 
-static const AVTextFormatter *registered_writers[MAX_REGISTERED_WRITERS_NB + 1];
+static const AVTextFormatter *registered_formatters[MAX_REGISTERED_FORMATTERS_NB + 1];
 
-static int writer_register(const AVTextFormatter *writer)
+static int formatter_register(const AVTextFormatter *formatter)
 {
-    static int next_registered_writer_idx = 0;
+    static int next_registered_formatter_idx = 0;
 
-    if (next_registered_writer_idx == MAX_REGISTERED_WRITERS_NB)
+    if (next_registered_formatter_idx == MAX_REGISTERED_FORMATTERS_NB)
         return AVERROR(ENOMEM);
 
-    registered_writers[next_registered_writer_idx++] = writer;
+    registered_formatters[next_registered_formatter_idx++] = formatter;
     return 0;
 }
 
-static const AVTextFormatter *writer_get_by_name(const char *name)
+static const AVTextFormatter *formatter_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];
+    for (i = 0; registered_formatters[i]; i++)
+        if (!strcmp(registered_formatters[i]->name, name))
+            return registered_formatters[i];
 
     return NULL;
 }
 
 
-/* WRITERS */
+/* FORMATTERS */
 
-#define DEFINE_WRITER_CLASS(name)                   \
+#define DEFINE_FORMATTER_CLASS(name)                   \
 static const char *name##_get_name(void *ctx)       \
 {                                                   \
     return #name ;                                  \
@@ -472,7 +472,7 @@ static const AVOption default_options[] = {
     {NULL},
 };
 
-DEFINE_WRITER_CLASS(default);
+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)
@@ -630,7 +630,7 @@ static const AVOption compact_options[]= {
     {NULL},
 };
 
-DEFINE_WRITER_CLASS(compact);
+DEFINE_FORMATTER_CLASS(compact);
 
 static av_cold int compact_init(AVTextFormatContext *wctx)
 {
@@ -768,7 +768,7 @@ static const AVOption csv_options[] = {
     {NULL},
 };
 
-DEFINE_WRITER_CLASS(csv);
+DEFINE_FORMATTER_CLASS(csv);
 
 static const AVTextFormatter csv_formatter = {
     .name                 = "csv",
@@ -802,7 +802,7 @@ static const AVOption flat_options[]= {
     {NULL},
 };
 
-DEFINE_WRITER_CLASS(flat);
+DEFINE_FORMATTER_CLASS(flat);
 
 static av_cold int flat_init(AVTextFormatContext *wctx)
 {
@@ -923,7 +923,7 @@ static const AVOption ini_options[] = {
     {NULL},
 };
 
-DEFINE_WRITER_CLASS(ini);
+DEFINE_FORMATTER_CLASS(ini);
 
 static char *ini_escape_str(AVBPrint *dst, const char *src)
 {
@@ -1030,7 +1030,7 @@ static const AVOption json_options[]= {
     { NULL }
 };
 
-DEFINE_WRITER_CLASS(json);
+DEFINE_FORMATTER_CLASS(json);
 
 static av_cold int json_init(AVTextFormatContext *wctx)
 {
@@ -1200,7 +1200,7 @@ static const AVOption xml_options[] = {
     {NULL},
 };
 
-DEFINE_WRITER_CLASS(xml);
+DEFINE_FORMATTER_CLASS(xml);
 
 static av_cold int xml_init(AVTextFormatContext *wctx)
 {
@@ -3381,7 +3381,7 @@ static void close_input_file(InputFile *ifile)
     avformat_close_input(&ifile->fmt_ctx);
 }
 
-static int probe_file(AVTextFormatContext *wctx, const char *filename,
+static int probe_file(AVTextFormatContext *tctx, const char *filename,
                       const char *print_filename)
 {
     InputFile ifile = { 0 };
@@ -3423,40 +3423,40 @@ static int probe_file(AVTextFormatContext *wctx, const char *filename,
 
     if (do_read_frames || do_read_packets) {
         if (do_show_frames && do_show_packets &&
-            wctx->writer->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT)
+            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)
-            avtext_print_section_header(wctx, NULL, section_id);
-        ret = read_packets(wctx, &ifile);
+            avtext_print_section_header(tctx, NULL, section_id);
+        ret = read_packets(tctx, &ifile);
         if (do_show_frames || do_show_packets)
-            avtext_print_section_footer(wctx);
+            avtext_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;
     }
 
@@ -4019,10 +4019,10 @@ static inline int check_section_show_entries(int section_id)
 
 int main(int argc, char **argv)
 {
-    const AVTextFormatter *w;
-    AVTextFormatContext *wctx;
+    const AVTextFormatter *f;
+    AVTextFormatContext *tctx;
     char *buf;
-    char *w_name = NULL, *w_args = NULL;
+    char *f_name = NULL, *f_args = NULL;
     int ret, input_ret, i;
 
     init_dynload();
@@ -4084,7 +4084,7 @@ int main(int argc, char **argv)
         goto end;
     }
 
-    writer_register_all();
+    formatters_register_all();
 
     if (!output_format)
         output_format = av_strdup("default");
@@ -4092,37 +4092,37 @@ int main(int argc, char **argv)
         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;
+    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 = formatter_get_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 = avtext_context_open(&wctx, w, w_args,
+    if ((ret = avtext_context_open(&tctx, f, f_args,
                            sections, FF_ARRAY_ELEMS(sections), output_filename, show_value_unit,
                             use_value_prefix, use_byte_value_binary_prefix, use_value_sexagesimal_format,
                             show_optional_fields, show_data_hash)) >= 0) {
-        if (w == &xml_formatter)
-            wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
+        if (f == &avtextformatter_xml)
+            tctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES;
 
-        avtext_print_section_header(wctx, NULL, SECTION_ID_ROOT);
+        avtext_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) ||
@@ -4132,15 +4132,15 @@ 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;
 
-        avtext_print_section_footer(wctx);
-        ret = avtext_context_close(&wctx);
+        avtext_print_section_footer(tctx);
+        ret = avtext_context_close(&tctx);
         if (ret < 0)
             av_log(NULL, AV_LOG_ERROR, "Writing output failed: %s\n", av_err2str(ret));
 
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index 68db9445c9..c7cdfe4144 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -66,8 +66,8 @@ typedef enum {
 } StringValidation;
 
 typedef struct AVTextFormatter {
-    const AVClass *priv_class;      ///< private class of the writer, if any
-    int priv_size;                  ///< private size for the writer context
+    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 *wctx);
@@ -85,15 +85,15 @@ typedef struct AVTextFormatter {
 #define SECTION_MAX_NB_SECTIONS 100
 
 struct AVTextFormatContext {
-    const AVClass *class;           ///< class of the writer
-    const AVTextFormatter *writer;           ///< the AVTextFormatter of which this is an instance
+    const AVClass *class;           ///< class of the formatter
+    const AVTextFormatter *formatter;           ///< the AVTextFormatter of which this is an instance
     AVIOContext *avio;              ///< the I/O context used to write
 
     void (* writer_w8)(AVTextFormatContext *wctx, int b);
     void (* writer_put_str)(AVTextFormatContext *wctx, const char *str);
     void (* writer_printf)(AVTextFormatContext *wctx, const char *fmt, ...);
 
-    char *name;                     ///< name of this writer instance
+    char *name;                     ///< name of this formatter instance
     void *priv;                     ///< private data for use by the filter
 
     const struct AVTextFormatSection *sections; ///< array containing all sections
@@ -108,7 +108,7 @@ struct AVTextFormatContext {
     /** 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 writers
+                                                  ///  used by various formatters
 
     int show_optional_fields;
     int show_value_unit;
@@ -126,7 +126,7 @@ struct AVTextFormatContext {
 #define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1
 #define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2
 
-int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writer, const char *args,
+int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *formatter, const AVTextFormatter *writer, const char *args,
                         const struct AVTextFormatSection *sections, int nb_sections,
                         const char *output_filename,
                         int show_value_unit,
diff --git a/libavutil/textformat/avtextformat.c b/libavutil/textformat/avtextformat.c
index 63a07051ce..90b6545a88 100644
--- a/libavutil/textformat/avtextformat.c
+++ b/libavutil/textformat/avtextformat.c
@@ -54,10 +54,10 @@ static const struct {
     { 1.125899906842624e15, 1e15, "Pi", "P" },
 };
 
-static const char *avtext_context_get_writer_name(void *p)
+static const char *avtext_context_get_formatter_name(void *p)
 {
     AVTextFormatContext *wctx = p;
-    return wctx->writer->name;
+    return wctx->formatter->name;
 }
 
 #define OFFSET(x) offsetof(AVTextFormatContext, x)
@@ -77,14 +77,14 @@ static const AVOption textcontext_options[] = {
 static void *trextcontext_child_next(void *obj, void *prev)
 {
     AVTextFormatContext *ctx = obj;
-    if (!prev && ctx->writer && ctx->writer->priv_class && ctx->priv)
+    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_writer_name,
+    .item_name  = avtext_context_get_formatter_name,
     .option     = textcontext_options,
     .version    = LIBAVUTIL_VERSION_INT,
     .child_next = trextcontext_child_next,
@@ -148,11 +148,11 @@ int avtext_context_close(AVTextFormatContext **pwctx)
 
     av_hash_freep(&wctx->hash);
 
-    if (wctx->writer->uninit)
-        wctx->writer->uninit(wctx);
+    if (wctx->formatter->uninit)
+        wctx->formatter->uninit(wctx);
     for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
         av_bprint_finalize(&wctx->section_pbuf[i], NULL);
-    if (wctx->writer->priv_class)
+    if (wctx->formatter->priv_class)
         av_opt_free(wctx->priv);
     av_freep(&wctx->priv);
     av_opt_free(wctx);
@@ -165,7 +165,7 @@ int avtext_context_close(AVTextFormatContext **pwctx)
 }
 
 
-int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writer, const char *args,
+int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *formatter, const AVTextFormatter *writer, const char *args,
                         const struct AVTextFormatSection *sections, int nb_sections,
                         const char *output_filename,
                         int show_value_unit,
@@ -183,7 +183,7 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writ
         goto fail;
     }
 
-    if (!(wctx->priv = av_mallocz(writer->priv_size))) {
+    if (!(wctx->priv = av_mallocz(formatter->priv_size))) {
         ret = AVERROR(ENOMEM);
         goto fail;
     }
@@ -200,16 +200,16 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writ
     }
 
     wctx->class = &textcontext_class;
-    wctx->writer = writer;
+    wctx->formatter = formatter;
     wctx->level = -1;
     wctx->sections = sections;
     wctx->nb_sections = nb_sections;
 
     av_opt_set_defaults(wctx);
 
-    if (writer->priv_class) {
+    if (formatter->priv_class) {
         void *priv_ctx = wctx->priv;
-        *(const AVClass **)priv_ctx = writer->priv_class;
+        *(const AVClass **)priv_ctx = formatter->priv_class;
         av_opt_set_defaults(priv_ctx);
     }
 
@@ -219,14 +219,14 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writ
         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_log(wctx, 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(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",
+                av_log(wctx, 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;
@@ -287,8 +287,8 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *writ
     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 (wctx->formatter->init)
+        ret = wctx->formatter->init(wctx);
     if (ret < 0)
         goto fail;
 
@@ -319,8 +319,8 @@ void avtext_print_section_header(AVTextFormatContext *wctx,
     memset(wctx->nb_item_type[wctx->level], 0, sizeof(wctx->nb_item_type[wctx->level]));
     wctx->section[wctx->level] = &wctx->sections[section_id];
 
-    if (wctx->writer->print_section_header)
-        wctx->writer->print_section_header(wctx, data);
+    if (wctx->formatter->print_section_header)
+        wctx->formatter->print_section_header(wctx, data);
 }
 
 void avtext_print_section_footer(AVTextFormatContext *wctx)
@@ -334,8 +334,8 @@ void avtext_print_section_footer(AVTextFormatContext *wctx)
         wctx->nb_item_type[wctx->level - 1][section_id]++;
     }
 
-    if (wctx->writer->print_section_footer)
-        wctx->writer->print_section_footer(wctx);
+    if (wctx->formatter->print_section_footer)
+        wctx->formatter->print_section_footer(wctx);
     wctx->level--;
 }
 
@@ -345,7 +345,7 @@ void avtext_print_integer(AVTextFormatContext *wctx,
     const struct AVTextFormatSection *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->formatter->print_integer(wctx, key, val);
         wctx->nb_item[wctx->level]++;
     }
 }
@@ -482,7 +482,7 @@ int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *
     if (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
         (wctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
         && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
-        && !(wctx->writer->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS)))
+        && !(wctx->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)) {
@@ -492,7 +492,7 @@ int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *
             if (ret < 0) goto end;
             ret = validate_string(wctx, &val1, val);
             if (ret < 0) goto end;
-            wctx->writer->print_string(wctx, key1, val1);
+            wctx->formatter->print_string(wctx, key1, val1);
         end:
             if (ret < 0) {
                 av_log(wctx, AV_LOG_ERROR,
@@ -502,7 +502,7 @@ int avtext_print_string(AVTextFormatContext *wctx, const char *key, const char *
             av_free(key1);
             av_free(val1);
         } else {
-            wctx->writer->print_string(wctx, key, val);
+            wctx->formatter->print_string(wctx, key, val);
         }
 
         wctx->nb_item[wctx->level]++;
-- 
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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 7/8] ffprobe/avtextformat: Move formatters to avutil
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
                   ` (5 preceding siblings ...)
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 6/8] ffprobe/avtextformat: Rename writer to formatter softworkz
@ 2025-02-27 14:01 ` softworkz
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 8/8] ffprobe/avtextformat: Split out text writers as independent classes softworkz
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c                 | 936 +-----------------------------
 libavutil/Makefile                |   6 +
 libavutil/avtextformat.h          |   8 +
 libavutil/textformat/tf_compact.c | 285 +++++++++
 libavutil/textformat/tf_default.c | 150 +++++
 libavutil/textformat/tf_flat.c    | 177 ++++++
 libavutil/textformat/tf_ini.c     | 165 ++++++
 libavutil/textformat/tf_json.c    | 220 +++++++
 libavutil/textformat/tf_xml.c     | 224 +++++++
 9 files changed, 1244 insertions(+), 927 deletions(-)
 create mode 100644 libavutil/textformat/tf_compact.c
 create mode 100644 libavutil/textformat/tf_default.c
 create mode 100644 libavutil/textformat/tf_flat.c
 create mode 100644 libavutil/textformat/tf_ini.c
 create mode 100644 libavutil/textformat/tf_json.c
 create mode 100644 libavutil/textformat/tf_xml.c

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 25eb3f0b61..28d499ac80 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -404,13 +404,7 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl)
 #endif
 }
 
-/* WRITERS API */
-
-
-
-#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__)
+/* FORMATTERS */
 
 #define MAX_REGISTERED_FORMATTERS_NB 64
 
@@ -439,919 +433,7 @@ static const AVTextFormatter *formatter_get_by_name(const char *name)
 }
 
 
-/* FORMATTERS */
-
-#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);
-}
-
-static const AVTextFormatter default_formatter = {
-    .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,
-};
-
-/* 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);
-}
-
-static const AVTextFormatter compact_formatter = {
-    .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);
-
-static const AVTextFormatter csv_formatter = {
-    .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,
-};
-
-/* 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);
-}
-
-static const AVTextFormatter flat_formatter = {
-    .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,
-};
-
-/* 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);
-}
-
-static const AVTextFormatter ini_formatter = {
-    .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,
-};
-
-/* 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);
-}
-
-static const AVTextFormatter json_formatter = {
-    .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,
-};
-
-/* 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);
-}
-
-static AVTextFormatter xml_formatter = {
-    .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,
-};
-
-static void writer_register_all(void)
+static void formatters_register_all(void)
 {
     static int initialized;
 
@@ -1359,13 +441,13 @@ static void writer_register_all(void)
         return;
     initialized = 1;
 
-    writer_register(&default_formatter);
-    writer_register(&compact_formatter);
-    writer_register(&csv_formatter);
-    writer_register(&flat_formatter);
-    writer_register(&ini_formatter);
-    writer_register(&json_formatter);
-    writer_register(&xml_formatter);
+    formatter_register(&avtextformatter_default);
+    formatter_register(&avtextformatter_compact);
+    formatter_register(&avtextformatter_csv);
+    formatter_register(&avtextformatter_flat);
+    formatter_register(&avtextformatter_ini);
+    formatter_register(&avtextformatter_json);
+    formatter_register(&avtextformatter_xml);
 }
 
 #define print_fmt(k, f, ...) do {              \
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 78c2c0d707..ce72aec1b7 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -189,6 +189,12 @@ OBJS = adler32.o                                                        \
        xga_font_data.o                                                  \
        xtea.o                                                           \
        tea.o                                                            \
+       textformat/tf_compact.o                                          \
+       textformat/tf_default.o                                          \
+       textformat/tf_flat.o                                             \
+       textformat/tf_ini.o                                              \
+       textformat/tf_json.o                                             \
+       textformat/tf_xml.o                                              \
        tx.o                                                             \
        tx_float.o                                                       \
        tx_double.o                                                      \
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index c7cdfe4144..4f428d74c6 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -162,4 +162,12 @@ void avtext_print_data_hash(AVTextFormatContext *wctx, const char *name, const u
 void avtext_print_integers(AVTextFormatContext *wctx, const char *name, uint8_t *data, int size, 
                            const char *format, int columns, int bytes, int offset_add);
 
+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 /* AVUTIL_AVTEXTFORMAT_H */
diff --git a/libavutil/textformat/tf_compact.c b/libavutil/textformat/tf_compact.c
new file mode 100644
index 0000000000..fa8d3f2223
--- /dev/null
+++ b/libavutil/textformat/tf_compact.c
@@ -0,0 +1,285 @@
+/*
+ * 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 "config.h"
+#include "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+
+#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 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/libavutil/textformat/tf_default.c b/libavutil/textformat/tf_default.c
new file mode 100644
index 0000000000..884d7160ff
--- /dev/null
+++ b/libavutil/textformat/tf_default.c
@@ -0,0 +1,150 @@
+/*
+ * 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 "config.h"
+#include "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+#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 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/libavutil/textformat/tf_flat.c b/libavutil/textformat/tf_flat.c
new file mode 100644
index 0000000000..767762dd0e
--- /dev/null
+++ b/libavutil/textformat/tf_flat.c
@@ -0,0 +1,177 @@
+/*
+ * 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 "config.h"
+#include "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+#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 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/libavutil/textformat/tf_ini.c b/libavutil/textformat/tf_ini.c
new file mode 100644
index 0000000000..3d0c4ea564
--- /dev/null
+++ b/libavutil/textformat/tf_ini.c
@@ -0,0 +1,165 @@
+/*
+ * 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 "config.h"
+#include "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+#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 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/libavutil/textformat/tf_json.c b/libavutil/textformat/tf_json.c
new file mode 100644
index 0000000000..c4dbf5a3d8
--- /dev/null
+++ b/libavutil/textformat/tf_json.c
@@ -0,0 +1,220 @@
+/*
+ * 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 "config.h"
+#include "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+#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 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/libavutil/textformat/tf_xml.c b/libavutil/textformat/tf_xml.c
new file mode 100644
index 0000000000..760d9fbf9d
--- /dev/null
+++ b/libavutil/textformat/tf_xml.c
@@ -0,0 +1,224 @@
+/*
+ * 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 "config.h"
+#include "../mem.h"
+#include "../avassert.h"
+#include "../avtextformat.h"
+#include "../bprint.h"
+#include "../error.h"
+#include "../hash.h"
+#include "../intreadwrite.h"
+#include "../macros.h"
+#include "../opt.h"
+
+#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 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,
+};
+
-- 
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] 9+ messages in thread

* [FFmpeg-devel] [PATCH 8/8] ffprobe/avtextformat: Split out text writers as independent classes
  2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
                   ` (6 preceding siblings ...)
  2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 7/8] ffprobe/avtextformat: Move formatters to avutil softworkz
@ 2025-02-27 14:01 ` softworkz
  7 siblings, 0 replies; 9+ messages in thread
From: softworkz @ 2025-02-27 14:01 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: softworkz

From: softworkz <softworkz@hotmail.com>

Signed-off-by: softworkz <softworkz@hotmail.com>
---
 fftools/ffprobe.c                   |  20 +++-
 libavutil/Makefile                  |   4 +
 libavutil/avtextformat.h            |  13 +--
 libavutil/avtextwriters.h           |  68 +++++++++++++
 libavutil/textformat/avtextformat.c | 145 ++++++++++++++++------------
 libavutil/textformat/tf_compact.c   |   6 +-
 libavutil/textformat/tf_default.c   |   6 +-
 libavutil/textformat/tf_flat.c      |   6 +-
 libavutil/textformat/tf_ini.c       |   6 +-
 libavutil/textformat/tf_json.c      |   6 +-
 libavutil/textformat/tf_xml.c       |   6 +-
 libavutil/textformat/tw_avio.c      | 129 +++++++++++++++++++++++++
 libavutil/textformat/tw_buffer.c    |  91 +++++++++++++++++
 libavutil/textformat/tw_stdout.c    |  82 ++++++++++++++++
 14 files changed, 499 insertions(+), 89 deletions(-)
 create mode 100644 libavutil/avtextwriters.h
 create mode 100644 libavutil/textformat/tw_avio.c
 create mode 100644 libavutil/textformat/tw_buffer.c
 create mode 100644 libavutil/textformat/tw_stdout.c

diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 28d499ac80..5ab1299442 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -3103,6 +3103,7 @@ int main(int argc, char **argv)
 {
     const AVTextFormatter *f;
     AVTextFormatContext *tctx;
+    AVTextWriterContext *wctx;
     char *buf;
     char *f_name = NULL, *f_args = NULL;
     int ret, input_ret, i;
@@ -3190,8 +3191,16 @@ int main(int argc, char **argv)
         goto end;
     }
 
-    if ((ret = avtext_context_open(&tctx, f, f_args,
-                           sections, FF_ARRAY_ELEMS(sections), output_filename, show_value_unit,
+    if (output_filename) {
+        ret = avtextwriter_create_file(&wctx, output_filename, 1);
+    } else
+        ret = avtextwriter_create_stdout(&wctx);
+
+    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)
@@ -3222,9 +3231,14 @@ int main(int argc, char **argv)
         input_ret = ret;
 
         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);
     }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index ce72aec1b7..09bcf3597d 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -10,6 +10,7 @@ HEADERS = adler32.h                                                     \
           avassert.h                                                    \
           avstring.h                                                    \
           avtextformat.h                                                \
+          avtextwriters.h                                               \
           avutil.h                                                      \
           base64.h                                                      \
           blowfish.h                                                    \
@@ -195,6 +196,9 @@ OBJS = adler32.o                                                        \
        textformat/tf_ini.o                                              \
        textformat/tf_json.o                                             \
        textformat/tf_xml.o                                              \
+       textformat/tw_avio.o                                             \
+       textformat/tw_buffer.o                                           \
+       textformat/tw_stdout.o                                           \
        tx.o                                                             \
        tx_float.o                                                       \
        tx_double.o                                                      \
diff --git a/libavutil/avtextformat.h b/libavutil/avtextformat.h
index 4f428d74c6..7c8e1315bf 100644
--- a/libavutil/avtextformat.h
+++ b/libavutil/avtextformat.h
@@ -29,6 +29,7 @@
 #include "bprint.h"
 #include "rational.h"
 #include "libavutil/hash.h"
+#include "avtextwriters.h"
 
 #define SECTION_MAX_NB_CHILDREN 11
 
@@ -53,7 +54,6 @@ struct AVTextFormatSection {
     int show_all_entries;
 };
 
-typedef struct AVTextFormatContext AVTextFormatContext;
 
 #define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS 1
 #define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2
@@ -65,6 +65,8 @@ typedef enum {
     AV_TEXTFORMAT_STRING_VALIDATION_NB
 } StringValidation;
 
+typedef struct AVTextFormatContext AVTextFormatContext;
+
 typedef struct AVTextFormatter {
     const AVClass *priv_class;      ///< private class of the formatter, if any
     int priv_size;                  ///< private size for the formatter context
@@ -87,11 +89,7 @@ typedef struct AVTextFormatter {
 struct AVTextFormatContext {
     const AVClass *class;           ///< class of the formatter
     const AVTextFormatter *formatter;           ///< the AVTextFormatter of which this is an instance
-    AVIOContext *avio;              ///< the I/O context used to write
-
-    void (* writer_w8)(AVTextFormatContext *wctx, int b);
-    void (* writer_put_str)(AVTextFormatContext *wctx, const char *str);
-    void (* writer_printf)(AVTextFormatContext *wctx, const char *fmt, ...);
+    AVTextWriterContext *writer;           ///< the AVTextWriterContext 
 
     char *name;                     ///< name of this formatter instance
     void *priv;                     ///< private data for use by the filter
@@ -126,9 +124,8 @@ struct AVTextFormatContext {
 #define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1
 #define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2
 
-int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *formatter, const AVTextFormatter *writer, const char *args,
+int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *formatter, const AVTextWriterContext *writer, const char *args,
                         const struct AVTextFormatSection *sections, int nb_sections,
-                        const char *output_filename,
                         int show_value_unit,
                         int use_value_prefix,
                         int use_byte_value_binary_prefix,
diff --git a/libavutil/avtextwriters.h b/libavutil/avtextwriters.h
new file mode 100644
index 0000000000..873a062777
--- /dev/null
+++ b/libavutil/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 AVUTIL_AVTEXTWRITERS_H
+#define AVUTIL_AVTEXTWRITERS_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "attributes.h"
+#include "dict.h"
+#include <libavformat/avio.h>
+#include "bprint.h"
+#include "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 /* AVUTIL_AVTEXTWRITERS_H */
diff --git a/libavutil/textformat/avtextformat.c b/libavutil/textformat/avtextformat.c
index 90b6545a88..b1cf93f63e 100644
--- a/libavutil/textformat/avtextformat.c
+++ b/libavutil/textformat/avtextformat.c
@@ -54,6 +54,7 @@ static const struct {
     { 1.125899906842624e15, 1e15, "Pi", "P" },
 };
 
+
 static const char *avtext_context_get_formatter_name(void *p)
 {
     AVTextFormatContext *wctx = p;
@@ -90,45 +91,6 @@ static const AVClass textcontext_class = {
     .child_next = trextcontext_child_next,
 };
 
-
-static inline void textoutput_w8_avio(AVTextFormatContext *wctx, int b)
-{
-    avio_w8(wctx->avio, b);
-}
-
-static inline void textoutput_put_str_avio(AVTextFormatContext *wctx, const char *str)
-{
-    avio_write(wctx->avio, str, strlen(str));
-}
-
-static inline void textoutput_printf_avio(AVTextFormatContext *wctx, const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start(ap, fmt);
-    avio_vprintf(wctx->avio, fmt, ap);
-    va_end(ap);
-}
-
-static inline void textoutput_w8_printf(AVTextFormatContext *wctx, int b)
-{
-    printf("%c", b);
-}
-
-static inline void textoutput_put_str_printf(AVTextFormatContext *wctx, const char *str)
-{
-    printf("%s", str);
-}
-
-static inline void textoutput_printf_printf(AVTextFormatContext *wctx, const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start(ap, fmt);
-    vprintf(fmt, ap);
-    va_end(ap);
-}
-
 static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
 {
     int i;
@@ -156,18 +118,13 @@ int avtext_context_close(AVTextFormatContext **pwctx)
         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(pwctx);
     return ret;
 }
 
 
-int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *formatter, const AVTextFormatter *writer, const char *args,
+int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *formatter, const AVTextWriterContext *writer_context, const char *args,
                         const struct AVTextFormatSection *sections, int nb_sections,
-                        const char *output_filename,
                         int show_value_unit,
                         int use_value_prefix,
                         int use_byte_value_binary_prefix,
@@ -204,6 +161,7 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *form
     wctx->level = -1;
     wctx->sections = sections;
     wctx->nb_sections = nb_sections;
+    wctx->writer = writer_context;
 
     av_opt_set_defaults(wctx);
 
@@ -269,21 +227,6 @@ int avtext_context_open(AVTextFormatContext **pwctx, const AVTextFormatter *form
         }
     }
 
-    if (!output_filename) {
-        wctx->writer_w8 = textoutput_w8_printf;
-        wctx->writer_put_str = textoutput_put_str_printf;
-        wctx->writer_printf = textoutput_printf_printf;
-    } else {
-        if ((ret = avio_open(&wctx->avio, output_filename, AVIO_FLAG_WRITE)) < 0) {
-            av_log(wctx, AV_LOG_ERROR,
-                   "Failed to open output '%s' with error: %s\n", output_filename, av_err2str(ret));
-            goto fail;
-        }
-        wctx->writer_w8 = textoutput_w8_avio;
-        wctx->writer_put_str = textoutput_put_str_avio;
-        wctx->writer_printf = textoutput_printf_avio;
-    }
-
     for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
         av_bprint_init(&wctx->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
 
@@ -614,3 +557,85 @@ void avtext_print_integers(AVTextFormatContext *wctx, const char *name,
     avtext_print_string(wctx, 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;
+}
+
diff --git a/libavutil/textformat/tf_compact.c b/libavutil/textformat/tf_compact.c
index fa8d3f2223..8d4078d3ab 100644
--- a/libavutil/textformat/tf_compact.c
+++ b/libavutil/textformat/tf_compact.c
@@ -36,9 +36,9 @@
 #include "../opt.h"
 
 
-#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 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)                   \
diff --git a/libavutil/textformat/tf_default.c b/libavutil/textformat/tf_default.c
index 884d7160ff..8fa6615476 100644
--- a/libavutil/textformat/tf_default.c
+++ b/libavutil/textformat/tf_default.c
@@ -35,9 +35,9 @@
 #include "../macros.h"
 #include "../opt.h"
 
-#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 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)       \
diff --git a/libavutil/textformat/tf_flat.c b/libavutil/textformat/tf_flat.c
index 767762dd0e..a6474e5ba0 100644
--- a/libavutil/textformat/tf_flat.c
+++ b/libavutil/textformat/tf_flat.c
@@ -35,9 +35,9 @@
 #include "../macros.h"
 #include "../opt.h"
 
-#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 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)       \
diff --git a/libavutil/textformat/tf_ini.c b/libavutil/textformat/tf_ini.c
index 3d0c4ea564..d9fadcf9f0 100644
--- a/libavutil/textformat/tf_ini.c
+++ b/libavutil/textformat/tf_ini.c
@@ -35,9 +35,9 @@
 #include "../macros.h"
 #include "../opt.h"
 
-#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 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)       \
diff --git a/libavutil/textformat/tf_json.c b/libavutil/textformat/tf_json.c
index c4dbf5a3d8..a18e27deae 100644
--- a/libavutil/textformat/tf_json.c
+++ b/libavutil/textformat/tf_json.c
@@ -35,9 +35,9 @@
 #include "../macros.h"
 #include "../opt.h"
 
-#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 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)       \
diff --git a/libavutil/textformat/tf_xml.c b/libavutil/textformat/tf_xml.c
index 760d9fbf9d..a306c4203b 100644
--- a/libavutil/textformat/tf_xml.c
+++ b/libavutil/textformat/tf_xml.c
@@ -35,9 +35,9 @@
 #include "../macros.h"
 #include "../opt.h"
 
-#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 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)       \
diff --git a/libavutil/textformat/tw_avio.c b/libavutil/textformat/tw_avio.c
new file mode 100644
index 0000000000..83437d2e6f
--- /dev/null
+++ b/libavutil/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 "../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/libavutil/textformat/tw_buffer.c b/libavutil/textformat/tw_buffer.c
new file mode 100644
index 0000000000..dbdd7759c9
--- /dev/null
+++ b/libavutil/textformat/tw_buffer.c
@@ -0,0 +1,91 @@
+/*
+ * 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 "../opt.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 = 0;
+
+    ret = avtextwriter_context_open(pwctx, &avtextwriter_buffer);
+    if (ret < 0)
+        return ret;
+
+    ctx = (*pwctx)->priv;
+    ctx->buffer = buffer;
+
+    return ret;
+}
diff --git a/libavutil/textformat/tw_stdout.c b/libavutil/textformat/tw_stdout.c
new file mode 100644
index 0000000000..4ea10f272e
--- /dev/null
+++ b/libavutil/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 "../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] 9+ messages in thread

end of thread, other threads:[~2025-02-27 14:04 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-02-27 14:01 [FFmpeg-devel] [PATCH 0/8] [RFC] avtextformat: Transform text writing into an independent API ffmpegagent
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 1/8] ffprobe/avtextformat: Rename and move writer structs softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 2/8] ffprobe/avtextformat: Rename and move writer functions and options softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 3/8] ffprobe/avtextformat: Generalize ffprobe specifics softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 4/8] ffprobe/avtextformat: Rename flags and enums softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 5/8] ffprobe/avtextformat: Move flags softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 6/8] ffprobe/avtextformat: Rename writer to formatter softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 7/8] ffprobe/avtextformat: Move formatters to avutil softworkz
2025-02-27 14:01 ` [FFmpeg-devel] [PATCH 8/8] ffprobe/avtextformat: Split out text writers as independent classes softworkz

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git