From patchwork Wed Apr 21 12:27:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas George X-Patchwork-Id: 27202 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:5014:0:0:0:0:0 with SMTP id e20csp349691iob; Wed, 21 Apr 2021 05:27:48 -0700 (PDT) X-Google-Smtp-Source: ABdhPJz3+fpYUwF6PQfCW27d+kWG5nIg3tCz0dKaXPGB1OrqGFjhiJHeSmTkNmBMxv1ejvkdOOCB X-Received: by 2002:a05:6402:a4a:: with SMTP id bt10mr37907769edb.39.1619008068397; Wed, 21 Apr 2021 05:27:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1619008068; cv=none; d=google.com; s=arc-20160816; b=DyLe1iUfOuOGEhzoVbBwtl3ZXqBJxqFRk/bHr3uAzDBRl+hTlYzPS5JhoHfcBo5cRm CGAX81xFxo1An3HR4shMfnNU6yG/zLhc694DAAWsuII4xnMKMhveCvrCYnPVWKZ32AZi ju7juvtTnan9u3ZoJPx5UX0pKpGvggz4ASMCEhw/7H7JHMrYEiKBMqMhFIXoE40Wr2a0 FbyJQ+fKoUX5A+8KlDMGuGNirW4f2Io/zMkhj3euWRQkKZATLKaGJZgRCY+avJSa7Jl7 Wl+Rln2VW+LTj+SBZrjVy+7AJchJJAT5xWxJmf7gwbHhI8biJTGaXziSxqw3bLeZRGJD +30w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:delivered-to; bh=p+QrA2/RrC32XRxxLbzPWNFlxBQcTizsF9FIAGuRoMw=; b=TvVzWX8SJz2S5dWyzgruZLuvu23FEm/EOKVVqS8uVZGQR/lareccBpBwo89sOuFX9w MxHpRpewzwuU4ktueHCzhTG134gA47jIRLYl0Hvtha5O8y4lKr16WQ5o3fwgaG2KKLLP N4Gxl+O4D0UpqJb3hY0w8aIhVKbbI/aI/wwTQ3fthZeFUEhcgCgt29iolIQE4pSM3x50 54T7cOR/YV1MaA4xWbm50FyW3NMYl4A4fCTlPRsLZRj4Jh6GaFte7T0S9mNH3qiQyP0q 7tyipbGicOuMT6d0vGKyUsZR2eD3DRwEP9wJ4s1hWOiFFwlSrgxB84kZaokcnaBVxFrp KnIw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id f5si1815144edr.186.2021.04.21.05.27.47; Wed, 21 Apr 2021 05:27:48 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 6B44368996A; Wed, 21 Apr 2021 15:27:19 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from nef.ens.fr (nef2.ens.fr [129.199.96.40]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1BA8A6899A8 for ; Wed, 21 Apr 2021 15:27:12 +0300 (EEST) X-ENS-nef-client: 129.199.129.80 ( name = phare.normalesup.org ) Received: from phare.normalesup.org (phare.normalesup.org [129.199.129.80]) by nef.ens.fr (8.14.4/1.01.28121999) with ESMTP id 13LCR93U006279 for ; Wed, 21 Apr 2021 14:27:10 +0200 Received: by phare.normalesup.org (Postfix, from userid 1001) id 96E3CEB5BC; Wed, 21 Apr 2021 14:27:09 +0200 (CEST) From: Nicolas George To: ffmpeg-devel@ffmpeg.org Date: Wed, 21 Apr 2021 14:27:01 +0200 Message-Id: <20210421122706.9002-3-george@nsup.org> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210421122706.9002-1-george@nsup.org> References: <20210421122706.9002-1-george@nsup.org> MIME-Version: 1.0 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.4.3 (nef.ens.fr [129.199.96.32]); Wed, 21 Apr 2021 14:27:11 +0200 (CEST) Subject: [FFmpeg-devel] [PATCH 2/7] lavu: new AVWriter API. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: RunRb0xSOqmx Signed-off-by: Nicolas George --- 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 +#include +#include + +#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 */