diff --git a/doc/avwriter_intro.md b/doc/avwriter_intro.md
new file mode 100644
index 0000000000..4fd8a5a4ad
--- /dev/null
+++ b/doc/avwriter_intro.md
@@ -0,0 +1,109 @@
+# Quick start guide for AVWriter
+
+AVWriter is an API to unify functions returning strings (or any arbitrary
+binary buffer) and to make building strings from parts easier. Here is a
+quick introduction with pairs of “what I would do without AVWriter” /
+“how to do it with AVWriter” of example code.
+
+## I want a `char*` buffer, the function wants an AVWriter
+
+Old-style code:
+
+```
+        char *buf;
+        ret = av_something_to_string(&buf, something);
+        if (ret < 0)
+            die("Failed");
+        use_string(buf);
+        av_freep(&buf);
+```
+
+Equivalent code with AVWriter:
+
+```
+        AVWriter wr = av_dynbuf_writer();
+        av_something_write(wr, something, 0);
+        if (av_writer_get_error(wr, 0) < 0)
+            die("Failed");
+        use_string(av_dynbuf_writer_get_data(wr, NULL));
+        av_dynbuf_writer_finalize(wr, NULL, NULL);
+```
+
+If the string is small enough, no dynamic memory allocation happens.
+
+The NULL to `av_dynbuf_writer_get_data()` can be used to retrieve the size
+of the data in the buffer.
+
+Calling `av_writer_get_error()` is mandatory.
+
+## I want a *persistent* `char*` buffer, the function wants an AVWriter
+
+Old-style code:
+
+```
+        char *buf;
+        ret = av_something_to_string(&buf, something);
+        if (ret < 0)
+            die("Failed");
+        ctx->string = buf;
+```
+
+Equivalent code with AVWriter:
+
+```
+        AVWriter wr = av_dynbuf_writer();
+        av_something_write(wr, something, 0);
+        ret = av_dynbuf_writer_finalize(wr, &ctx->string, NULL);
+        if (ret < 0)
+            die("Failed");
+```
+
+## I have a `char[]` buffer, the function wants an AVWriter
+
+Old-style code:
+
+```
+        char buf[BUF_SIZE];
+        av_something_to_string(buf, sizeof(buf), something);
+        use_string(buf);
+```
+
+Equivalent code with AVWriter:
+
+```
+        char buf[BUF_SIZE];
+        av_something_write(av_buf_writer(buf, sizeof(buf)), something, 0);
+        use_string(buf);
+```
+
+## I need to build a string from parts
+
+Old-style code:
+
+```
+        char buf[1024];
+        int pos = 0;
+        pos += snprintf(buf + pos, sizeof(buf) - pos,
+                        "Stream %d: ", i);
+        av_get_channel_layout_string(buf + pos, sizeof(buf) - pos,
+                                     nb, layout);
+        pos += strlen(buf + pos);
+        pos += snprintf(buf + pos, sizeof(buf) - pos,
+                        ", %s", av_get_sample_fmt_name(fmt));
+```
+
+Note: this code could overflow the buffer.
+
+Equivalent code with AVWriter:
+
+```
+        AVWriter wr = av_dynbuf_writer();
+        av_writer_printf(wr, "Stream %d: ", i);
+        av_channel_layout_write(wr, nb, layout, 0);
+        av_writer_printf(wr, ", %s", av_get_sample_fmt_name(fmt));
+```
+
+See the first example on how to access the resulting string.
+
+Note: this is very similar to using AVBPrint; from this side, AVWriter
+replaces AVBPrint.
diff --git a/libavutil/writer.h b/libavutil/writer.h
new file mode 100644
index 0000000000..55e2cf3ea6
--- /dev/null
+++ b/libavutil/writer.h
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2022 The FFmpeg project
+ *
+ * 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_WRITER_H
+#define AVUTIL_WRITER_H
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+#include "extendable.h"
+#include "bprint.h"
+
+/**
+ * @defgroup av_writer AVWriter
+ *
+ * Object-oriented API to write strings and binary data.
+ *
+ * @{
+ */
+
+typedef struct AVWriterMethods AVWriterMethods;
+
+/**
+ * Opaque object to write strings and binary data.
+ *
+ * AVWriter is meant to allow to build and return strings (and blocks of
+ * binary data) efficiently between functions.
+ * For example, a function that serialize something into a string will
+ * actually write into an AVWriter, and the caller will choose the way the
+ * data will be stored or processed on the fly.
+ *
+ * For a quick introduction on how to use AVWriter in simple cases, see
+ * doc/avwriter_intro.md.
+ *
+ * There are various pre-defined types of AVWriter, see below:
+ *
+ * - av_dynbuf_writer() writes the data into a buffer that is grown with
+ *   av_realloc() if it does not fit in the initial buffer; it is the
+ *   recommended choice.
+ *
+ * - av_buf_writer() writes the data into a pre-existing finite buffer.
+ *
+ * - av_stdio_writer() writes the data into a FILE*.
+ *
+ * - av_log_writer() writes the data to av_log().
+ *
+ * AVWriter objects are passed by value. It allows creating a writer on the
+ * fly, without extra variable or error checking. The structure can never
+ * change.
+ */
+typedef struct AVWriter {
+    const AVWriterMethods *methods;
+    void *obj;
+} AVWriter;
+
+/**
+ * Write a data buffer to an AVWriter.
+ */
+void av_writer_write(AVWriter wr, const char *buf, size_t size);
+
+/**
+ * Write a 0-terminated string to an AVWriter.
+ */
+void av_writer_print(AVWriter wr, const char *str);
+
+/**
+ * Write a formatted string to an AVWriter.
+ * Note: do not use libc-specific extensions to the format string.
+ */
+void av_writer_printf(AVWriter wr, const char *fmt, ...);
+
+/**
+ * Write a formatted to string an AVWriter using a va_list.
+ */
+void av_writer_vprintf(AVWriter wr, const char *fmt, va_list va);
+
+/**
+ * Write a sequence of identical chars to an AVWriter.
+ */
+void av_writer_add_chars(AVWriter wr, char c, size_t n);
+
+/**
+ * Flush data that may be buffered in the writer.
+ */
+void av_writer_flush(AVWriter wr);
+
+/**
+ * Return the error status of the writer, an AVERROR code.
+ *
+ * If self_only is non-zero, return errors only on this writer and not on
+ * possible other writers that it got from the caller. If in doubt, use 0.
+ */
+int av_writer_get_error(AVWriter wr, int self_only);
+
+/**
+ * Print a sane value for invalid input.
+ *
+ * This is a flag for all av_something_write() functions: when presented
+ * with an invalid "something", if the flag is not present they should leave
+ * the writer unchanged; if the flag is present they should print a sane
+ * fallback string.
+ */
+#define AV_WRITER_FALLBACK 0x10000
+
+/***************************************************************************/
+
+/**
+ * @defgroup av_dynbuf_writer AVDynbufWriter
+ *
+ * An implementation of AVWriter that writes to a dynamic buffer.
+ *
+ * The buffer is kept 0-terminated.
+ *
+ * The buffer is initially kept in the variable itself, it switches to heap
+ * allocation if it grows too large. In that case, av_writer_get_error() can
+ * return AVERROR(ENOMEM) if the allocation fails.
+ *
+ * @{
+ */
+
+/**
+ * An AVWriter object for pre-allocated memory buffers.
+ *
+ * Can be allocated on the stack.
+ *
+ * Should be inited with one of the utility functions.
+ */
+typedef struct AVDynbufWriter {
+    size_t self_size; /**< Size of the structure itself */
+    unsigned flags;
+    AVBPrint bp;
+} AVDynbufWriter;
+
+const AVWriterMethods *av_dynbuf_writer_get_methods(void);
+int av_dynbuf_writer_check(AVWriter wr);
+
+/**
+ * Initialize an AVDynbufWriter.
+ *
+ * @return  dwr itself
+ * dwr->self_size must be set.
+ */
+AVDynbufWriter *av_dynbuf_writer_init(AVDynbufWriter *dwr);
+
+/**
+ * Create an AVWriter from an AVDynbufWriter structure.
+ */
+AVWriter av_dynbuf_writer_wrap(AVDynbufWriter *dwr);
+
+/**
+ * Create an AVWriter to a dynamic buffer.
+ *
+ * Note: as it relies on a compound statement, the AVDynbufWriter object has
+ * a scope limited to the block where this macro is called.
+ */
+#define av_dynbuf_writer() \
+    av_dynbuf_writer_wrap(av_dynbuf_writer_init(&FF_NEW_SZ(AVDynbufWriter)))
+
+/**
+ * Get a pointer to the buffer data of an AVWriter to a dynamic buffer.
+ *
+ * If not null, size will be set to the size of the data in the buffer.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ * Undefined behavior if called without checking for error first.
+ */
+char *av_dynbuf_writer_get_data(AVWriter wr, size_t *size);
+
+/**
+ * Get a pointer to the buffer data of an AVWriter to a dynamic buffer
+ * (unsafe version).
+ *
+ * If not null, size will be set to the size of the data in the buffer.
+ *
+ * If the writer is in error, the data may be truncated.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ */
+char *av_dynbuf_writer_get_data_unsafe(AVWriter wr, size_t *size);
+
+/**
+ * Finalize an AVWriter to a dynamic buffer.
+ *
+ * @arg[out] buf   if not NULL, used to return the buffer contents
+ * @arg[out] size  if not NULL, used to return the size of the data
+ * @return  0 for success or error code (probably AVERROR(ENOMEM))
+ *
+ * In case of error, the buffer will not be duplicated but still freed.
+ * Same if the writer is already in error.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ */
+int av_dynbuf_writer_finalize(AVWriter wr, char **buf, size_t *size);
+
+/**
+ * Finalize an AVWriter to a dynamic buffer (unsafe version).
+ *
+ * @arg[out] buf   if not NULL, used to return the buffer contents
+ * @arg[out] size  if not NULL, used to return the size of the data
+ * @return  0 for success or error code (probably AVERROR(ENOMEM))
+ *
+ * If the writer is in error, the returned data may be truncated.
+ *
+ * In case of error, the buffer will not be duplicated but still freed.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ */
+int av_dynbuf_writer_finalize_unsafe(AVWriter wr, char **buf, size_t *size);
+
+/**
+ * Allocate chars in the buffer for external use.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ */
+char *av_dynbuf_writer_get_buffer(AVWriter wr, size_t size, size_t *rsize);
+
+/**
+ * Advance the position in the buffer.
+ *
+ * size must be <= *rsize on a previous call to
+ * av_dynbuf_writer_get_buffer().
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ * Undefined behavior is called not directly after
+ * av_dynbuf_writer_get_buffer().
+ */
+void av_dynbuf_writer_advance_buffer(AVWriter wr, size_t size);
+
+/**
+ * @}
+ */
+
+/***************************************************************************/
+
+/**
+ * @defgroup av_buf_writer AVBufWriter
+ *
+ * An implementtion of AVWriter that writes into an already-allocated buffer
+ * of memory.
+ *
+ * The buffer is kept 0-terminated. If the buffer is too small, the data is
+ * discarded, but the total size is computed.
+ *
+ * @{
+ */
+
+/**
+ * An AVWriter object for pre-allocated memory buffers.
+ *
+ * Can be allocated on the stack.
+ *
+ * Should be inited with one of the utility functions.
+ */
+typedef struct AVBufWriter {
+    size_t self_size; /**< Size of the structure itself */
+    char *buf; /**< Memory buffer. Must not be NULL nor empty. */
+    size_t size; /**< Size of the memory buffer. Must not be 0. */
+    size_t pos; /**< Position in the memory buffer. */
+} AVBufWriter;
+
+const AVWriterMethods *av_buf_writer_get_methods(void);
+int av_buf_writer_check(AVWriter wr);
+
+/**
+ * Initialize an AVBufWriter to an already-allocated memory buffer.
+ *
+ * @return  bwr itself
+ * bwr->self_size must be set.
+ */
+AVBufWriter *av_buf_writer_init(AVBufWriter *bwr, char *buf, size_t size);
+
+/**
+ * Create an AVWriter from an AVBufWriter structure.
+ */
+AVWriter av_buf_writer_wrap(AVBufWriter *bwr);
+
+/**
+ * Create an AVWriter to a buffer.
+ *
+ * Note: as it relies on a compound statement, the AVBufWriter object has
+ * a scope limited to the block where this macro is called.
+ */
+#define av_buf_writer(buf, size) \
+    av_buf_writer_wrap(av_buf_writer_init(&FF_NEW_SZ(AVBufWriter), (buf), (size)))
+
+/**
+ * Create an AVWriter to a char[] or equivalent.
+ *
+ * Note: as it relies on a compound statement, the AVBufWriter object has
+ * a scope limited to the block where this macro is called.
+ */
+#define av_buf_writer_array(array) \
+    av_buf_writer(array, sizeof (array))
+
+/**
+ * @}
+ */
+
+/***************************************************************************/
+
+/**
+ * @defgroup av_stdio_writer AVStdioWriter
+ *
+ * An implementation of AVWriter that writes to stdio.
+ *
+ * @{
+ */
+
+const AVWriterMethods *av_stdio_writer_get_methods(void);
+int av_stdio_writer_check(AVWriter wr);
+
+/**
+ * Create an AVWriter that goes to a stdio FILE.
+ */
+AVWriter av_stdio_writer(FILE *out);
+
+/**
+ * @}
+ */
+
+/***************************************************************************/
+
+/**
+ * @defgroup av_log_writer AVLogWriter
+ *
+ * An implementation of AVWriter that writes to av_log().
+ *
+ * @{
+ */
+
+const AVWriterMethods *av_log_writer_get_methods(void);
+int av_log_writer_check(AVWriter wr);
+
+/**
+ * Create an AVWriter that goes to av_log(obj).
+ */
+AVWriter av_log_writer(void *obj);
+
+/**
+ * Log to a writer.
+ * If wr does not go to av_log(), level is ignored.
+ */
+void av_log_writer_log(AVWriter wr, int level, const char *fmt, ...);
+
+/**
+ * @}
+ */
+
+/***************************************************************************/
+
+/**
+ * @defgroup av_writer_methods AVWriterMethods
+ *
+ * Structures and utility needed to define new types of AVWriter.
+ *
+ * @{
+ */
+
+/**
+ * Set of methods for implementing an AVWriter.
+ *
+ * Applications that want to implement other kinds of AVWriter
+ * need to create a structure with some of these methods implemented.
+ *
+ * Every type of AVWriter is supposed to have a pair of functions:
+ * av_something_writer_get_methods() returns the methods for that type.
+ * av_something_writer_checks() returns 1 if the writer is of that type.
+ *
+ * A macro to define the structure and functions is provided below.
+ *
+ * When a type of AVWriter defines specific functions, it is usually the
+ * responsibility of the caller to ensure that they are called only with a
+ * compatible AVWriter; otherwise the behavior is undefined.
+ */
+struct AVWriterMethods {
+
+    /**
+     * Size of the structure itself.
+     * Must normally be set to sizeof(AVWriterMethods).
+     */
+    size_t self_size;
+
+    /**
+     * Name of the object type.
+     */
+    const char *name;
+
+    /**
+     * Warn that an operation was impossible even with fallbacks.
+     */
+    void (*impossible)(AVWriter wr, const char *message);
+
+    /**
+     * Notify that size chars have been discarded
+     */
+    void (*notify_discard)(AVWriter wr, size_t size);
+
+    /**
+     * Get the error status of the writer
+     * If self_only is non-zero, return errors only on this writer and not on
+     * possible other writers that it got from the caller. Errors from
+     * writers created internally should always be checked.
+     */
+    int (*get_error)(AVWriter wr, int self_only);
+
+    /**
+     * Write chars directly.
+     */
+    void (*write)(AVWriter wr, const char *buf, size_t size);
+
+    /**
+     * Write a formatted string.
+     */
+    void (*vprintf)(AVWriter wr, const char *fmt, va_list ap);
+
+    /**
+     * Get a buffer to write data.
+     * If *size is returned < min_size, data may get discarded.
+     * A writer that implements this method must implement advance_buffer too.
+     * If vprintf is not implemented, av_write_printf() will use
+     * get_buffer() and vsnprintf(): it will need an extra char in the
+     * buffer for the 0 that vsnprintf() adds.
+     */
+    char *(*get_buffer)(AVWriter wr, size_t min_size, size_t *size);
+
+    /**
+     * Acknowledge chars written in a buffer obtained with get_buffer().
+     * size is guaranteed <= the size value returned by get_buffer().
+     */
+    void (*advance_buffer)(AVWriter wr, size_t size);
+
+    /**
+     * Flush immediately data that may be buffered in the writer.
+     */
+    void (*flush)(AVWriter wr);
+
+};
+
+/**
+ * Convenience macro for the boilerplate necessary to define a writer class.
+ *
+ * @arg qual    type qualifier for for the symbols, for example "static"
+ * @arg type    class name for the type of writer,
+ *              for example "AVBufWriter" or "MyWriter"
+ * @arg prefix  prefix for the symbols,
+ *              for example "av_buf_writer" or "my_writer"
+ */
+#define AV_WRITER_DEFINE_METHODS(qual, type, prefix) \
+static const AVWriterMethods prefix##_methods; \
+qual const AVWriterMethods *prefix##_get_methods(void) { \
+    return &prefix##_methods; \
+} \
+qual int prefix##_check(AVWriter wr) { \
+    return wr.methods == prefix##_get_methods(); \
+} \
+static const AVWriterMethods prefix##_methods =
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_WRITER_H */
