Signed-off-by: Nicolas George <geo...@nsup.org> --- libavutil/Makefile | 2 +- libavutil/writer.c | 443 +++++++++++++++++++++++++++++++++++++++++ libavutil/writer.h | 487 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 931 insertions(+), 1 deletion(-) create mode 100644 libavutil/writer.c create mode 100644 libavutil/writer.h
diff --git a/libavutil/Makefile b/libavutil/Makefile index 27bafe9e12..d92872e296 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -172,7 +172,7 @@ OBJS = adler32.o \ tx_int32.o \ video_enc_params.o \ film_grain_params.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..89159ceaa2 --- /dev/null +++ b/libavutil/writer.c @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2021 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) + +#define methods_assert_abi(methods) av_assert1(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_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) +{ + methods_assert_abi(wr.methods); + 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) +{ + 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_assert1(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_assert1(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_assert1(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_assert1(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_assert1(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_assert1(FIELDOK(dwr, bp)) + +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)); + 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; +} + +static void dynbuf_writer_vprintf(AVWriter wr, const char *fmt, va_list va) +{ + AVDynbufWriter *dwr = wr.obj; + + av_assert1(av_dynbuf_writer_check(wr)); + dynbuf_writer_assert_abi(dwr); + 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)); + 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_assert1(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; +} + +static int dynbuf_writer_get_error(AVWriter wr, int self_only) +{ + AVDynbufWriter *dwr = wr.obj; + + av_assert1(av_dynbuf_writer_check(wr)); + dynbuf_writer_assert_abi(dwr); + 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); + 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) +{ + av_assert1(!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_assert1(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_assert1(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_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); +} + +/*************************************************************************** + * AVStdioWriter - write to stdio + ***************************************************************************/ + +static void stdio_writer_vprintf(AVWriter wr, const char *fmt, va_list va) +{ + av_assert1(av_stdio_writer_check(wr)); + vfprintf(wr.obj, fmt, va); +} + +static int stdio_writer_get_error(AVWriter wr, int self_only) +{ + av_assert1(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; +} diff --git a/libavutil/writer.h b/libavutil/writer.h new file mode 100644 index 0000000000..661da381c2 --- /dev/null +++ b/libavutil/writer.h @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2021 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. + * + * 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_writer_get_error(wr)) { + * output(av_dynbuf_writer_get_data(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); + +/** + * 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. 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. + * + * The buffer is initially kept in the variable itself, it switches to heap + * allocation if it grows too large. In that case, av_writer_get_error() can + * return AVERROR(ENOMEM) if the allocation fails. + * + * @{ + */ + +/** + * An AVWriter object for pre-allocated memory buffers. + * + * Can be allocated on the stack. + * + * Should be inited with one of the utility functions. + */ +typedef struct AVDynbufWriter { + size_t self_size; /**< Size of the structure itself */ + 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 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 with a writer in error. + */ +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. + * + * 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. + * + * @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. + */ +void av_dynbuf_writer_advance_buffer(AVWriter wr, size_t size); + +/** + * @} + */ + +/***************************************************************************/ + +/** + * @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. + * + * 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); + + /** + * Get the error status of the writer + * If self_only is non-zero, return errors only on this writer and not on + * possible other writers that it got from the caller. Errors from + * writers created internally should always be checked. + */ + int (*get_error)(AVWriter wr, int self_only); + + /** + * Write chars directly. + */ + void (*write)(AVWriter wr, const char *buf, size_t size); + + /** + * Write a formatted string. + */ + void (*vprintf)(AVWriter wr, const char *fmt, va_list ap); + + /** + * Get a buffer to write data. + * If *size is returned < min_size, data may get discarded. + * A writer that implements this method must implement advance_buffer too. + * If vprintf is not implemented, av_write_printf() will use + * get_buffer() and vsnprintf(): it will need an extra char in the + * buffer for the 0 that vsnprintf() adds. + */ + char *(*get_buffer)(AVWriter wr, size_t min_size, size_t *size); + + /** + * Acknowledge chars written in a buffer obtained with get_buffer(). + * size is guaranteed <= the size value returned by get_buffer(). + */ + void (*advance_buffer)(AVWriter wr, size_t size); + + /** + * Flush immediately data that may be buffered in the writer. + */ + void (*flush)(AVWriter wr); + +}; + +/** + * Convenience macro for the boilerplate necessary to define a writer class. + * + * @arg qual type qualifier for for the symbols, for example "static" + * @arg type class name for the type of writer, + * for example "AVBufWriter" or "MyWriter" + * @arg prefix prefix for the symbols, + * for example "av_buf_writer" or "my_writer" + */ +#define AV_WRITER_DEFINE_METHODS(qual, type, prefix) \ +static const AVWriterMethods prefix##_methods; \ +qual const AVWriterMethods *prefix##_get_methods(void) { \ + return &prefix##_methods; \ +} \ +qual int prefix##_check(AVWriter wr) { \ + return wr.methods == prefix##_get_methods(); \ +} \ +static const AVWriterMethods prefix##_methods = + +/** + * @} + */ + +/** + * @} + */ + +#endif /* AVUTIL_WRITER_H */ -- 2.30.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".