diff mbox

[FFmpeg-devel,1/3] lavu: new AVWriter API.

Message ID 20191227132427.51398-1-george@nsup.org
State New
Headers show

Commit Message

Nicolas George Dec. 27, 2019, 1:24 p.m. UTC
Signed-off-by: Nicolas George <george@nsup.org>
---
 libavutil/Makefile |   3 +-
 libavutil/writer.c | 371 ++++++++++++++++++++++++++++++++++++++++
 libavutil/writer.h | 418 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 791 insertions(+), 1 deletion(-)
 create mode 100644 libavutil/writer.c
 create mode 100644 libavutil/writer.h


I intend to continue by converting avcodec_string() and the functions it
depends on to this API, to show that it makes things a bit simpler and more
convenient, but it is already looking good.

To see what it feels like to use this API, look at the last hunk of the
third patch.
diff mbox

Patch

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 */