diff --git a/libavutil/Makefile b/libavutil/Makefile
index 57e6e3d7e8..b4609cd7bf 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -162,7 +162,8 @@ OBJS = adler32.o                                                        \
        tea.o                                                            \
        tx.o                                                             \
        tx_float.o                                                       \
-       tx_double.o
+       tx_double.o                                                      \
+       writer.o
 
 OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
 OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
diff --git a/libavutil/writer.c b/libavutil/writer.c
new file mode 100644
index 0000000000..c564a4fa3b
--- /dev/null
+++ b/libavutil/writer.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2019 Nicolas George
+ *
+ * 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 "avassert.h"
+#include "log.h"
+#include "writer.h"
+
+/***************************************************************************
+ * Generic API
+ ***************************************************************************/
+
+#define FIELDOK(st, f) ((char *)(&(st)->f + 1) <= (char *)(st) + (st)->self_size)
+
+static void printf_unchecked(AVWriter wr, const char *fmt, ...)
+{
+    va_list va;
+
+    va_start(va, fmt);
+    wr.methods->vprintf(wr, fmt, va);
+    va_end(va);
+}
+
+static void write_or_discard(AVWriter wr, size_t buf_size, size_t write_size)
+{
+    av_assert1(wr.methods->advance_buffer);
+    wr.methods->advance_buffer(wr, FFMIN(buf_size, write_size));
+    if (write_size > buf_size && wr.methods->notify_discard)
+        wr.methods->notify_discard(wr, write_size - buf_size);
+}
+
+static void av_writer_impossible(AVWriter wr, const char *message)
+{
+    av_assert1(FIELDOK(wr.methods, impossible));
+    if (wr.methods->impossible)
+        wr.methods->impossible(wr, message);
+    else
+        av_log(NULL, AV_LOG_ERROR, "Operation impossible with %s: %s\n",
+               wr.methods->name, message);
+}
+
+void av_writer_write(AVWriter wr, const char *data, size_t size)
+{
+    av_assert1(FIELDOK(wr.methods, vprintf));
+    if (wr.methods->write) {
+        wr.methods->write(wr, data, size);
+    } else if (wr.methods->get_buffer) {
+        size_t buf_size;
+        char *buf = wr.methods->get_buffer(wr, size, &buf_size);
+        if (buf_size > 0)
+            memcpy(buf, data, FFMIN(size, buf_size));
+        write_or_discard(wr, buf_size, size);
+    } else if (wr.methods->vprintf) {
+        size_t i;
+        for (i = 0; i < size; i++)
+            printf_unchecked(wr, "%c", data[i]);
+    } else {
+        av_writer_impossible(wr, "av_writer_write()");
+    }
+}
+
+void av_writer_print(AVWriter wr, const char *str)
+{
+    av_writer_write(wr, str, strlen(str));
+}
+
+void av_writer_printf(AVWriter wr, const char *fmt, ...)
+{
+    va_list va;
+
+    va_start(va, fmt);
+    av_writer_vprintf(wr, fmt, va);
+    va_end(va);
+}
+
+void av_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+    av_assert1(FIELDOK(wr.methods, vprintf));
+    if (wr.methods->vprintf) {
+        wr.methods->vprintf(wr, fmt, va);
+    } else if (wr.methods->get_buffer) {
+        size_t buf_size;
+        char *buf = wr.methods->get_buffer(wr, 128, &buf_size);
+        va_list va2;
+        int ret;
+        va_copy(va2, va);
+        ret = vsnprintf(buf, buf_size, fmt, va2);
+        va_end(va2);
+        if (ret < 0)
+            return;
+        if ((size_t)ret + 1 > buf_size) {
+            buf = wr.methods->get_buffer(wr, ret + 1, &buf_size);
+            ret = vsnprintf(buf, buf_size, fmt, va);
+            if (ret < 0)
+                return;
+        }
+        write_or_discard(wr, buf_size, ret);
+    } else if (wr.methods->write) {
+        AVBPrint bp;
+        av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+        av_vbprintf(&bp, fmt, va);
+        if (av_bprint_is_complete(&bp)) {
+            wr.methods->write(wr, bp.str, bp.len);
+        } else {
+            wr.methods->write(wr, bp.str, bp.size - 1);
+            if (wr.methods->notify_discard)
+                wr.methods->notify_discard(wr, bp.len - bp.size + 1);
+        }
+        av_bprint_finalize(&bp, NULL);
+    } else {
+        av_writer_impossible(wr, "av_writer_vprintf()");
+    }
+}
+
+void av_writer_add_chars(AVWriter wr, char c, size_t n)
+{
+    char buf[512];
+    size_t m;
+
+    m = FFMIN(n, sizeof(buf) - 1);
+    memset(buf, c, m);
+    while (n) {
+        av_writer_write(wr, buf, m);
+        n -= m;
+        m = FFMIN(n, sizeof(buf) - 1);
+    }
+}
+
+/***************************************************************************
+ * AVBufWriter - write to pre-allocated memory
+ ***************************************************************************/
+
+static size_t buf_writer_room(AVBufWriter *bwr)
+{
+    return bwr->pos < bwr->size ? bwr->size - bwr->pos - 1 : 0;
+}
+
+static void buf_writer_write(AVWriter wr, const char *data, size_t size)
+{
+    AVBufWriter *bwr = wr.obj;
+
+    av_assert1(av_buf_writer_check(wr));
+    av_assert1(FIELDOK(bwr, pos));
+    size = FFMIN(buf_writer_room(bwr), size);
+    memcpy(bwr->buf + bwr->pos, data, size);
+    bwr->pos += size;
+    bwr->buf[bwr->pos] = 0;
+}
+
+static void buf_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+    AVBufWriter *bwr = wr.obj;
+    int ret;
+
+    av_assert1(av_buf_writer_check(wr));
+    av_assert1(FIELDOK(bwr, pos));
+    ret = vsnprintf(bwr->buf + bwr->pos, buf_writer_room(bwr) + 1, fmt, va);
+    if (ret > 0)
+        bwr->pos += ret;
+}
+
+static char *buf_writer_get_buffer(AVWriter wr, size_t min_size, size_t *size)
+{
+    AVBufWriter *bwr = wr.obj;
+
+    av_assert1(av_buf_writer_check(wr));
+    av_assert1(FIELDOK(bwr, pos));
+    *size = bwr->size - 1 - bwr->pos;
+    return bwr->buf + bwr->pos;
+}
+
+static void buf_writer_advance_buffer(AVWriter wr, size_t size)
+{
+    AVBufWriter *bwr = wr.obj;
+
+    av_assert1(av_buf_writer_check(wr));
+    av_assert1(FIELDOK(bwr, pos));
+    bwr->pos += size;
+    bwr->buf[bwr->pos] = 0;
+}
+
+AV_WRITER_DEFINE_METHODS(/*public*/, AVBufWriter, av_buf_writer) {
+    .self_size        = sizeof(AVWriterMethods),
+    .name             = "AVBufWriter",
+    .write            = buf_writer_write,
+    .vprintf          = buf_writer_vprintf,
+    .get_buffer       = buf_writer_get_buffer,
+    .advance_buffer   = buf_writer_advance_buffer,
+};
+
+AVBufWriter *av_buf_writer_init(AVBufWriter *bwr, char *buf, size_t size)
+{
+    av_assert1(FIELDOK(bwr, pos));
+    bwr->self_size = sizeof(AVBufWriter);
+    bwr->buf       = buf;
+    bwr->size      = size;
+    bwr->pos       = 0;
+    buf[0] = 0;
+    return bwr;
+}
+
+AVWriter av_buf_writer_wrap(AVBufWriter *bwr)
+{
+    AVWriter r = { av_buf_writer_get_methods(), bwr };
+    av_assert1(FIELDOK(bwr, pos));
+    return r;
+}
+
+/***************************************************************************
+ * AVDynbufWriter - write to a dynamic buffer
+ ***************************************************************************/
+
+static void dynbuf_writer_write(AVWriter wr, const char *data, size_t size)
+{
+    AVDynbufWriter *dwr = wr.obj;
+    unsigned char *buf;
+    unsigned buf_size;
+    size_t copy;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    av_bprint_get_buffer(&dwr->bp, FFMIN(size, UINT_MAX - 5), &buf, &buf_size);
+    if (buf_size >= 1) {
+        copy = FFMIN(buf_size - 1, size);
+        memcpy(buf, data, copy);
+        buf[copy] = 0;
+    }
+    dwr->bp.len += size;
+}
+
+static void dynbuf_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+    AVDynbufWriter *dwr = wr.obj;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    av_vbprintf(&dwr->bp, fmt, va);
+}
+
+char *av_dynbuf_writer_get_buffer(AVWriter wr, size_t size, size_t *rsize)
+{
+    AVDynbufWriter *dwr = wr.obj;
+    unsigned char *buf;
+    unsigned isize;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    av_bprint_get_buffer(&dwr->bp, FFMIN(size, UINT_MAX - 5), &buf, &isize);
+    *rsize = isize ? isize - 1 : 0;
+    return buf;
+}
+
+void av_dynbuf_writer_advance_buffer(AVWriter wr, size_t size)
+{
+    AVDynbufWriter *dwr = wr.obj;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    dwr->bp.len += size;
+    dwr->bp.str[FFMIN(dwr->bp.len, dwr->bp.size - 1)] = 0;
+}
+
+AV_WRITER_DEFINE_METHODS(/*public*/, AVDynbufWriter, av_dynbuf_writer) {
+    .self_size        = sizeof(AVWriterMethods),
+    .name             = "AVDynbufWriter",
+    .write            = dynbuf_writer_write,
+    .vprintf          = dynbuf_writer_vprintf,
+    .get_buffer       = av_dynbuf_writer_get_buffer,
+    .advance_buffer   = av_dynbuf_writer_advance_buffer,
+};
+
+AVDynbufWriter *av_dynbuf_writer_init(AVDynbufWriter *dwr)
+{
+    av_assert1(FIELDOK(dwr, bp));
+    av_bprint_init(&dwr->bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+    return dwr;
+}
+
+AVWriter av_dynbuf_writer_wrap(AVDynbufWriter *dwr)
+{
+    AVWriter r = { av_dynbuf_writer_get_methods(), dwr };
+    av_assert1(FIELDOK(dwr, bp));
+    return r;
+}
+
+int av_dynbuf_writer_get_error(AVWriter wr, size_t *full_size)
+{
+    AVDynbufWriter *dwr = wr.obj;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    if (full_size)
+        *full_size = dwr->bp.len;
+    return av_bprint_is_complete(&dwr->bp) ? 0 : AVERROR(ENOMEM);
+}
+
+char *av_dynbuf_writer_get_data(AVWriter wr, size_t *size)
+{
+    AVDynbufWriter *dwr = wr.obj;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    if (size)
+        *size = dwr->bp.len;
+    return dwr->bp.str;
+}
+
+int av_dynbuf_writer_finalize(AVWriter wr, char **buf, size_t *size)
+{
+    AVDynbufWriter *dwr = wr.obj;
+
+    av_assert1(av_dynbuf_writer_check(wr));
+    av_assert1(FIELDOK(dwr, bp));
+    if (size)
+        *size = dwr->bp.len;
+    return av_bprint_finalize(&dwr->bp, buf);
+}
+
+/***************************************************************************
+ * AVLogWriter - write to av_log()
+ ***************************************************************************/
+
+static void log_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+    av_assert1(av_log_writer_check(wr));
+    av_vlog(wr.obj, AV_LOG_INFO, fmt, va);
+}
+
+AV_WRITER_DEFINE_METHODS(/*public*/, AVLogWriter, av_log_writer) {
+    .self_size        = sizeof(AVWriterMethods),
+    .name             = "AVLogWriter",
+    .vprintf          = log_writer_vprintf,
+};
+
+AVWriter av_log_writer(void *obj)
+{
+    AVWriter wr = {
+        .methods = av_log_writer_get_methods(),
+        .obj     = obj,
+    };
+    return wr;
+}
+
+void av_log_writer_log(AVWriter wr, int level, const char *fmt, ...)
+{
+    va_list va;
+
+    va_start(va, fmt);
+    if (av_log_writer_check(wr)) {
+        av_vlog(wr.obj, level, fmt, va);
+    } else {
+        av_writer_vprintf(wr, fmt, va);
+    }
+    va_end(va);
+}
diff --git a/libavutil/writer.h b/libavutil/writer.h
new file mode 100644
index 0000000000..0cf0bc9739
--- /dev/null
+++ b/libavutil/writer.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2019 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 <stddef.h>
+#include <stdarg.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.
+ *
+ * An AVWriter is meant to allow to build and return string (and blocks of
+ * binary data) efficiently between functions.
+ * For example, a function that serialize something into a string will
+ * actually write into an AVWriter.
+ *
+ * To emulate idiomatic practices of writing into a buffer, you can write:
+ *   char buf[4096];
+ *   AVWriter wr = av_buf_writer_array(buf);
+ * which is a macro equivalent (using modern C magic) to:
+ *   AVWriterBuf bwr;
+ *   av_buf_writer_init(&bwr, buf, sizeof(buf));
+ *   AVWriter wr = av_buf_writer_wrap(&bwr);
+ * so you can write into any already allocated memory.
+ * The string will be 0-terminated, so you can omit len.
+ *
+ * To avoid the problems of size limits, you should use a dynamic buffer.
+ * AVDynbufWriter provides an implementation that uses the stack for small
+ * strings and the heap for longer ones.
+ *
+ * AVWriter wr = av_dynbuf_writer();
+ * write_something(wr);
+ * if (!av_dynbuf_get_error(wr)) {
+ *     output(av_dynbuf_get_buffer(wr, &size));
+ * av_dynbuf_finish(wr, NULL, NULL);
+ *
+ * It is possible to create an AVWriter to av_log() and to implement new
+ * kinds of AVWriter.
+ *
+ * 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.
+ *
+ * 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.
+ * 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.
+ */
+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.
+ */
+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);
+
+/**
+ * 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. In both case they should return an error code.
+ */
+#define AV_WRITER_FALLBACK 0x10000
+
+/***************************************************************************/
+
+/**
+ * @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_dynbuf_writer AVDynbufWriter
+ *
+ * An implementation of AVWriter that writes to a dynamic buffer.
+ *
+ * The buffer is kept 0-terminated.
+ *
+ * @{
+ */
+
+/**
+ * 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 */
+    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.
+ */
+/* XXX do we need to expose size_init and max_size from AVBPrint? */
+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 the error status of the writer.
+ *
+ * AVERROR(ENOMEM) means that allocation was impossible and caused data to
+ * be discarded. If full_size is not NULL, it will be set to the size of the
+ * full_data, including the discarded part.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ */
+int av_dynbuf_writer_get_error(AVWriter wr, size_t *full_size);
+
+/**
+ * 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.
+ */
+char *av_dynbuf_writer_get_data(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.
+ *
+ * Undefined behavior if called with another type of AVWriter.
+ */
+int av_dynbuf_writer_finalize(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.
+ */
+void av_dynbuf_writer_advance_buffer(AVWriter wr, size_t size);
+
+/**
+ * @}
+ */
+
+/***************************************************************************/
+
+/**
+ * @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.
+ *
+ * There is no error report.
+ * Implementations must provide their own way of reporting errors.
+ */
+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);
+
+    /**
+     * 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);
+
+};
+
+/**
+ * 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 =
+
+#define FF_NEW_SZ(type) ((type){ .self_size = sizeof(type) })
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_WRITER_H */
