new file mode 100644
@@ -0,0 +1,186 @@
+# Quick start guide for AVWriter
+
+AVWriter is an API to unify functions returning strings and to make building
+strings from parts easier. In this document, you will find an introduction
+on how to *use* AVWriter, mostly in the form of code snippets compating
+mainstream C solutions with their AVWriter counterpart.
+
+**Note:** AVWriter is 8-bit clean, the strings it manipulates can be buffers
+of binary data. The documentation is mostly written uing the vocabulary of
+strings for simplicity.
+
+In mainstream C, a function that needs to return a string usually have two
+options: either they accept pointer to a buffer that they fill or they
+allocate the buffer themselves and return it. Both these options have
+drawbacks, which one is best depends on the circumstances of the caller.
+
+AVWriter lets the caller choose the option best suited to the circumstances,
+among a small variety of built-in options or custom implementations,
+including on-the-fly compression or escaping and direct writing to a file.
+The first built-in implementation, where the strings is stored in a
+dynamically-allocated buffer, includes the optimization that small strings
+are kept on the stack.
+
+AVWriter also makes the work of the called function easier by providing
+convenient functions to append to the string that completely wrap error
+checks. Note that it only works for strings created as streams; functions
+that need random access to the string already built still need to manage
+their own buffers; some AVWriter implementations can still help for that.
+
+## 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.
+
+## I am writing the function that returns a string
+
+Old-style code:
+
+```
+int myfunction(char **buf, something arguments)
+{
+ *buf = malloc(enough room);
+```
+
+or:
+
+```
+int myfunction(char *buf, size_t buf_size, something arguments)
+{
+ …
+ if (buf_size < pos + len)
+ return -1;
+```
+
+Equivalent with AVWriter:
+
+```
+void myfunction(AVWriter wr, something arguments)
+{
+```
+
+… and write on the AVWriter, see the previous section.
+
+## I want to write directly to a file
+
+If the file is a stdio `FILE*`:
+
+```
+ av_something_write(av_stdio_writer(file), something, 0);
+```
+
+(Checking for error using `ferror()` is still your responsibility.)
+
+If the file is a libavformat `AVIOContext*`, the implementation is yet to
+come.
+
+## I want to write into something specific to my case
+
+- … into a Java/Perl/whatever string.
+- … into a GUI text widget.
+- … into a data structure provided by another library.
+- … to compress on the fly.
+- … to escape special characters on the fly.
+- … to hash the data on the fly and get the digest in the end.
+
+All these are possible with AVWriter. When doing something on the fly, it
+will usually involve writing the result into another AVWriter, like a
+filter.
@@ -182,7 +182,7 @@ OBJS = adler32.o \
version.o \
video_enc_params.o \
film_grain_params.o \
-
+ writer.o \
OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o
OBJS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.o
new file mode 100644
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2023 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 <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include "avassert.h"
+#include "error.h"
+#include "log.h"
+#include "writer.h"
+
+/***************************************************************************
+ * Generic API
+ ***************************************************************************/
+
+#define FIELDOK(st, f) ((char *)(&(st)->f + 1) <= (char *)(st) + (st)->self_size)
+
+#define methods_assert_abi(methods) av_assert0(FIELDOK(methods, flush))
+
+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_assert0(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)
+{
+ methods_assert_abi(wr.methods);
+ if (wr.methods->notify_impossible)
+ wr.methods->notify_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)
+{
+ methods_assert_abi(wr.methods);
+ 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)
+{
+ methods_assert_abi(wr.methods);
+ 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);
+ }
+}
+
+void av_writer_flush(AVWriter wr)
+{
+ methods_assert_abi(wr.methods);
+ if (wr.methods->flush)
+ wr.methods->flush(wr);
+}
+
+int av_writer_get_error(AVWriter wr, int self_only)
+{
+ methods_assert_abi(wr.methods);
+ if (wr.methods->get_error)
+ return wr.methods->get_error(wr, self_only);
+ return 0;
+}
+
+/***************************************************************************
+ * AVBufWriter - write to pre-allocated memory
+ ***************************************************************************/
+
+#define buf_writer_assert_abi(bwr) av_assert0(FIELDOK(bwr, pos))
+
+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_assert0(av_buf_writer_check(wr));
+ buf_writer_assert_abi(bwr);
+ 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_assert0(av_buf_writer_check(wr));
+ buf_writer_assert_abi(bwr);
+ 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_assert0(av_buf_writer_check(wr));
+ buf_writer_assert_abi(bwr);
+ *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_assert0(av_buf_writer_check(wr));
+ buf_writer_assert_abi(bwr);
+ 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)
+{
+ buf_writer_assert_abi(bwr);
+ 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 };
+ buf_writer_assert_abi(bwr);
+ return r;
+}
+
+/***************************************************************************
+ * AVDynbufWriter - write to a dynamic buffer
+ ***************************************************************************/
+
+#define dynbuf_writer_assert_abi(dwr) av_assert0(FIELDOK(dwr, bp))
+
+#define DWR_DIRTY 1
+
+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_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ 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;
+ dwr->flags |= DWR_DIRTY;
+}
+
+static void dynbuf_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+ AVDynbufWriter *dwr = wr.obj;
+
+ av_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ av_vbprintf(&dwr->bp, fmt, va);
+ dwr->flags |= DWR_DIRTY;
+}
+
+char *av_dynbuf_writer_get_buffer(AVWriter wr, size_t size, size_t *rsize)
+{
+ AVDynbufWriter *dwr = wr.obj;
+ unsigned char *buf;
+ unsigned isize;
+
+ av_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ 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_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ dwr->bp.len += size;
+ dwr->bp.str[FFMIN(dwr->bp.len, dwr->bp.size - 1)] = 0;
+ dwr->flags |= DWR_DIRTY;
+}
+
+static int dynbuf_writer_get_error(AVWriter wr, int self_only)
+{
+ AVDynbufWriter *dwr = wr.obj;
+
+ av_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ dwr->flags &= ~DWR_DIRTY;
+ return av_bprint_is_complete(&dwr->bp) ? 0 : AVERROR(ENOMEM);
+}
+
+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,
+ .get_error = dynbuf_writer_get_error,
+};
+
+AVDynbufWriter *av_dynbuf_writer_init(AVDynbufWriter *dwr)
+{
+ dynbuf_writer_assert_abi(dwr);
+ av_bprint_init(&dwr->bp, 0, AV_BPRINT_SIZE_UNLIMITED);
+ dwr->flags = 0;
+ return dwr;
+}
+
+AVWriter av_dynbuf_writer_wrap(AVDynbufWriter *dwr)
+{
+ AVWriter r = { av_dynbuf_writer_get_methods(), dwr };
+ dynbuf_writer_assert_abi(dwr);
+ return r;
+}
+
+char *av_dynbuf_writer_get_data(AVWriter wr, size_t *size)
+{
+ AVDynbufWriter *dwr = wr.obj;
+
+ dynbuf_writer_assert_abi(dwr);
+ av_assert0(!(dwr->flags & DWR_DIRTY));
+ av_assert0(!dynbuf_writer_get_error(wr, 0));
+ return av_dynbuf_writer_get_data_unsafe(wr, size);
+}
+
+char *av_dynbuf_writer_get_data_unsafe(AVWriter wr, size_t *size)
+{
+ AVDynbufWriter *dwr = wr.obj;
+
+ av_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ if (size)
+ *size = dwr->bp.len;
+ return dwr->bp.str;
+}
+
+int av_dynbuf_writer_finalize(AVWriter wr, char **buf, size_t *size)
+{
+ int ret;
+
+ ret = dynbuf_writer_get_error(wr, 0);
+ if (ret < 0) {
+ *buf = NULL;
+ *size = 0;
+ av_dynbuf_writer_finalize_unsafe(wr, NULL, NULL);
+ return ret;
+ } else {
+ return av_dynbuf_writer_finalize_unsafe(wr, buf, size);
+ }
+}
+
+int av_dynbuf_writer_finalize_unsafe(AVWriter wr, char **buf, size_t *size)
+{
+ AVDynbufWriter *dwr = wr.obj;
+
+ av_assert0(av_dynbuf_writer_check(wr));
+ dynbuf_writer_assert_abi(dwr);
+ 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_assert0(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);
+}
+
+/***************************************************************************
+ * AVStdioWriter - write to stdio
+ ***************************************************************************/
+
+static void stdio_writer_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+ av_assert0(av_stdio_writer_check(wr));
+ vfprintf(wr.obj, fmt, va);
+}
+
+static int stdio_writer_get_error(AVWriter wr, int self_only)
+{
+ av_assert0(av_stdio_writer_check(wr));
+ return AVERROR(ferror(wr.obj));
+}
+
+AV_WRITER_DEFINE_METHODS(/*public*/, AVStdioWriter, av_stdio_writer) {
+ .self_size = sizeof(AVWriterMethods),
+ .name = "AVStdioWriter",
+ .vprintf = stdio_writer_vprintf,
+ .get_error = stdio_writer_get_error,
+};
+
+AVWriter av_stdio_writer(FILE *out)
+{
+ AVWriter wr = {
+ .methods = av_stdio_writer_get_methods(),
+ .obj = out,
+ };
+ return wr;
+}
new file mode 100644
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2023 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 serializes 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 AVWriter data 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 if 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.
+ *
+ * These methods should only be called directly by the implementation of
+ * AVWriter. Their documentation is written from the point of view of
+ * implementing them.
+ *
+ * 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 (*notify_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 the returned *size is < 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 */
Signed-off-by: Nicolas George <george@nsup.org> --- doc/avwriter_intro.md | 186 ++++++++++++++++ libavutil/Makefile | 2 +- libavutil/writer.c | 458 +++++++++++++++++++++++++++++++++++++++ libavutil/writer.h | 488 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1133 insertions(+), 1 deletion(-) create mode 100644 doc/avwriter_intro.md create mode 100644 libavutil/writer.c create mode 100644 libavutil/writer.h