[FFmpeg-devel] [PATCH] lavu: header and documentation for AVWriter

Stefano Sabatini stefasab at gmail.com
Thu Sep 1 02:03:58 EEST 2022


On date Wednesday 2022-08-24 17:18:28 +0200, Nicolas George wrote:
> The actual implementation, tests and uses in the rest of
> FFmpeg code will be committed separately once the API is
> settled.
> 
> Signed-off-by: Nicolas George <george at nsup.org>
> ---
>  doc/avwriter_intro.md | 109 ++++++++++
>  libavutil/writer.h    | 484 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 593 insertions(+)
>  create mode 100644 doc/avwriter_intro.md
>  create mode 100644 libavutil/writer.h
> 
[...] 
> 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

serializeS

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

This "something" is vague and can be confused with the "something" in
"av_something_write", maybe something as:
when presented with invalid arguments, if the flag is not set the
called method 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

what variable? => in the AVWriter data?

> + * 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().

what is *rsize?

> + *
> + * Undefined behavior if called with another type of AVWriter.

> + * Undefined behavior is called not directly after

is called => if called

> + * 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))

av_buf_writer_from_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);

av_file_writer() ?

> +
> +/**
> + * @}
> + */
> +
> +/***************************************************************************/
> +
> +/**
> + * @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);

I'd use a verb for consistency/clarify.
notify_impossibility or notify_impossible_op?

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

If the returned *size is < min_size... ?

> +     * 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().

size is or should be guaranteed?

> +     */
> +    void (*advance_buffer)(AVWriter wr, size_t size);
> +
[...]


More information about the ffmpeg-devel mailing list