* [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
@ 2023-04-28 9:55 Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API Nicolas George
` (8 more replies)
0 siblings, 9 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Nicolas George <george@nsup.org>
---
libavutil/extendable.h | 59 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 libavutil/extendable.h
FFReservedPadding is used by the WIP JSON writer.
diff --git a/libavutil/extendable.h b/libavutil/extendable.h
new file mode 100644
index 0000000000..79980fa202
--- /dev/null
+++ b/libavutil/extendable.h
@@ -0,0 +1,59 @@
+/*
+ * 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_EXTENDABLE_H
+#define AVUTIL_EXTENDABLE_H
+
+/**
+ * @defgroup ff_extendable FFExtendable
+ *
+ * Types and macros to help designing structures that can be allocated by
+ * the application, including on the stack, but will not break ABI when
+ * extendded.
+ *
+ * This should not be used outside FFmpeg.
+ *
+ * @{
+ */
+
+/**
+ * Define a value of type as a compound literal (hidden local variable)
+ * with the field self_size filled.
+ */
+#define FF_NEW_SZ(type) ((type){ .self_size = sizeof(type) })
+
+/**
+ * Type suitable for paddign at the end of a structure, with maximum
+ * alignment.
+ */
+typedef union FFReservedPadding {
+ union {
+ double d;
+ void *p;
+ void (*f)(void);
+ intmax_t i;
+ } dummy;
+} FFReservedPadding;
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_EXTENDABLE_H */
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-28 10:37 ` Rodney Baker
2023-05-02 15:53 ` Rémi Denis-Courmont
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 3/8] lavu/writer: add test Nicolas George
` (7 subsequent siblings)
8 siblings, 2 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
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
diff --git a/doc/avwriter_intro.md b/doc/avwriter_intro.md
new file mode 100644
index 0000000000..0e092246a2
--- /dev/null
+++ b/doc/avwriter_intro.md
@@ -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.
diff --git a/libavutil/Makefile b/libavutil/Makefile
index dc9012f9a8..eb8de1dfbc 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -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
diff --git a/libavutil/writer.c b/libavutil/writer.c
new file mode 100644
index 0000000000..1d1cbd6525
--- /dev/null
+++ b/libavutil/writer.c
@@ -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;
+}
diff --git a/libavutil/writer.h b/libavutil/writer.h
new file mode 100644
index 0000000000..d11fa2f01e
--- /dev/null
+++ b/libavutil/writer.h
@@ -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 */
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 3/8] lavu/writer: add test
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 4/8] lavf/dump: use a writer Nicolas George
` (6 subsequent siblings)
8 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Nicolas George <george@nsup.org>
---
libavutil/Makefile | 1 +
libavutil/tests/.gitignore | 1 +
libavutil/tests/writer.c | 192 +++++++++++++++++++++++++++++++++++++
tests/fate/libavutil.mak | 4 +
tests/ref/fate/writer | 36 +++++++
5 files changed, 234 insertions(+)
create mode 100644 libavutil/tests/writer.c
create mode 100644 tests/ref/fate/writer
diff --git a/libavutil/Makefile b/libavutil/Makefile
index eb8de1dfbc..4526ec80ca 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -269,6 +269,7 @@ TESTPROGS = adler32 \
uuid \
xtea \
tea \
+ writer \
TESTPROGS-$(HAVE_THREADS) += cpu_init
TESTPROGS-$(HAVE_LZO1X_999_COMPRESS) += lzo
diff --git a/libavutil/tests/.gitignore b/libavutil/tests/.gitignore
index 87895912f5..7559e7a24c 100644
--- a/libavutil/tests/.gitignore
+++ b/libavutil/tests/.gitignore
@@ -51,3 +51,4 @@
/utf8
/uuid
/xtea
+/writer
diff --git a/libavutil/tests/writer.c b/libavutil/tests/writer.c
new file mode 100644
index 0000000000..fdb8c4d703
--- /dev/null
+++ b/libavutil/tests/writer.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2019 Nicolas George
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/error.h"
+#include "libavutil/log.h"
+#include "libavutil/writer.h"
+
+/* see 69bd73b3ff873abb43de9db062b04425de153643 */
+
+/* AVWriter with only get_buffer/advance_buffer */
+
+static char *getbuf_get_buffer(AVWriter wr, size_t min_size, size_t *size);
+static void getbuf_advance_buffer(AVWriter wr, size_t size);
+
+AV_WRITER_DEFINE_METHODS(static, Getbuf, getbuf) {
+ .self_size = sizeof(AVWriterMethods),
+ .name = "Getbuf",
+ .get_buffer = getbuf_get_buffer,
+ .advance_buffer = getbuf_advance_buffer,
+};
+
+static char *getbuf_get_buffer(AVWriter wr, size_t min_size, size_t *size)
+{
+ AVBufWriter *bwr = wr.obj;
+
+ av_assert0(getbuf_check(wr));
+ *size = bwr->size - 1 - bwr->pos;
+ return bwr->buf + bwr->pos;
+}
+
+static void getbuf_advance_buffer(AVWriter wr, size_t size)
+{
+ AVBufWriter *bwr = wr.obj;
+
+ bwr->pos += size;
+ bwr->buf[bwr->pos] = 0;
+}
+
+/* AVWriter with only write */
+
+static void write_write(AVWriter wr, const char *data, size_t size);
+
+AV_WRITER_DEFINE_METHODS(static, Write, write) {
+ .self_size = sizeof(AVWriterMethods),
+ .name = "Write",
+ .write = write_write,
+};
+
+static void write_write(AVWriter wr, const char *data, size_t size)
+{
+ AVBufWriter *bwr = wr.obj;
+
+ av_assert0(write_check(wr));
+ size = FFMIN(bwr->size - 1 - bwr->pos, size);
+ memcpy(bwr->buf + bwr->pos, data, size);
+ bwr->pos += size;
+ bwr->buf[bwr->pos] = 0;
+}
+
+/* AVWriter with only vprintf */
+
+static void vprintf_vprintf(AVWriter wr, const char *fmt, va_list va);
+
+AV_WRITER_DEFINE_METHODS(static, Vprintf, vprintf) {
+ .self_size = sizeof(AVWriterMethods),
+ .name = "Vprintf",
+ .vprintf = vprintf_vprintf,
+};
+
+static void vprintf_vprintf(AVWriter wr, const char *fmt, va_list va)
+{
+ AVBufWriter *bwr = wr.obj;
+ int ret;
+
+ av_assert0(vprintf_check(wr));
+ ret = vsnprintf(bwr->buf + bwr->pos, bwr->size - bwr->pos, fmt, va);
+ if (ret > 0)
+ bwr->pos += ret;
+}
+
+/* Tests */
+
+static void stress_writer(AVWriter wr)
+{
+ av_writer_add_chars(wr, '*', 72);
+ av_writer_add_chars(wr, '\n', 1);
+ av_writer_print(wr, "Stressing the writer\n");
+ av_writer_printf(wr, "Answer: %d\n", 42);
+ av_writer_printf(wr, "Question: %0600d * %0600d\n", 6, 9); /* > sizeof(AVBPrint) */
+ av_writer_add_chars(wr, '*', 72);
+ av_writer_add_chars(wr, '\n', 1);
+ av_writer_write(wr, "\0Bonus track!\n", 14);
+}
+
+static void test_buf_writer_small(void)
+{
+ char buf[1024];
+ AVWriter wr = av_buf_writer_array(buf);
+ stress_writer(wr);
+ printf("AVBufWriter/1024: [%s]\n", buf);
+}
+
+static void test_buf_writer_large(void)
+{
+ char buf[8192];
+ AVWriter wr = av_buf_writer_array(buf);
+ stress_writer(wr);
+ printf("AVDynbufWriter/8192: [%s]\n", buf);
+}
+
+static void test_dynbuf_writer(void)
+{
+ AVWriter wr = av_dynbuf_writer();
+ char *data;
+ size_t size, i;
+ int ret;
+
+ stress_writer(wr);
+ data = av_dynbuf_writer_get_buffer(wr, 100, &size);
+ ret = snprintf(data, size, "Some more?\n");
+ av_dynbuf_writer_advance_buffer(wr, ret);
+ ret = av_writer_get_error(wr, 0);
+ if (ret < 0) {
+ av_log(NULL, AV_LOG_ERROR, "DynbufWriter (%zd chars): %s\n", size, av_err2str(ret));
+ return;
+ }
+ data = av_dynbuf_writer_get_data(wr, &size);
+ for (i = 0; i < size; i++)
+ if (data[i] == 0)
+ data[i] = '@'; /* avoid confusing diff */
+ printf("AVDynbufWriter (%zd chars): [", size);
+ fwrite(data, 1, size, stdout);
+ printf("]\n");
+ av_dynbuf_writer_finalize(wr, NULL, NULL);
+}
+
+static void test_writer_limited(const char *name, const AVWriterMethods *methods)
+{
+ char buf[8192];
+ AVBufWriter bwr = { sizeof(AVBufWriter), buf, sizeof(buf), 0 };
+ AVWriter wr = { methods, &bwr };
+ stress_writer(wr);
+ printf("%s: [%s]\n", name, buf);
+}
+
+static void test_writer_get_buffer(void)
+{
+ test_writer_limited("get_buffer", getbuf_get_methods());
+}
+
+static void test_writer_write(void)
+{
+ test_writer_limited("write", write_get_methods());
+}
+
+static void test_writer_vprintf(void)
+{
+ test_writer_limited("vprintf", vprintf_get_methods());
+}
+
+int main(int argc, char **argv)
+{
+ if (1) test_buf_writer_small();
+ if (1) test_buf_writer_large();
+ if (1) test_dynbuf_writer();
+ if (1) test_writer_get_buffer();
+ if (1) test_writer_write();
+ if (1) test_writer_vprintf();
+ return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index 80153f4395..a9dcc9ec87 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -175,6 +175,10 @@ fate-uuid: libavutil/tests/uuid$(EXESUF)
fate-uuid: CMD = run libavutil/tests/uuid$(EXESUF)
fate-uuid: CMP = null
+FATE_LIBAVUTIL += fate-writer
+fate-writer: libavutil/tests/writer$(EXESUF)
+fate-writer: CMD = run libavutil/tests/writer$(EXESUF)
+
FATE_LIBAVUTIL += $(FATE_LIBAVUTIL-yes)
FATE-$(CONFIG_AVUTIL) += $(FATE_LIBAVUTIL)
fate-libavutil: $(FATE_LIBAVUTIL)
diff --git a/tests/ref/fate/writer b/tests/ref/fate/writer
new file mode 100644
index 0000000000..3068d83a4a
--- /dev/null
+++ b/tests/ref/fate/writer
@@ -0,0 +1,36 @@
+AVBufWriter/1024: [************************************************************************
+Stressing the writer
+Answer: 42
+Question: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 * 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]
+AVDynbufWriter/8192: [************************************************************************
+Stressing the writer
+Answer: 42
+Question: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 * 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+************************************************************************
+]
+AVDynbufWriter (1417 chars): [************************************************************************
+Stressing the writer
+Answer: 42
+Question: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 * 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+************************************************************************
+@Bonus track!
+Some more?
+]
+get_buffer: [************************************************************************
+Stressing the writer
+Answer: 42
+Question: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 * 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+************************************************************************
+]
+write: [************************************************************************
+Stressing the writer
+Answer: 42
+Question: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 * 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+************************************************************************
+]
+vprintf: [************************************************************************
+Stressing the writer
+Answer: 42
+Question: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006 * 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009
+************************************************************************
+]
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 4/8] lavf/dump: use a writer
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 3/8] lavu/writer: add test Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP) Nicolas George
` (5 subsequent siblings)
8 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Nicolas George <george@nsup.org>
---
libavformat/avformat.h | 21 +++
libavformat/dump.c | 318 +++++++++++++++++++++--------------------
2 files changed, 185 insertions(+), 154 deletions(-)
This is meant to be an example, I chose a piece of code that does a lot
of strings but does not change much (to avoid rebase conflicts).
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 1916aa2dc5..5302a34c0d 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -317,6 +317,7 @@
#include "libavutil/dict.h"
#include "libavutil/log.h"
+#include "libavutil/writer.h"
#include "avio.h"
#include "libavformat/version_major.h"
@@ -2638,6 +2639,26 @@ void av_dump_format(AVFormatContext *ic,
const char *url,
int is_output);
+/**
+ * Write detailed information about the input or output format, such as
+ * duration, bitrate, streams, container, programs, metadata, side data,
+ * codec and time base, to an AVWriter.
+ *
+ * @param wr the writer where the format should be written
+ * @param ic the context to analyze
+ * @param index index of the stream to dump information about
+ * @param url the URL to print, such as source or destination file
+ * @param flags formatting flags, see below
+ */
+void av_dump_format_to_writer(struct AVWriter wr,
+ AVFormatContext *ic, int index,
+ const char *url, unsigned flags);
+
+/**
+ * Flag to av_dump_format_to_writer():
+ * if set, the cotext is output; if unset, the context is input.
+ */
+#define AV_DUMP_FORMAT_IS_OUTPUT 1
#define AV_FRAME_FILENAME_FLAGS_MULTIPLE 1 ///< Allow multiple %d
diff --git a/libavformat/dump.c b/libavformat/dump.c
index d31e4c2ec6..96b364e337 100644
--- a/libavformat/dump.c
+++ b/libavformat/dump.c
@@ -34,6 +34,7 @@
#include "libavutil/spherical.h"
#include "libavutil/stereo3d.h"
#include "libavutil/timecode.h"
+#include "libavutil/writer.h"
#include "libavcodec/avcodec.h"
@@ -121,45 +122,45 @@ void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_paylo
}
-static void print_fps(double d, const char *postfix)
+static void print_fps(AVWriter wr, double d, const char *postfix)
{
uint64_t v = lrintf(d * 100);
if (!v)
- av_log(NULL, AV_LOG_INFO, "%1.4f %s", d, postfix);
+ av_writer_printf(wr, "%1.4f %s", d, postfix);
else if (v % 100)
- av_log(NULL, AV_LOG_INFO, "%3.2f %s", d, postfix);
+ av_writer_printf(wr, "%3.2f %s", d, postfix);
else if (v % (100 * 1000))
- av_log(NULL, AV_LOG_INFO, "%1.0f %s", d, postfix);
+ av_writer_printf(wr, "%1.0f %s", d, postfix);
else
- av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix);
+ av_writer_printf(wr, "%1.0fk %s", d / 1000, postfix);
}
-static void dump_metadata(void *ctx, const AVDictionary *m, const char *indent)
+static void dump_metadata(AVWriter wr, const AVDictionary *m, const char *indent)
{
if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) {
const AVDictionaryEntry *tag = NULL;
- av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent);
+ av_writer_printf(wr, "%sMetadata:\n", indent);
while ((tag = av_dict_iterate(m, tag)))
if (strcmp("language", tag->key)) {
const char *p = tag->value;
- av_log(ctx, AV_LOG_INFO,
+ av_writer_printf(wr,
"%s %-16s: ", indent, tag->key);
while (*p) {
size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
- av_log(ctx, AV_LOG_INFO, "%.*s", (int)(FFMIN(255, len)), p);
+ av_writer_printf(wr, "%.*s", (int)(FFMIN(255, len)), p);
p += len;
- if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " ");
- if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, "");
+ if (*p == 0xd) av_writer_printf(wr, " ");
+ if (*p == 0xa) av_writer_printf(wr, "\n%s %-16s: ", indent, "");
if (*p) p++;
}
- av_log(ctx, AV_LOG_INFO, "\n");
+ av_writer_printf(wr, "\n");
}
}
}
/* param change side data*/
-static void dump_paramchange(void *ctx, const AVPacketSideData *sd)
+static void dump_paramchange(AVWriter wr, const AVPacketSideData *sd)
{
int size = sd->size;
const uint8_t *data = sd->data;
@@ -184,7 +185,7 @@ FF_DISABLE_DEPRECATION_WARNINGS
channels = AV_RL32(data);
data += 4;
size -= 4;
- av_log(ctx, AV_LOG_INFO, "channel count %"PRIu32", ", channels);
+ av_writer_printf(wr, "channel count %"PRIu32", ", channels);
}
if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) {
if (size < 8)
@@ -192,7 +193,7 @@ FF_DISABLE_DEPRECATION_WARNINGS
layout = AV_RL64(data);
data += 8;
size -= 8;
- av_log(ctx, AV_LOG_INFO,
+ av_writer_printf(wr,
"channel layout: %s, ", av_get_channel_name(layout));
}
FF_ENABLE_DEPRECATION_WARNINGS
@@ -203,7 +204,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
sample_rate = AV_RL32(data);
data += 4;
size -= 4;
- av_log(ctx, AV_LOG_INFO, "sample_rate %"PRIu32", ", sample_rate);
+ av_writer_printf(wr, "sample_rate %"PRIu32", ", sample_rate);
}
if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) {
if (size < 8)
@@ -214,135 +215,135 @@ FF_ENABLE_DEPRECATION_WARNINGS
height = AV_RL32(data);
data += 4;
size -= 4;
- av_log(ctx, AV_LOG_INFO, "width %"PRIu32" height %"PRIu32, width, height);
+ av_writer_printf(wr, "width %"PRIu32" height %"PRIu32, width, height);
}
return;
fail:
- av_log(ctx, AV_LOG_ERROR, "unknown param\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "unknown param\n");
}
/* replaygain side data*/
-static void print_gain(void *ctx, const char *str, int32_t gain)
+static void print_gain(AVWriter wr, const char *str, int32_t gain)
{
- av_log(ctx, AV_LOG_INFO, "%s - ", str);
+ av_writer_printf(wr, "%s - ", str);
if (gain == INT32_MIN)
- av_log(ctx, AV_LOG_INFO, "unknown");
+ av_writer_printf(wr, "unknown");
else
- av_log(ctx, AV_LOG_INFO, "%f", gain / 100000.0f);
- av_log(ctx, AV_LOG_INFO, ", ");
+ av_writer_printf(wr, "%f", gain / 100000.0f);
+ av_writer_printf(wr, ", ");
}
-static void print_peak(void *ctx, const char *str, uint32_t peak)
+static void print_peak(AVWriter wr, const char *str, uint32_t peak)
{
- av_log(ctx, AV_LOG_INFO, "%s - ", str);
+ av_writer_printf(wr, "%s - ", str);
if (!peak)
- av_log(ctx, AV_LOG_INFO, "unknown");
+ av_writer_printf(wr, "unknown");
else
- av_log(ctx, AV_LOG_INFO, "%f", (float) peak / UINT32_MAX);
- av_log(ctx, AV_LOG_INFO, ", ");
+ av_writer_printf(wr, "%f", (float) peak / UINT32_MAX);
+ av_writer_printf(wr, ", ");
}
-static void dump_replaygain(void *ctx, const AVPacketSideData *sd)
+static void dump_replaygain(AVWriter wr, const AVPacketSideData *sd)
{
const AVReplayGain *rg;
if (sd->size < sizeof(*rg)) {
- av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "invalid data\n");
return;
}
rg = (const AVReplayGain *)sd->data;
- print_gain(ctx, "track gain", rg->track_gain);
- print_peak(ctx, "track peak", rg->track_peak);
- print_gain(ctx, "album gain", rg->album_gain);
- print_peak(ctx, "album peak", rg->album_peak);
+ print_gain(wr, "track gain", rg->track_gain);
+ print_peak(wr, "track peak", rg->track_peak);
+ print_gain(wr, "album gain", rg->album_gain);
+ print_peak(wr, "album peak", rg->album_peak);
}
-static void dump_stereo3d(void *ctx, const AVPacketSideData *sd)
+static void dump_stereo3d(AVWriter wr, const AVPacketSideData *sd)
{
const AVStereo3D *stereo;
if (sd->size < sizeof(*stereo)) {
- av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "invalid data\n");
return;
}
stereo = (const AVStereo3D *)sd->data;
- av_log(ctx, AV_LOG_INFO, "%s", av_stereo3d_type_name(stereo->type));
+ av_writer_printf(wr, "%s", av_stereo3d_type_name(stereo->type));
if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
- av_log(ctx, AV_LOG_INFO, " (inverted)");
+ av_writer_printf(wr, " (inverted)");
}
-static void dump_audioservicetype(void *ctx, const AVPacketSideData *sd)
+static void dump_audioservicetype(AVWriter wr, const AVPacketSideData *sd)
{
const enum AVAudioServiceType *ast = (const enum AVAudioServiceType *)sd->data;
if (sd->size < sizeof(*ast)) {
- av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "invalid data\n");
return;
}
switch (*ast) {
case AV_AUDIO_SERVICE_TYPE_MAIN:
- av_log(ctx, AV_LOG_INFO, "main");
+ av_writer_printf(wr, "main");
break;
case AV_AUDIO_SERVICE_TYPE_EFFECTS:
- av_log(ctx, AV_LOG_INFO, "effects");
+ av_writer_printf(wr, "effects");
break;
case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
- av_log(ctx, AV_LOG_INFO, "visually impaired");
+ av_writer_printf(wr, "visually impaired");
break;
case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
- av_log(ctx, AV_LOG_INFO, "hearing impaired");
+ av_writer_printf(wr, "hearing impaired");
break;
case AV_AUDIO_SERVICE_TYPE_DIALOGUE:
- av_log(ctx, AV_LOG_INFO, "dialogue");
+ av_writer_printf(wr, "dialogue");
break;
case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
- av_log(ctx, AV_LOG_INFO, "commentary");
+ av_writer_printf(wr, "commentary");
break;
case AV_AUDIO_SERVICE_TYPE_EMERGENCY:
- av_log(ctx, AV_LOG_INFO, "emergency");
+ av_writer_printf(wr, "emergency");
break;
case AV_AUDIO_SERVICE_TYPE_VOICE_OVER:
- av_log(ctx, AV_LOG_INFO, "voice over");
+ av_writer_printf(wr, "voice over");
break;
case AV_AUDIO_SERVICE_TYPE_KARAOKE:
- av_log(ctx, AV_LOG_INFO, "karaoke");
+ av_writer_printf(wr, "karaoke");
break;
default:
- av_log(ctx, AV_LOG_WARNING, "unknown");
+ av_log_writer_log(wr, AV_LOG_WARNING, "unknown");
break;
}
}
-static void dump_cpb(void *ctx, const AVPacketSideData *sd)
+static void dump_cpb(AVWriter wr, const AVPacketSideData *sd)
{
const AVCPBProperties *cpb = (const AVCPBProperties *)sd->data;
if (sd->size < sizeof(*cpb)) {
- av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "invalid data\n");
return;
}
- av_log(ctx, AV_LOG_INFO,
+ av_writer_printf(wr,
"bitrate max/min/avg: %"PRId64"/%"PRId64"/%"PRId64" buffer size: %"PRId64" ",
cpb->max_bitrate, cpb->min_bitrate, cpb->avg_bitrate,
cpb->buffer_size);
if (cpb->vbv_delay == UINT64_MAX)
- av_log(ctx, AV_LOG_INFO, "vbv_delay: N/A");
+ av_writer_printf(wr, "vbv_delay: N/A");
else
- av_log(ctx, AV_LOG_INFO, "vbv_delay: %"PRIu64"", cpb->vbv_delay);
+ av_writer_printf(wr, "vbv_delay: %"PRIu64"", cpb->vbv_delay);
}
-static void dump_mastering_display_metadata(void *ctx, const AVPacketSideData *sd)
+static void dump_mastering_display_metadata(AVWriter wr, const AVPacketSideData* sd)
{
const AVMasteringDisplayMetadata *metadata =
(const AVMasteringDisplayMetadata *)sd->data;
- av_log(ctx, AV_LOG_INFO, "Mastering Display Metadata, "
+ av_writer_printf(wr, "Mastering Display Metadata, "
"has_primaries:%d has_luminance:%d "
"r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
"min_luminance=%f, max_luminance=%f",
@@ -357,51 +358,51 @@ static void dump_mastering_display_metadata(void *ctx, const AVPacketSideData *s
av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance));
}
-static void dump_content_light_metadata(void *ctx, const AVPacketSideData *sd)
+static void dump_content_light_metadata(AVWriter wr, const AVPacketSideData* sd)
{
const AVContentLightMetadata *metadata =
(const AVContentLightMetadata *)sd->data;
- av_log(ctx, AV_LOG_INFO, "Content Light Level Metadata, "
+ av_writer_printf(wr, "Content Light Level Metadata, "
"MaxCLL=%d, MaxFALL=%d",
metadata->MaxCLL, metadata->MaxFALL);
}
-static void dump_spherical(void *ctx, const AVCodecParameters *par,
+static void dump_spherical(AVWriter wr, const AVCodecParameters *par,
const AVPacketSideData *sd)
{
const AVSphericalMapping *spherical = (const AVSphericalMapping *)sd->data;
double yaw, pitch, roll;
if (sd->size < sizeof(*spherical)) {
- av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "invalid data\n");
return;
}
- av_log(ctx, AV_LOG_INFO, "%s ", av_spherical_projection_name(spherical->projection));
+ av_writer_printf(wr, "%s ", av_spherical_projection_name(spherical->projection));
yaw = ((double)spherical->yaw) / (1 << 16);
pitch = ((double)spherical->pitch) / (1 << 16);
roll = ((double)spherical->roll) / (1 << 16);
- av_log(ctx, AV_LOG_INFO, "(%f/%f/%f) ", yaw, pitch, roll);
+ av_writer_printf(wr, "(%f/%f/%f) ", yaw, pitch, roll);
if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
size_t l, t, r, b;
av_spherical_tile_bounds(spherical, par->width, par->height,
&l, &t, &r, &b);
- av_log(ctx, AV_LOG_INFO,
+ av_writer_printf(wr,
"[%"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER"] ",
l, t, r, b);
} else if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
- av_log(ctx, AV_LOG_INFO, "[pad %"PRIu32"] ", spherical->padding);
+ av_writer_printf(wr, "[pad %"PRIu32"] ", spherical->padding);
}
}
-static void dump_dovi_conf(void *ctx, const AVPacketSideData *sd)
+static void dump_dovi_conf(AVWriter wr, const AVPacketSideData *sd)
{
const AVDOVIDecoderConfigurationRecord *dovi =
(const AVDOVIDecoderConfigurationRecord *)sd->data;
- av_log(ctx, AV_LOG_INFO, "version: %d.%d, profile: %d, level: %d, "
+ av_writer_printf(wr, "version: %d.%d, profile: %d, level: %d, "
"rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d",
dovi->dv_version_major, dovi->dv_version_minor,
dovi->dv_profile, dovi->dv_level,
@@ -411,104 +412,104 @@ static void dump_dovi_conf(void *ctx, const AVPacketSideData *sd)
dovi->dv_bl_signal_compatibility_id);
}
-static void dump_s12m_timecode(void *ctx, const AVStream *st, const AVPacketSideData *sd)
+static void dump_s12m_timecode(AVWriter wr, const AVStream *st, const AVPacketSideData *sd)
{
const uint32_t *tc = (const uint32_t *)sd->data;
if ((sd->size != sizeof(uint32_t) * 4) || (tc[0] > 3)) {
- av_log(ctx, AV_LOG_ERROR, "invalid data\n");
+ av_log_writer_log(wr, AV_LOG_ERROR, "invalid data\n");
return;
}
for (int j = 1; j <= tc[0]; j++) {
char tcbuf[AV_TIMECODE_STR_SIZE];
av_timecode_make_smpte_tc_string2(tcbuf, st->avg_frame_rate, tc[j], 0, 0);
- av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : "");
+ av_writer_printf(wr, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : "");
}
}
-static void dump_sidedata(void *ctx, const AVStream *st, const char *indent)
+static void dump_sidedata(AVWriter wr, const AVStream *st, const char *indent)
{
int i;
if (st->nb_side_data)
- av_log(ctx, AV_LOG_INFO, "%sSide data:\n", indent);
+ av_writer_printf(wr, "%sSide data:\n", indent);
for (i = 0; i < st->nb_side_data; i++) {
const AVPacketSideData *sd = &st->side_data[i];
- av_log(ctx, AV_LOG_INFO, "%s ", indent);
+ av_writer_printf(wr, "%s ", indent);
switch (sd->type) {
case AV_PKT_DATA_PALETTE:
- av_log(ctx, AV_LOG_INFO, "palette");
+ av_writer_printf(wr, "palette");
break;
case AV_PKT_DATA_NEW_EXTRADATA:
- av_log(ctx, AV_LOG_INFO, "new extradata");
+ av_writer_printf(wr, "new extradata");
break;
case AV_PKT_DATA_PARAM_CHANGE:
- av_log(ctx, AV_LOG_INFO, "paramchange: ");
- dump_paramchange(ctx, sd);
+ av_writer_printf(wr, "paramchange: ");
+ dump_paramchange(wr, sd);
break;
case AV_PKT_DATA_H263_MB_INFO:
- av_log(ctx, AV_LOG_INFO, "H.263 macroblock info");
+ av_writer_printf(wr, "H.263 macroblock info");
break;
case AV_PKT_DATA_REPLAYGAIN:
- av_log(ctx, AV_LOG_INFO, "replaygain: ");
- dump_replaygain(ctx, sd);
+ av_writer_printf(wr, "replaygain: ");
+ dump_replaygain(wr, sd);
break;
case AV_PKT_DATA_DISPLAYMATRIX:
- av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees",
+ av_writer_printf(wr, "displaymatrix: rotation of %.2f degrees",
av_display_rotation_get((const int32_t *)sd->data));
break;
case AV_PKT_DATA_STEREO3D:
- av_log(ctx, AV_LOG_INFO, "stereo3d: ");
- dump_stereo3d(ctx, sd);
+ av_writer_printf(wr, "stereo3d: ");
+ dump_stereo3d(wr, sd);
break;
case AV_PKT_DATA_AUDIO_SERVICE_TYPE:
- av_log(ctx, AV_LOG_INFO, "audio service type: ");
- dump_audioservicetype(ctx, sd);
+ av_writer_printf(wr, "audio service type: ");
+ dump_audioservicetype(wr, sd);
break;
case AV_PKT_DATA_QUALITY_STATS:
- av_log(ctx, AV_LOG_INFO, "quality factor: %"PRId32", pict_type: %c",
+ av_writer_printf(wr, "quality factor: %"PRId32", pict_type: %c",
AV_RL32(sd->data), av_get_picture_type_char(sd->data[4]));
break;
case AV_PKT_DATA_CPB_PROPERTIES:
- av_log(ctx, AV_LOG_INFO, "cpb: ");
- dump_cpb(ctx, sd);
+ av_writer_printf(wr, "cpb: ");
+ dump_cpb(wr, sd);
break;
case AV_PKT_DATA_MASTERING_DISPLAY_METADATA:
- dump_mastering_display_metadata(ctx, sd);
+ dump_mastering_display_metadata(wr, sd);
break;
case AV_PKT_DATA_SPHERICAL:
- av_log(ctx, AV_LOG_INFO, "spherical: ");
- dump_spherical(ctx, st->codecpar, sd);
+ av_writer_printf(wr, "spherical: ");
+ dump_spherical(wr, st->codecpar, sd);
break;
case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:
- dump_content_light_metadata(ctx, sd);
+ dump_content_light_metadata(wr, sd);
break;
case AV_PKT_DATA_ICC_PROFILE:
- av_log(ctx, AV_LOG_INFO, "ICC Profile");
+ av_writer_printf(wr, "ICC Profile");
break;
case AV_PKT_DATA_DOVI_CONF:
- av_log(ctx, AV_LOG_INFO, "DOVI configuration record: ");
- dump_dovi_conf(ctx, sd);
+ av_writer_printf(wr, "DOVI configuration record: ");
+ dump_dovi_conf(wr, sd);
break;
case AV_PKT_DATA_S12M_TIMECODE:
- av_log(ctx, AV_LOG_INFO, "SMPTE ST 12-1:2014: ");
- dump_s12m_timecode(ctx, st, sd);
+ av_writer_printf(wr, "SMPTE ST 12-1:2014: ");
+ dump_s12m_timecode(wr, st, sd);
break;
default:
- av_log(ctx, AV_LOG_INFO, "unknown side data type %d "
- "(%"SIZE_SPECIFIER" bytes)", sd->type, sd->size);
+ av_writer_printf(wr, "unknown side data type %d (%"SIZE_SPECIFIER" bytes)", sd->type, sd->size);
break;
}
- av_log(ctx, AV_LOG_INFO, "\n");
+ av_writer_printf(wr, "\n");
}
}
/* "user interface" functions */
-static void dump_stream_format(const AVFormatContext *ic, int i,
+static void dump_stream_format(AVWriter wr,
+ const AVFormatContext *ic, int i,
int index, int is_output)
{
char buf[256];
@@ -543,17 +544,17 @@ static void dump_stream_format(const AVFormatContext *ic, int i,
avcodec_string(buf, sizeof(buf), avctx, is_output);
avcodec_free_context(&avctx);
- av_log(NULL, AV_LOG_INFO, " Stream #%d:%d", index, i);
+ av_writer_printf(wr, " Stream #%d:%d", index, i);
/* the pid is an important information, so we display it */
/* XXX: add a generic system */
if (flags & AVFMT_SHOW_IDS)
- av_log(NULL, AV_LOG_INFO, "[0x%x]", st->id);
+ av_writer_printf(wr, "[0x%x]", st->id);
if (lang)
- av_log(NULL, AV_LOG_INFO, "(%s)", lang->value);
- av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d", sti->codec_info_nb_frames,
+ av_writer_printf(wr, "(%s)", lang->value);
+ av_log_writer_log(wr, AV_LOG_DEBUG, ", %d, %d/%d", sti->codec_info_nb_frames,
st->time_base.num, st->time_base.den);
- av_log(NULL, AV_LOG_INFO, ": %s", buf);
+ av_writer_printf(wr, ": %s", buf);
if (st->sample_aspect_ratio.num &&
av_cmp_q(st->sample_aspect_ratio, st->codecpar->sample_aspect_ratio)) {
@@ -562,7 +563,7 @@ static void dump_stream_format(const AVFormatContext *ic, int i,
st->codecpar->width * (int64_t)st->sample_aspect_ratio.num,
st->codecpar->height * (int64_t)st->sample_aspect_ratio.den,
1024 * 1024);
- av_log(NULL, AV_LOG_INFO, ", SAR %d:%d DAR %d:%d",
+ av_writer_printf(wr, ", SAR %d:%d DAR %d:%d",
st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
display_aspect_ratio.num, display_aspect_ratio.den);
}
@@ -573,76 +574,78 @@ static void dump_stream_format(const AVFormatContext *ic, int i,
int tbn = st->time_base.den && st->time_base.num;
if (fps || tbr || tbn)
- av_log(NULL, AV_LOG_INFO, "%s", separator);
+ av_writer_printf(wr, "%s", separator);
if (fps)
- print_fps(av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps");
+ print_fps(wr, av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps");
if (tbr)
- print_fps(av_q2d(st->r_frame_rate), tbn ? "tbr, " : "tbr");
+ print_fps(wr, av_q2d(st->r_frame_rate), tbn ? "tbr, " : "tbr");
if (tbn)
- print_fps(1 / av_q2d(st->time_base), "tbn");
+ print_fps(wr, 1 / av_q2d(st->time_base), "tbn");
}
if (st->disposition & AV_DISPOSITION_DEFAULT)
- av_log(NULL, AV_LOG_INFO, " (default)");
+ av_writer_printf(wr, " (default)");
if (st->disposition & AV_DISPOSITION_DUB)
- av_log(NULL, AV_LOG_INFO, " (dub)");
+ av_writer_printf(wr, " (dub)");
if (st->disposition & AV_DISPOSITION_ORIGINAL)
- av_log(NULL, AV_LOG_INFO, " (original)");
+ av_writer_printf(wr, " (original)");
if (st->disposition & AV_DISPOSITION_COMMENT)
- av_log(NULL, AV_LOG_INFO, " (comment)");
+ av_writer_printf(wr, " (comment)");
if (st->disposition & AV_DISPOSITION_LYRICS)
- av_log(NULL, AV_LOG_INFO, " (lyrics)");
+ av_writer_printf(wr, " (lyrics)");
if (st->disposition & AV_DISPOSITION_KARAOKE)
- av_log(NULL, AV_LOG_INFO, " (karaoke)");
+ av_writer_printf(wr, " (karaoke)");
if (st->disposition & AV_DISPOSITION_FORCED)
- av_log(NULL, AV_LOG_INFO, " (forced)");
+ av_writer_printf(wr, " (forced)");
if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
- av_log(NULL, AV_LOG_INFO, " (hearing impaired)");
+ av_writer_printf(wr, " (hearing impaired)");
if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
- av_log(NULL, AV_LOG_INFO, " (visual impaired)");
+ av_writer_printf(wr, " (visual impaired)");
if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS)
- av_log(NULL, AV_LOG_INFO, " (clean effects)");
+ av_writer_printf(wr, " (clean effects)");
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
- av_log(NULL, AV_LOG_INFO, " (attached pic)");
+ av_writer_printf(wr, " (attached pic)");
if (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS)
- av_log(NULL, AV_LOG_INFO, " (timed thumbnails)");
+ av_writer_printf(wr, " (timed thumbnails)");
if (st->disposition & AV_DISPOSITION_CAPTIONS)
- av_log(NULL, AV_LOG_INFO, " (captions)");
+ av_writer_printf(wr, " (captions)");
if (st->disposition & AV_DISPOSITION_DESCRIPTIONS)
- av_log(NULL, AV_LOG_INFO, " (descriptions)");
+ av_writer_printf(wr, " (descriptions)");
if (st->disposition & AV_DISPOSITION_METADATA)
- av_log(NULL, AV_LOG_INFO, " (metadata)");
+ av_writer_printf(wr, " (metadata)");
if (st->disposition & AV_DISPOSITION_DEPENDENT)
- av_log(NULL, AV_LOG_INFO, " (dependent)");
+ av_writer_printf(wr, " (dependent)");
if (st->disposition & AV_DISPOSITION_STILL_IMAGE)
- av_log(NULL, AV_LOG_INFO, " (still image)");
+ av_writer_printf(wr, " (still image)");
if (st->disposition & AV_DISPOSITION_NON_DIEGETIC)
- av_log(NULL, AV_LOG_INFO, " (non-diegetic)");
- av_log(NULL, AV_LOG_INFO, "\n");
+ av_writer_printf(wr, " (non-diegetic)");
+ av_writer_printf(wr, "\n");
- dump_metadata(NULL, st->metadata, " ");
+ dump_metadata(wr, st->metadata, " ");
- dump_sidedata(NULL, st, " ");
+ dump_sidedata(wr, st, " ");
}
-void av_dump_format(AVFormatContext *ic, int index,
- const char *url, int is_output)
+void av_dump_format_to_writer(AVWriter wr,
+ AVFormatContext *ic, int index,
+ const char *url, unsigned flags)
{
+ int is_output = flags & AV_DUMP_FORMAT_IS_OUTPUT;
int i;
uint8_t *printed = ic->nb_streams ? av_mallocz(ic->nb_streams) : NULL;
if (ic->nb_streams && !printed)
return;
- av_log(NULL, AV_LOG_INFO, "%s #%d, %s, %s '%s':\n",
+ av_writer_printf(wr, "%s #%d, %s, %s '%s':\n",
is_output ? "Output" : "Input",
index,
is_output ? ic->oformat->name : ic->iformat->name,
is_output ? "to" : "from", url);
- dump_metadata(NULL, ic->metadata, " ");
+ dump_metadata(wr, ic->metadata, " ");
if (!is_output) {
- av_log(NULL, AV_LOG_INFO, " Duration: ");
+ av_writer_printf(wr, " Duration: ");
if (ic->duration != AV_NOPTS_VALUE) {
int64_t hours, mins, secs, us;
int64_t duration = ic->duration + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0);
@@ -652,40 +655,40 @@ void av_dump_format(AVFormatContext *ic, int index,
secs %= 60;
hours = mins / 60;
mins %= 60;
- av_log(NULL, AV_LOG_INFO, "%02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"", hours, mins, secs,
+ av_writer_printf(wr, "%02"PRId64":%02"PRId64":%02"PRId64".%02"PRId64"", hours, mins, secs,
(100 * us) / AV_TIME_BASE);
} else {
- av_log(NULL, AV_LOG_INFO, "N/A");
+ av_writer_printf(wr, "N/A");
}
if (ic->start_time != AV_NOPTS_VALUE) {
int secs, us;
- av_log(NULL, AV_LOG_INFO, ", start: ");
+ av_writer_printf(wr, ", start: ");
secs = llabs(ic->start_time / AV_TIME_BASE);
us = llabs(ic->start_time % AV_TIME_BASE);
- av_log(NULL, AV_LOG_INFO, "%s%d.%06d",
+ av_writer_printf(wr, "%s%d.%06d",
ic->start_time >= 0 ? "" : "-",
secs,
(int) av_rescale(us, 1000000, AV_TIME_BASE));
}
- av_log(NULL, AV_LOG_INFO, ", bitrate: ");
+ av_writer_printf(wr, ", bitrate: ");
if (ic->bit_rate)
- av_log(NULL, AV_LOG_INFO, "%"PRId64" kb/s", ic->bit_rate / 1000);
+ av_writer_printf(wr, "%"PRId64" kb/s", ic->bit_rate / 1000);
else
- av_log(NULL, AV_LOG_INFO, "N/A");
- av_log(NULL, AV_LOG_INFO, "\n");
+ av_writer_printf(wr, "N/A");
+ av_writer_printf(wr, "\n");
}
if (ic->nb_chapters)
av_log(NULL, AV_LOG_INFO, " Chapters:\n");
for (i = 0; i < ic->nb_chapters; i++) {
const AVChapter *ch = ic->chapters[i];
- av_log(NULL, AV_LOG_INFO, " Chapter #%d:%d: ", index, i);
- av_log(NULL, AV_LOG_INFO,
+ av_writer_printf(wr, " Chapter #%d:%d: ", index, i);
+ av_writer_printf(wr,
"start %f, ", ch->start * av_q2d(ch->time_base));
- av_log(NULL, AV_LOG_INFO,
+ av_writer_printf(wr,
"end %f\n", ch->end * av_q2d(ch->time_base));
- dump_metadata(NULL, ch->metadata, " ");
+ dump_metadata(wr, ch->metadata, " ");
}
if (ic->nb_programs) {
@@ -694,23 +697,30 @@ void av_dump_format(AVFormatContext *ic, int index,
const AVProgram *program = ic->programs[j];
const AVDictionaryEntry *name = av_dict_get(program->metadata,
"name", NULL, 0);
- av_log(NULL, AV_LOG_INFO, " Program %d %s\n", program->id,
+ av_writer_printf(wr, " Program %d %s\n", program->id,
name ? name->value : "");
- dump_metadata(NULL, program->metadata, " ");
+ dump_metadata(wr, program->metadata, " ");
for (k = 0; k < program->nb_stream_indexes; k++) {
- dump_stream_format(ic, program->stream_index[k],
+ dump_stream_format(wr, ic, program->stream_index[k],
index, is_output);
printed[program->stream_index[k]] = 1;
}
total += program->nb_stream_indexes;
}
if (total < ic->nb_streams)
- av_log(NULL, AV_LOG_INFO, " No Program\n");
+ av_writer_printf(wr, " No Program\n");
}
for (i = 0; i < ic->nb_streams; i++)
if (!printed[i])
- dump_stream_format(ic, i, index, is_output);
+ dump_stream_format(wr, ic, i, index, is_output);
av_free(printed);
}
+
+void av_dump_format(AVFormatContext *ic, int index,
+ const char *url, int is_output)
+{
+ av_dump_format_to_writer(av_log_writer(NULL), ic, index, url,
+ is_output ? AV_DUMP_FORMAT_IS_OUTPUT : 0);
+}
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
` (2 preceding siblings ...)
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 4/8] lavf/dump: use a writer Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-29 9:11 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 6/8] lavu: add JSON writer test (WIP) Nicolas George
` (4 subsequent siblings)
8 siblings, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Nicolas George <george@nsup.org>
---
libavutil/Makefile | 1 +
libavutil/json.c | 368 +++++++++++++++++++++++++++++++++++
libavutil/json.h | 470 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 839 insertions(+)
create mode 100644 libavutil/json.c
create mode 100644 libavutil/json.h
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 4526ec80ca..a8a9700778 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -140,6 +140,7 @@ OBJS = adler32.o \
imgutils.o \
integer.o \
intmath.o \
+ json.o \
lfg.o \
lls.o \
log.o \
diff --git a/libavutil/json.c b/libavutil/json.c
new file mode 100644
index 0000000000..7ee68aaa4a
--- /dev/null
+++ b/libavutil/json.c
@@ -0,0 +1,368 @@
+/*
+ * 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 "common.h"
+#include "json.h"
+#include "opt.h"
+
+#define FIELDOK(st, f) ((char *)(&(st)->f + 1) <= (char *)(st) + (st)->self_size)
+
+#define json_assert_abi(jwr) av_assert1(FIELDOK(jwr, padding))
+#define json_escape_writer_assert_abi(jwr) av_assert1(FIELDOK(jwr, padding))
+
+AVClass *av_json_get_class(void)
+{
+ return NULL;
+}
+
+int av_json_alloc(AVJson **jc, unsigned max_depth)
+{
+ return AVERROR_BUG;
+}
+
+void av_json_free(AVJson **jc)
+{
+}
+
+AVJson *av_json_preinit(AVJson *jc, AVJsonEscapeWriter *jwr)
+{
+ json_assert_abi(jc);
+ json_escape_writer_assert_abi(jwr);
+ jc->av_class = av_json_get_class();
+ jc->escape_writer = jwr;
+ av_opt_set_defaults(jc);
+ return jc;
+}
+
+void av_json_init(AVJson *jc, AVWriter wr, unsigned flags, AVDictionary *options)
+{
+ jc->out = wr;
+ jc->flags = flags;
+ jc->in_string = 0;
+ jc->in_object = 0;
+ jc->first_element = 1;
+ jc->depth = 0;
+ jc->stack = NULL;
+}
+
+static inline int check_value_must_be_string(AVJson *jc, int string)
+{
+ return !jc->object_key || string;
+}
+
+static inline int check_stack_clean(AVJsonStack *stack)
+{
+ return !stack->prev;
+}
+
+static inline int check_begin_end_balanced(AVJson *jc, int obj)
+{
+ return jc->in_object == obj && jc->stack;
+}
+
+static inline int flag_pretty_print(AVJson *jc)
+{
+ return !!(jc->flags & AV_JSON_FLAG_PRETTY_PRINT);
+}
+
+static void end_value(AVJson *jc)
+{
+ if (!jc->stack && flag_pretty_print(jc))
+ av_writer_print(jc->out, "\n");
+}
+
+static void auto_end_string(AVJson *jc)
+{
+ if (jc->in_string) {
+ av_writer_print(jc->out, "\"");
+ jc->in_string = 0;
+ end_value(jc);
+ }
+}
+
+static void auto_add_separator(AVJson *jc)
+{
+ int indent = flag_pretty_print(jc);
+
+ auto_end_string(jc);
+ if (!jc->first_element) {
+ if (jc->object_key) {
+ av_writer_print(jc->out, flag_pretty_print(jc) ? " : " : ":");
+ indent = jc->object_key = 0;
+ } else {
+ av_writer_print(jc->out, ",");
+ jc->object_key = jc->in_object;
+ }
+ }
+ if (indent) {
+ if (jc->stack)
+ av_writer_print(jc->out, "\n");
+ av_writer_add_chars(jc->out, ' ', 3 * jc->depth);
+ }
+ jc->first_element = 0;
+}
+
+static void begin_value(AVJson *jc, int string)
+{
+ auto_add_separator(jc);
+ av_assert1(check_value_must_be_string(jc, string));
+}
+
+static void begin_compound(AVJson *jc, AVJsonStack *stack, unsigned obj)
+{
+ av_assert1(FIELDOK(stack, in_object));
+ av_assert1(check_stack_clean(stack));
+ stack->prev = jc->stack;
+ stack->in_object = jc->in_object;
+ jc->stack = stack;
+ jc->first_element = 1;
+ jc->in_object = jc->object_key = obj;
+ jc->depth++;
+}
+
+static void end_compound(AVJson *jc, unsigned obj)
+{
+ AVJsonStack *stack = jc->stack;
+
+ av_assert1(check_begin_end_balanced(jc, obj));
+ auto_end_string(jc);
+ jc->depth--;
+ if (!jc->first_element && flag_pretty_print(jc)) {
+ av_writer_print(jc->out, "\n");
+ av_writer_add_chars(jc->out, ' ', 3 * jc->depth);
+ }
+ jc->in_object = stack->in_object;
+ jc->object_key = 0;
+ jc->first_element = 0;
+ jc->stack = stack->prev;
+ stack->prev = NULL;
+}
+
+void av_json_begin_object_with_stack(AVJson *jc, AVJsonStack *stack)
+{
+ begin_value(jc, 0);
+ begin_compound(jc, stack, 1);
+ av_writer_print(jc->out, "{");
+}
+
+void av_json_end_object(AVJson *jc)
+{
+ end_compound(jc, 1);
+ av_writer_print(jc->out, "}");
+ end_value(jc);
+}
+
+void av_json_begin_array_with_stack(AVJson *jc, AVJsonStack *stack)
+{
+ begin_value(jc, 0);
+ begin_compound(jc, stack, 0);
+ av_writer_print(jc->out, "[");
+}
+
+void av_json_end_array(AVJson *jc)
+{
+ end_compound(jc, 0);
+ av_writer_print(jc->out, "]");
+ end_value(jc);
+}
+
+AVWriter av_json_begin_string(AVJson *jc)
+{
+ begin_value(jc, 1);
+ jc->in_string = 1;
+ av_writer_print(jc->out, "\"");
+ av_json_escape_writer_init(jc->escape_writer, jc->out, jc->flags);
+ return av_json_escape_writer_wrap(jc->escape_writer);
+}
+
+void av_json_end_string(AVJson *jc)
+{
+ av_assert1(jc->in_string);
+ auto_end_string(jc);
+}
+
+void av_json_add_string(AVJson *jc, const char *str)
+{
+ AVWriter wr = av_json_begin_string(jc);
+ av_writer_print(wr, str);
+}
+
+void av_json_add_string_vprintf(AVJson *jc, const char *fmt, va_list va)
+{
+ AVWriter wr = av_json_begin_string(jc);
+ av_writer_vprintf(wr, fmt, va);
+ av_json_end_string(jc);
+}
+
+void av_json_add_string_printf(AVJson *jc, const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ av_json_add_string_vprintf(jc, fmt, va);
+ va_end(va);
+}
+
+void av_json_add_int(AVJson *jc, intmax_t val)
+{
+ begin_value(jc, 0);
+ av_writer_printf(jc->out, "%jd", val);
+ end_value(jc);
+}
+
+void av_json_add_double(AVJson *jc, double val)
+{
+ begin_value(jc, 0);
+ av_writer_printf(jc->out, "%.15g", val);
+ end_value(jc);
+}
+
+void av_json_add_bool(AVJson *jc, int val)
+{
+ begin_value(jc, 0);
+ av_writer_print(jc->out, val ? "true" : "false");
+ end_value(jc);
+}
+
+void av_json_add_null(AVJson *jc)
+{
+ begin_value(jc, 0);
+ av_writer_print(jc->out, "null");
+ end_value(jc);
+}
+
+void av_json_add_raw_vprintf(AVJson *jc, const char *fmt, va_list va)
+{
+ begin_value(jc, 1);
+ av_writer_vprintf(jc->out, fmt, va);
+ end_value(jc);
+}
+
+void av_json_add_raw_printf(AVJson *jc, const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ av_json_add_raw_vprintf(jc, fmt, va);
+ va_end(va);
+}
+
+/***************************************************************************
+ * AVJsonEscapeWriter - escpae JSON strings
+ ***************************************************************************/
+
+static void json_escape_writer_write(AVWriter wr, const char *data, size_t size)
+{
+ AVJsonEscapeWriter *jwr = wr.obj;
+ const uint8_t *end = data + size;
+ const uint8_t *cur = data;
+ const uint8_t *written = data;
+ const uint8_t *raw = data;
+ unsigned char buf[13];
+ const char *escape;
+ int32_t c;
+ int ret;
+
+ av_assert1(av_json_escape_writer_check(wr));
+ json_escape_writer_assert_abi(jwr);
+ if (jwr->error)
+ return;
+ while (cur < end) {
+ raw = cur;
+ ret = av_utf8_decode(&c, &cur, end, 0);
+ if (ret < 0) {
+ if ((jwr->flags & AV_JSON_FLAG_BAD_ENCODING_EXPLODE)) {
+ av_log(NULL, AV_LOG_PANIC, "Bad UTF-8 in JSON\n");
+ abort();
+ }
+ if ((jwr->flags & AV_JSON_FLAG_BAD_ENCODING_REPLACE)) {
+ c = 0xFFFD;
+ } else {
+ jwr->error = ret;
+ return;
+ }
+ }
+ av_assert1(c >= 0 && c <= 0x10FFFF);
+ if ((unsigned)(c - ' ') < 127 - ' ' && c != '"' && c != '\\')
+ continue;
+ if ((unsigned)(c - 0x0080) <= (0xFFFF - 0x0080)) /* TODO flag */
+ continue;
+ if (c == '"') {
+ escape = "\\\"";
+ } else if (c == '\\') {
+ escape = "\\\\";
+ } else if (c == '\n') {
+ escape = "\\n";
+ } else if (c == '\r') {
+ escape = "\\r";
+ } else if (c == '\t') {
+ escape = "\\t";
+ } else if (c == '\f') {
+ escape = "\\f";
+ } else if (c == '\b') {
+ escape = "\\b";
+ } else {
+ if (c < 0x10000)
+ snprintf(buf, sizeof(buf), "\\u%04X", c);
+ else /* JSON sucks: UTF-16 */
+ snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
+ 0xD800 + (((c - 0x10000) >> 10) & 0x3FF),
+ 0xDC00 + (((c - 0x10000) >> 0) & 0x3FF));
+ escape = buf;
+ }
+ if (raw > written)
+ av_writer_write(jwr->owr, written, raw - written);
+ av_writer_print(jwr->owr, escape);
+ written = raw = cur;
+ }
+ if (cur > written)
+ av_writer_write(jwr->owr, written, cur - written);
+}
+
+static int json_escape_writer_get_error(AVWriter wr, int self_only)
+{
+ AVJsonEscapeWriter *jwr = wr.obj;
+
+ av_assert1(av_json_escape_writer_check(wr));
+ return jwr->error ? jwr->error :
+ self_only ? 0 : av_writer_get_error(jwr->owr, 0);
+}
+
+AV_WRITER_DEFINE_METHODS(/*public*/, AVJsonEscapeWriter, av_json_escape_writer) {
+ .self_size = sizeof(AVWriterMethods),
+ .name = "AVJsonEscapeWriter",
+ .write = json_escape_writer_write,
+ .get_error = json_escape_writer_get_error,
+};
+
+AVJsonEscapeWriter *av_json_escape_writer_init(AVJsonEscapeWriter *jwr, AVWriter owr, unsigned flags)
+{
+ json_escape_writer_assert_abi(jwr);
+ jwr->owr = owr;
+ jwr->flags = flags;
+ return jwr;
+}
+
+AVWriter av_json_escape_writer_wrap(AVJsonEscapeWriter *jwr)
+{
+ AVWriter r = { av_json_escape_writer_get_methods(), jwr };
+ json_escape_writer_assert_abi(jwr);
+ return r;
+}
+
diff --git a/libavutil/json.h b/libavutil/json.h
new file mode 100644
index 0000000000..6cdcdc1348
--- /dev/null
+++ b/libavutil/json.h
@@ -0,0 +1,470 @@
+/*
+ * 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_JSON_H
+#define AVUTIL_JSON_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "dict.h"
+#include "extendable.h"
+#include "log.h"
+#include "writer.h"
+
+/**
+ * @defgroup av_json AVJson
+ *
+ * API to serialize data to JSON.
+ *
+ * API summary and quick HOWTO:
+ *
+ * AVWriter out = av_dynbuf_writer();
+ * AVJson *jc = AV_JSON_DEFINE();
+ * av_json_init(jc, out, AV_JSON_FLAG_PRETTY_PRINT, NULL);
+ * av_json_begin_object(jc);
+ * av_json_add_string("user");
+ * av_json_add_string(user_name);
+ * av_json_add_string("score");
+ * av_json_add_int("%d", score);
+ * av_json_end_object(jc);
+ * av_json_finish(jc);
+ * ret = av_writer_get_error(out, 0);
+ * if (ret < 0) ...
+ * data = av_dynbuf_writer_get_data(wr, &size);
+ *
+ * @{
+ */
+
+typedef struct AVJson AVJson;
+typedef struct AVJsonStack AVJsonStack;
+typedef struct AVJsonEscapeWriter AVJsonEscapeWriter;
+typedef struct AVJsonValue AVJsonValue;
+typedef enum AVJsonValueType AVJsonValueType;
+
+/**
+ * Produce ASCII output by escaping non-ASCII characters
+ */
+#define AV_JSON_FLAG_ASCII 0x0001
+
+/**
+ * Abort if the input is not valid UTF-8.
+ */
+#define AV_JSON_FLAG_BAD_ENCODING_EXPLODE 0x0002
+
+/**
+ * If the input is not valid UTF-8, replace the offending partis with a
+ * replacement character.
+ */
+#define AV_JSON_FLAG_BAD_ENCODING_REPLACE 0x0004
+
+/**
+ * Pretty-print the output with spaces and indentation.
+ * XXX
+ */
+#define AV_JSON_FLAG_PRETTY_PRINT 0x0008
+
+/**
+ * Consider strings to be pseudo-binary instead of UTF-8.
+ *
+ * Warning: without AV_JSON_FLAG_ASCII, it will probably produce
+ * non-standard JSON.
+ * XXX
+ */
+#define AV_JSON_FLAG_PSEUDO_BINARY 0x0010
+
+/**
+ * Define a JSON context by allocating it as compound literals
+ * (hidden local variables).
+ * The context will be valid in the current scope and no further.
+ */
+#define AV_JSON_DEFINE(max_depth) \
+ av_json_preinit(&FF_NEW_SZ(AVJson), &FF_NEW_SZ(AVJsonEscapeWriter))
+
+/**
+ * Get the AVClass for a JSON context.
+ */
+AVClass *av_json_get_class(void);
+
+/**
+ * Pre-initialize a JSON context with its escape writer.
+ * @return jc itself
+ */
+AVJson *av_json_preinit(AVJson *jc, AVJsonEscapeWriter *jwr);
+
+/**
+ * Allocate a new JSON context.
+ * Only use this if AV_JSON_DEFINE() is not suitable.
+ * @return 0 or an AVERROR code, including AVERROR(ENOMEM).
+ */
+int av_json_alloc(AVJson **jc, unsigned max_depth);
+
+/**
+ * Free a JSON context allocated with av_json_alloc().
+ */
+void av_json_free(AVJson **jc);
+
+/**
+ * Initialize a JSON context with output, flags an options.
+ * This function can be called several times on the same context to reuse
+ * it.
+ */
+void av_json_init(AVJson *jc, AVWriter wr, unsigned flags, AVDictionary *options);
+
+/**
+ * Begin an object, i.e. a { key : value... } dictionary.
+ * After this, every other value must be a string.
+ * The behavior is undefined if a key is not a string.
+ * The stack object must be allocated and inited, and valid until the
+ * corresponding av_json_end_object().
+ * See av_json_begin_object() for a more convenient version.
+ */
+void av_json_begin_object_with_stack(AVJson *jc, AVJsonStack *stack);
+
+/**
+ * End an object.
+ */
+void av_json_end_object(AVJson *jc);
+
+/**
+ * Begin an array, i.e. a [ value, value... ] list.
+ * The stack object must be allocated and inited, and valid until the
+ * corresponding av_json_end_object().
+ * See av_json_begin_array() for a more convenient version.
+ */
+void av_json_begin_array_with_stack(AVJson *jc, AVJsonStack *stack);
+
+/**
+ * End an array.
+ */
+void av_json_end_array(AVJson *jc);
+
+/**
+ * Begin a string and a return an AVWriter to write its contents.
+ */
+AVWriter av_json_begin_string(AVJson *jc);
+
+/**
+ * End a string. Optional.
+ */
+void av_json_end_string(AVJson *jc);
+
+/**
+ * Add a string all at once.
+ */
+void av_json_add_string(AVJson *jc, const char *str);
+
+/**
+ * Add a string all at once from a format string and a va_list.
+ */
+void av_json_add_string_vprintf(AVJson *jc, const char *fmt, va_list va);
+
+/**
+ * Add a sring all at once from a format string and arguments.
+ */
+void av_json_add_string_printf(AVJson *jc, const char *fmt, ...) av_printf_format(2, 3);
+
+/**
+ * Add an integer number.
+ */
+void av_json_add_int(AVJson *jc, intmax_t val);
+
+/**
+ * Add a floating-point number.
+ */
+void av_json_add_double(AVJson *jc, double val);
+
+/**
+ * Add a boolean value (true/false).
+ */
+void av_json_add_bool(AVJson *jc, int val);
+
+/**
+ * Add a null value.
+ */
+void av_json_add_null(AVJson *jc);
+
+/**
+ * Add an arbitrary value from a format string and a va_list.
+ * Useful for adding a floating point number and controlling the format.
+ * Warning: the validity of the output cannot be guaranteed.
+ */
+void av_json_add_raw_vprintf(AVJson *jc, const char *fmt, va_list va);
+
+/**
+ * Add an arbitrary value from a format string and arguments.
+ * Useful for adding a floating point number and controlling the format.
+ * Warning: the validity of the output cannot be guaranteed.
+ */
+void av_json_add_raw_printf(AVJson *jc, const char *fmt, ...) av_printf_format(2, 3);
+
+/**
+ * Define and init a stack element as a compound literal
+ * (hidden local variable).
+ * Using this directly is not recommended.
+ */
+#define AV_JSON_STACK() (&FF_NEW_SZ(AVJsonStack))
+
+/**
+ * Allocate a stack element.
+ * Only use this if av_json_begin_object() / av_json_begin_array() cannot be
+ * used.
+ * @return 0 or an AVERROR code, including AVERROR(ENOMEM).
+ */
+int av_json_stack_alloc(AVJsonStack **stack);
+
+/**
+ * Free a stack element allocated with av_json_stack_alloc().
+ */
+void av_json_stack_free(AVJsonStack **stack);
+
+/**
+ * Begin an object with a stack element as a compound literal
+ * (hidden local variable).
+ * The corresponding av_json_end_object() must be in the same scope.
+ * After this, every other value must be a string.
+ * The behavior is undefined if a key is not a string.
+ */
+#define av_json_begin_object(jc) av_json_begin_object_with_stack((jc), AV_JSON_STACK())
+
+/**
+ * Begin an array with a stack element as a compound literal
+ * (hidden local variable).
+ * The corresponding av_json_end_array() must be in the same scope.
+ */
+#define av_json_begin_array(jc) av_json_begin_array_with_stack((jc), AV_JSON_STACK())
+
+/**
+ * An AVWriter object to escape JSON strings.
+ *
+ * Can be allocated on the stack.
+ *
+ * Should be inited with one of the utility functions.
+ */
+struct AVJsonEscapeWriter {
+ size_t self_size; /**< Size of the structure itself */
+ AVWriter owr; /**< AVWriter to send the output */
+ unsigned flags; /**< Escaping flags, see AV_JSON_FLAG_* */
+ int error; /**< Error status */
+ unsigned replacement_char; /**< Replacement character for bad UTF-8 */
+ FFReservedPadding padding[2];
+};
+
+/**
+ * Get the methods for a JSON escape writer.
+ * Probably not useful to use directly.
+ */
+const AVWriterMethods *av_json_escape_writer_get_methods(void);
+
+/**
+ * Check if a writer is a JSON escape writer.
+ */
+int av_json_escape_writer_check(AVWriter wr);
+
+/**
+ * Initialize an AVJsonEscapeWriter to an already-allocated memory buffer.
+ *
+ * @return jwr itself
+ * jwr->self_size must be set.
+ */
+AVJsonEscapeWriter *av_json_escape_writer_init(AVJsonEscapeWriter *jwr, AVWriter owr, unsigned flags);
+
+/**
+ * Create an AVWriter from an AVJsonEscapeWriter structure.
+ */
+AVWriter av_json_escape_writer_wrap(AVJsonEscapeWriter *jwr);
+
+/**
+ * Create an AVWriter to escape JSON strings.
+ *
+ * Note: as it relies on a compound statement, the AVJsonEscapeWriter object has
+ * a scope limited to the block where this macro is called.
+ */
+#define av_json_escape_writer(owr, flags) \
+ av_json_escape_writer_wrap(av_json_escape_writer_init(&FF_NEW_SZ(AVJsonEscapeWriter), (owr), (flags)))
+
+/**
+ * JSON encoding context.
+ *
+ * This structure must be allocated with AV_JSON_DEFINE() or equivalent
+ * code that will set av_class, self_size and escape_writer.
+ *
+ * It then must be initialized using av_json_init().
+ */
+struct AVJson {
+
+ /**
+ * Class; must be initialized to av_json_get_class().
+ */
+ const AVClass *av_class;
+
+ /**
+ * Size of the structure, must be initialized at allocation.
+ */
+ size_t self_size;
+
+ /**
+ * Encoding flags, see AV_JSON_FLAG_*.
+ */
+ unsigned flags;
+
+ /**
+ * Indentation shift.
+ */
+ unsigned indentation;
+
+ /**
+ * Output writer.
+ */
+ AVWriter out;
+
+ /****************************************************************
+ * The fields below this limit are private.
+ ****************************************************************/
+
+ /**
+ * Stack of states (object/array)
+ */
+ AVJsonStack *stack;
+
+ /**
+ * Pre-allocated writer for escaping strings.
+ *
+ * Must be allocated before init.
+ */
+ AVJsonEscapeWriter *escape_writer;
+
+ /**
+ * Depth of nested structures, for indentation.
+ */
+ unsigned depth;
+
+ /**
+ * True if a string is being constructed.
+ */
+ unsigned in_string : 1;
+
+ /**
+ * True if we currently are directly in an object.
+ */
+ unsigned in_object : 1;
+
+ /**
+ * True if we are about to write the first element of a structure.
+ */
+ unsigned first_element : 1;
+
+ /**
+ * True if we are about to write the key in an object.
+ */
+ unsigned object_key : 1;
+
+ FFReservedPadding padding[8];
+};
+
+/**
+ * Stack element for the JSON context.
+ */
+struct AVJsonStack {
+ size_t self_size;
+ AVJsonStack *prev;
+ unsigned short in_object;
+ FFReservedPadding padding;
+};
+
+/**
+ * Type of a JSON value.
+ *
+ * Note that JSON does not distinguish int and float values.
+ */
+enum AVJsonValueType {
+ AV_JSON_TYPE_NULL,
+ AV_JSON_TYPE_BOOL,
+ AV_JSON_TYPE_STRING,
+ AV_JSON_TYPE_INT,
+ AV_JSON_TYPE_DOUBLE,
+ AV_JSON_TYPE_OBJECT,
+ AV_JSON_TYPE_ARRAY,
+};
+
+/**
+ * Typed atomic JSON values.
+ * Objects and arrays cannot be represented.
+ * This structure is meant to be passed by value.
+ */
+struct AVJsonValue {
+ AVJsonValueType type;
+ union AVJsonValueValue {
+ intmax_t i;
+ double d;
+ const char *s;
+ } val;
+};
+
+/**
+ * Build an AVJsonValue for null.
+ */
+static inline AVJsonValue av_json_value_null(void)
+{
+ AVJsonValue ret = { .type = AV_JSON_TYPE_NULL };
+ return ret;
+}
+
+/**
+ * Build an AVJsonValue for a boolean.
+ */
+static inline AVJsonValue av_json_value_bool(int val)
+{
+ AVJsonValue ret = { .type = AV_JSON_TYPE_BOOL, .val.i = val };
+ return ret;
+}
+
+/**
+ * Build an AVJsonValue for a string.
+ * The pointer must stay valid while the value is used.
+ */
+static inline AVJsonValue av_json_value_string(const char *val)
+{
+ AVJsonValue ret = { .type = AV_JSON_TYPE_STRING, .val.s = val };
+ return ret;
+}
+
+/**
+ * Build an AVJsonValue for an integer.
+ */
+static inline AVJsonValue av_json_value_int(intmax_t val)
+{
+ AVJsonValue ret = { .type = AV_JSON_TYPE_INT, .val.i = val };
+ return ret;
+}
+
+/**
+ * Build an AVJsonValue for an integer.
+ */
+static inline AVJsonValue av_json_value_double(double val)
+{
+ AVJsonValue ret = { .type = AV_JSON_TYPE_INT, .val.d = val };
+ return ret;
+}
+
+/**
+ * @}
+ */
+
+#endif /* AVUTIL_JSON_H */
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 6/8] lavu: add JSON writer test (WIP)
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
` (3 preceding siblings ...)
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP) Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 7/8] lavf/options: add av_disposition_write() Nicolas George
` (3 subsequent siblings)
8 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Nicolas George <george@nsup.org>
---
libavutil/Makefile | 1 +
libavutil/tests/json.c | 139 +++++++++++++++++++++++++++++++++++++++
tests/fate/libavutil.mak | 4 ++
3 files changed, 144 insertions(+)
create mode 100644 libavutil/tests/json.c
diff --git a/libavutil/Makefile b/libavutil/Makefile
index a8a9700778..8800d25831 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -247,6 +247,7 @@ TESTPROGS = adler32 \
hwdevice \
integer \
imgutils \
+ json \
lfg \
lls \
log \
diff --git a/libavutil/tests/json.c b/libavutil/tests/json.c
new file mode 100644
index 0000000000..4e4106889e
--- /dev/null
+++ b/libavutil/tests/json.c
@@ -0,0 +1,139 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "libavutil/json.h"
+#include "libavutil/opt.h"
+
+static void print_success(AVWriter wr)
+{
+ printf(" (%s)\n", av_writer_get_error(wr, 0) ? "error" : "ok");
+}
+
+static void test_escape_writer(void)
+{
+ AVWriter out = av_stdio_writer(stdout);
+ AVWriter wr;
+
+ wr = av_json_escape_writer(out, 0);
+ av_writer_printf(wr, "Test of the \"JSON\" writer.\n"
+ "It will \aring.\n"
+ "c∈UBMP.\n"
+ "𝄞 in UTF-16");
+ print_success(wr);
+ wr = av_json_escape_writer(out, AV_JSON_FLAG_BAD_ENCODING_REPLACE);
+ av_writer_printf(wr, "Text\bbadly\222encoded");
+ print_success(wr);
+}
+
+static void test_json_context(void)
+{
+ AVJson *jc = AV_JSON_DEFINE(8);
+ AVWriter out = av_stdio_writer(stdout);
+
+ av_opt_set_int(jc, "indent", 2, 0);
+ av_json_init(jc, out, 0, NULL);
+ av_json_begin_object(jc);
+ av_json_add_string(jc, "filename");
+ av_json_add_string(jc, "example.nut");
+ av_json_add_string(jc, "streams");
+ av_json_begin_array(jc);
+ av_json_begin_object(jc);
+ av_json_add_string(jc, "type");
+ av_json_add_string(jc, "video");
+ av_json_end_object(jc);
+ av_json_add_string(jc, "null");
+ av_json_begin_object(jc);
+ av_json_add_string(jc, "type");
+ av_json_add_string(jc, "audio");
+ av_json_end_object(jc);
+ av_json_end_array(jc);
+ av_json_add_string(jc, "duration");
+ av_json_add_string(jc, "42");
+ av_json_end_object(jc);
+}
+
+static unsigned rnd_next(unsigned *rnd, unsigned bits)
+{
+ *rnd = (*rnd * 1664525 + 1013904223) & 0xFFFFFFFF;
+ return *rnd >> (32 - bits);
+}
+
+static void test_json_context_rec(AVJson *jc, unsigned max_depth, unsigned obj, unsigned *rnd)
+{
+ unsigned n, i, t, d;
+
+ n = rnd_next(rnd, 3);
+ for (i = 0; i < n; i++) {
+ if (obj)
+ av_json_add_string_printf(jc, "key %d/%d[%d]", i, n, max_depth);
+ t = rnd_next(rnd, max_depth > 0 ? 3 : 2);
+ switch (t) {
+ case 0:
+ av_json_add_string_printf(jc, "random string #%u", rnd_next(rnd, 32));
+ break;
+ case 1:
+ av_json_add_int(jc, rnd_next(rnd, 32));
+ break;
+ case 2:
+ d = rnd_next(rnd, 4) + 8;
+ av_json_add_double(jc, rnd_next(rnd, 32) / (double)(1 << d));
+ break;
+ case 3:
+ av_json_add_bool(jc, rnd_next(rnd, 1));
+ break;
+ case 4:
+ case 5:
+ av_json_begin_object(jc);
+ test_json_context_rec(jc, max_depth - 1, 1, rnd);
+ av_json_end_object(jc);
+ break;
+ case 6:
+ case 7:
+ av_json_begin_array(jc);
+ test_json_context_rec(jc, max_depth - 1, 0, rnd);
+ av_json_end_array(jc);
+ break;
+ }
+ }
+}
+
+static void test_json_context_deep(void)
+{
+ unsigned rnd = 0x12345678;
+ AVJson *jc = AV_JSON_DEFINE();
+ AVWriter out = av_stdio_writer(stdout);
+
+ av_opt_set_int(jc, "indent", 2, 0);
+ av_json_init(jc, out, AV_JSON_FLAG_PRETTY_PRINT, NULL);
+ av_json_begin_object(jc);
+ test_json_context_rec(jc, 5, 1, &rnd);
+ av_json_end_object(jc);
+}
+
+int main(int argc, char **argv)
+{
+ if (0) test_escape_writer();
+ if (0) test_json_context();
+ if (1) test_json_context_deep();
+ return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index a9dcc9ec87..7dfbf02527 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -104,6 +104,10 @@ fate-integer: libavutil/tests/integer$(EXESUF)
fate-integer: CMD = run libavutil/tests/integer$(EXESUF)
fate-integer: CMP = null
+# XXX FATE_LIBAVUTIL += fate-json
+fate-json: libavutil/tests/json$(EXESUF)
+fate-json: CMD = run libavutil/tests/json$(EXESUF)
+
FATE_LIBAVUTIL += fate-lfg
fate-lfg: libavutil/tests/lfg$(EXESUF)
fate-lfg: CMD = run libavutil/tests/lfg$(EXESUF)
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 7/8] lavf/options: add av_disposition_write()
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
` (4 preceding siblings ...)
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 6/8] lavu: add JSON writer test (WIP) Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 8/8] lavf/dump: use av_disposition_write() Nicolas George
` (2 subsequent siblings)
8 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
TODO APIchanges entry
Signed-off-by: Nicolas George <george@nsup.org>
---
libavformat/avformat.h | 14 ++++++++++++++
libavformat/options.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+)
This and the next patch show the kind of fruits become low-hanging
thanks to the platform of AVWriter.
Eventually, I want that every public structure and enumeration to have a
_write() function with a standardized API, and later we will be able to
use a custom %something format string in printf directly.
Another project of mine (that has AVWriter as a prerequisite) would
invert the logic of this function: instead of having the to-string and
from-string function explore the options table, we would have the
options system quety the to-string and from-string functions.
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 5302a34c0d..51fd78b71c 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -824,6 +824,20 @@ int av_disposition_from_string(const char *disp);
*/
const char *av_disposition_to_string(int disposition);
+/**
+ * Write a disposition as a string.
+ * The before and after string are written around each element of the
+ * disposition and the between string is written between them, if they are
+ * not NULL.
+ * Unknown disposition bits are written as an extra hexadecimal value.
+ * No flags are defined.
+ */
+void av_disposition_write(AVWriter wr, unsigned disposition,
+ const char *before,
+ const char *after,
+ const char *between,
+ unsigned flags);
+
/**
* Options for behavior on timestamp wrap detection.
*/
diff --git a/libavformat/options.c b/libavformat/options.c
index e4a3aceed0..a813e11060 100644
--- a/libavformat/options.c
+++ b/libavformat/options.c
@@ -29,6 +29,7 @@
#include "libavutil/internal.h"
#include "libavutil/intmath.h"
#include "libavutil/opt.h"
+#include "libavutil/writer.h"
/**
* @file
@@ -348,3 +349,36 @@ const char *av_disposition_to_string(int disposition)
return NULL;
}
+
+void av_disposition_write(AVWriter wr, unsigned disposition,
+ const char *before,
+ const char *after,
+ const char *between,
+ unsigned flags)
+{
+ int sep = 0;
+
+ for (const AVOption *opt = stream_options; opt->name; opt++) {
+ if (option_is_disposition(opt) && (disposition & opt->default_val.i64)) {
+ if (sep && between)
+ av_writer_print(wr, between);
+ if (before)
+ av_writer_print(wr, before);
+ av_writer_print(wr, opt->name);
+ if (after)
+ av_writer_print(wr, after);
+ disposition &= ~opt->default_val.i64;
+ sep = 1;
+ }
+ }
+ if (disposition) {
+ if (sep && between)
+ av_writer_print(wr, between);
+ if (before)
+ av_writer_print(wr, before);
+ av_writer_printf(wr, "%x?", disposition);
+ if (after)
+ av_writer_print(wr, after);
+ sep = 1;
+ }
+}
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* [FFmpeg-devel] [PATCH 8/8] lavf/dump: use av_disposition_write()
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
` (5 preceding siblings ...)
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 7/8] lavf/options: add av_disposition_write() Nicolas George
@ 2023-04-28 9:55 ` Nicolas George
2023-04-29 8:17 ` [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Anton Khirnov
2023-05-02 15:36 ` Rémi Denis-Courmont
8 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 9:55 UTC (permalink / raw)
To: ffmpeg-devel
It changes the output to have underscores instead of spaces.
Signed-off-by: Nicolas George <george@nsup.org>
---
libavformat/dump.c | 37 +------------------------------------
1 file changed, 1 insertion(+), 36 deletions(-)
Note: I consider the change to backspaces good: now we can copy-paste
from dump directly into a command-line.
diff --git a/libavformat/dump.c b/libavformat/dump.c
index 96b364e337..8b55e60b6a 100644
--- a/libavformat/dump.c
+++ b/libavformat/dump.c
@@ -584,42 +584,7 @@ static void dump_stream_format(AVWriter wr,
print_fps(wr, 1 / av_q2d(st->time_base), "tbn");
}
- if (st->disposition & AV_DISPOSITION_DEFAULT)
- av_writer_printf(wr, " (default)");
- if (st->disposition & AV_DISPOSITION_DUB)
- av_writer_printf(wr, " (dub)");
- if (st->disposition & AV_DISPOSITION_ORIGINAL)
- av_writer_printf(wr, " (original)");
- if (st->disposition & AV_DISPOSITION_COMMENT)
- av_writer_printf(wr, " (comment)");
- if (st->disposition & AV_DISPOSITION_LYRICS)
- av_writer_printf(wr, " (lyrics)");
- if (st->disposition & AV_DISPOSITION_KARAOKE)
- av_writer_printf(wr, " (karaoke)");
- if (st->disposition & AV_DISPOSITION_FORCED)
- av_writer_printf(wr, " (forced)");
- if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
- av_writer_printf(wr, " (hearing impaired)");
- if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
- av_writer_printf(wr, " (visual impaired)");
- if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS)
- av_writer_printf(wr, " (clean effects)");
- if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
- av_writer_printf(wr, " (attached pic)");
- if (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS)
- av_writer_printf(wr, " (timed thumbnails)");
- if (st->disposition & AV_DISPOSITION_CAPTIONS)
- av_writer_printf(wr, " (captions)");
- if (st->disposition & AV_DISPOSITION_DESCRIPTIONS)
- av_writer_printf(wr, " (descriptions)");
- if (st->disposition & AV_DISPOSITION_METADATA)
- av_writer_printf(wr, " (metadata)");
- if (st->disposition & AV_DISPOSITION_DEPENDENT)
- av_writer_printf(wr, " (dependent)");
- if (st->disposition & AV_DISPOSITION_STILL_IMAGE)
- av_writer_printf(wr, " (still image)");
- if (st->disposition & AV_DISPOSITION_NON_DIEGETIC)
- av_writer_printf(wr, " (non-diegetic)");
+ av_disposition_write(wr, st->disposition, " (", ")", NULL, 0);
av_writer_printf(wr, "\n");
dump_metadata(wr, st->metadata, " ");
--
2.39.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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API Nicolas George
@ 2023-04-28 10:37 ` Rodney Baker
2023-04-28 11:20 ` Nicolas George
2023-05-02 15:53 ` Rémi Denis-Courmont
1 sibling, 1 reply; 38+ messages in thread
From: Rodney Baker @ 2023-04-28 10:37 UTC (permalink / raw)
To: ffmpeg-devel
I'm not normally a reviewer, but I noticed a few minor grammatical things that
stood out - hope this is OK.
Regards,
Rodney.
On Friday, 28 April 2023 7:25:02 PM ACST Nicolas George wrote:
> 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
>
> diff --git a/doc/avwriter_intro.md b/doc/avwriter_intro.md
> new file mode 100644
> index 0000000000..0e092246a2
> --- /dev/null
> +++ b/doc/avwriter_intro.md
> @@ -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.
Nit - s/compating/comparing/
[...]
> +
> +**Note:** AVWriter is 8-bit clean, the strings it manipulates can be
Use a hyphen or a semicolon rather than a comma after "clean".
> 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.
Semicolon instead of comma after "drawbacks".
> +
> +AVWriter lets the caller choose the option best suited to the
> circumstances, +among a small variety of built-in options or custom
Drop comma after "circumstances".
> 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.
Full stop after "buffers" (instead of semicolon - you've already used one
previously in the same sentence).
[...]
--
==================================================================================================================
Rodney Baker
rodney.baker@iinet.net.au
==================================================================================================================
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-04-28 10:37 ` Rodney Baker
@ 2023-04-28 11:20 ` Nicolas George
0 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-28 11:20 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1679 bytes --]
Rodney Baker (12023-04-28):
> I'm not normally a reviewer, but I noticed a few minor grammatical things that
> stood out - hope this is OK.
Thanks, it is absolutely useful.
> Nit - s/compating/comparing/
Fixed.
> > +**Note:** AVWriter is 8-bit clean, the strings it manipulates can be
> Use a hyphen or a semicolon rather than a comma after "clean".
After consideration, a semicolon would be too strong; a hyphen would be
strange, too literary.
> > +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.
> Semicolon instead of comma after "drawbacks".
Same here, I find a semicolon would be too strong.
> Drop comma after "circumstances".
Done.
> > +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.
> Full stop after "buffers" (instead of semicolon - you've already used one
> previously in the same sentence).
Disagree on this one: the last part should still be in the sentence that
starts with “Note that”. There is no problem in having multiple
semicolons to separate parts on the same level.
Regards,
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
` (6 preceding siblings ...)
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 8/8] lavf/dump: use av_disposition_write() Nicolas George
@ 2023-04-29 8:17 ` Anton Khirnov
2023-04-29 15:11 ` Derek Buitenhuis
2023-05-02 15:36 ` Rémi Denis-Courmont
8 siblings, 1 reply; 38+ messages in thread
From: Anton Khirnov @ 2023-04-29 8:17 UTC (permalink / raw)
To: FFmpeg development discussions and patches
I've said this before, repeating it again for ease of reference: I do
not believe the use case for this has been sufficiently established.
What important problem within the scope of the project is being solved
by this? Why do we need over a 1000 lines of just new header files for
it? Why do we need a generic JSON writer? We are not a JSON library.
Neither are we a string processing library.
IMO this should not go in until and unless significant practical
benefits of this code are shown on a non-toy use case within the scope
of our project.
--
Anton Khirnov
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP) Nicolas George
@ 2023-04-29 9:11 ` Nicolas George
2023-04-29 9:41 ` Anton Khirnov
0 siblings, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-04-29 9:11 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 2172 bytes --]
Nicolas George (12023-04-28):
> Signed-off-by: Nicolas George <george@nsup.org>
> ---
> libavutil/Makefile | 1 +
> libavutil/json.c | 368 +++++++++++++++++++++++++++++++++++
> libavutil/json.h | 470 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 839 insertions(+)
> create mode 100644 libavutil/json.c
> create mode 100644 libavutil/json.h
I forgot to write: I wrote this code not only because we have half-baked
JSON output in multiple places in the code, but also to show the
kind of API AVWriter makes possible.
Typical JSON APIs would have a function to write a string value or an
object key, requiring the caller to build the string beforehand. Of
course, this API can do that:
> +void av_json_add_string(AVJson *jc, const char *str);
including with a format string, which is less usual:
> +void av_json_add_string_printf(AVJson *jc, const char *fmt, ...) av_printf_format(2, 3);
But these are just wrappers for convenience over the real API:
> +AVWriter av_json_begin_string(AVJson *jc);
It starts a string, i.e. outputs a quote, and then we get a writer to
write into that string. It will be automatically escaped, no
intermediate buffer will be used, and all the functions of the writer
API are available, including all the av_something_write() to come to
serialize our various types.
Also note that this API as a whole can produce small JSON outputs
without any dynamic allocation, making it suitable for once-per-frame
calls, but is not limited to that.
Do we *need* that: of course not, we still put “len += snprintf()” and
“av_realloc()” and error checks all over the place.
Now, while people maybe look at the code, there are a few things I can
work on, and I wonder which one you would like to see first:
- Finishing this JSON API.
- A XML API: that would be useful for dashenc, movenc, ttmlenc, ffprobe
and possibly others.
- Add serialization functions for our various types, like I did for
av_disposition_write().
- Go forward with the others API enhancements that I promised and that
depend on AVWriter.
Regards,
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 9:11 ` Nicolas George
@ 2023-04-29 9:41 ` Anton Khirnov
2023-04-29 14:06 ` James Almer
` (2 more replies)
0 siblings, 3 replies; 38+ messages in thread
From: Anton Khirnov @ 2023-04-29 9:41 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Quoting Nicolas George (2023-04-29 11:11:59)
> Nicolas George (12023-04-28):
> > Signed-off-by: Nicolas George <george@nsup.org>
> > ---
> > libavutil/Makefile | 1 +
> > libavutil/json.c | 368 +++++++++++++++++++++++++++++++++++
> > libavutil/json.h | 470 +++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 839 insertions(+)
> > create mode 100644 libavutil/json.c
> > create mode 100644 libavutil/json.h
>
> I forgot to write: I wrote this code not only because we have half-baked
> JSON output in multiple places in the code
As far as I can see, there are exactly two places in the codebase that
produce JSON: af_loudnorm and ffprobe.
af_loudnorm:
* can optionally produce final filter stats as JSON output with av_log()
* the relevant code has ~25 lines and is unlikely to be simplified by
this
* IMO the filter should not be doing this at all and instead produce some
sort of a struct and let the users process it as they wish
ffprobe:
* is not one of the libraries, but rather their caller
* we are not in business of providing random non-multimedia-related
services to callers, unless they are useful in our libraries;
if this code is only useful in ffprobe then it should live in fftools/
* it is not at all obvious that switching ffprobe to this code would
be an improvement; a patch actually demonstrating this would be most
useful
--
Anton Khirnov
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 9:41 ` Anton Khirnov
@ 2023-04-29 14:06 ` James Almer
2023-04-29 17:17 ` Nicolas George
2023-04-29 15:06 ` Derek Buitenhuis
2023-04-29 17:11 ` Nicolas George
2 siblings, 1 reply; 38+ messages in thread
From: James Almer @ 2023-04-29 14:06 UTC (permalink / raw)
To: ffmpeg-devel
On 4/29/2023 6:41 AM, Anton Khirnov wrote:
> Quoting Nicolas George (2023-04-29 11:11:59)
>> Nicolas George (12023-04-28):
>>> Signed-off-by: Nicolas George <george@nsup.org>
>>> ---
>>> libavutil/Makefile | 1 +
>>> libavutil/json.c | 368 +++++++++++++++++++++++++++++++++++
>>> libavutil/json.h | 470 +++++++++++++++++++++++++++++++++++++++++++++
>>> 3 files changed, 839 insertions(+)
>>> create mode 100644 libavutil/json.c
>>> create mode 100644 libavutil/json.h
>>
>> I forgot to write: I wrote this code not only because we have half-baked
>> JSON output in multiple places in the code
>
> As far as I can see, there are exactly two places in the codebase that
> produce JSON: af_loudnorm and ffprobe.
>
> af_loudnorm:
> * can optionally produce final filter stats as JSON output with av_log()
> * the relevant code has ~25 lines and is unlikely to be simplified by
> this
> * IMO the filter should not be doing this at all and instead produce some
> sort of a struct and let the users process it as they wish
It should be exporting frame metadata, like aphasemeter, cropdetect, etc.
av_log() output is not meant to be parseable. It's why the relevant
output of vf_showinfo can be freely changed, whereas output from things
like framecrc/framehash muxers is standardized.
>
> ffprobe:
> * is not one of the libraries, but rather their caller
> * we are not in business of providing random non-multimedia-related
> services to callers, unless they are useful in our libraries;
> if this code is only useful in ffprobe then it should live in fftools/
> * it is not at all obvious that switching ffprobe to this code would
> be an improvement; a patch actually demonstrating this would be most
> useful
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 9:41 ` Anton Khirnov
2023-04-29 14:06 ` James Almer
@ 2023-04-29 15:06 ` Derek Buitenhuis
2023-04-30 0:29 ` Kieran Kunhya
2023-04-29 17:11 ` Nicolas George
2 siblings, 1 reply; 38+ messages in thread
From: Derek Buitenhuis @ 2023-04-29 15:06 UTC (permalink / raw)
To: ffmpeg-devel
On 4/29/2023 10:41 AM, Anton Khirnov wrote:
> ffprobe:
> * is not one of the libraries, but rather their caller
> * we are not in business of providing random non-multimedia-related
> services to callers, unless they are useful in our libraries;
> if this code is only useful in ffprobe then it should live in fftools/
> * it is not at all obvious that switching ffprobe to this code would
> be an improvement; a patch actually demonstrating this would be most
> useful
+1
- Derek
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
2023-04-29 8:17 ` [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Anton Khirnov
@ 2023-04-29 15:11 ` Derek Buitenhuis
0 siblings, 0 replies; 38+ messages in thread
From: Derek Buitenhuis @ 2023-04-29 15:11 UTC (permalink / raw)
To: ffmpeg-devel
On 4/29/2023 9:17 AM, Anton Khirnov wrote:
> What important problem within the scope of the project is being solved
> by this? Why do we need over a 1000 lines of just new header files for
> it? Why do we need a generic JSON writer? We are not a JSON library.
> Neither are we a string processing library.
I agree, I don't think it has a place in a set of multimedia libraries.
If I wanted this functionality there are many other good libaries I could
use that also don't pull in a multimeia framework.
> IMO this should not go in until and unless significant practical
> benefits of this code are shown on a non-toy use case within the scope
> of our project.
Having watched this from afar for a while it seems like a classic FOSS
problem:
One person may have infinite energy to argue ther point / idea until
nobody can be bothered to reply anymore since it is so draining.
Happy Coronation,
- Derek
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 9:41 ` Anton Khirnov
2023-04-29 14:06 ` James Almer
2023-04-29 15:06 ` Derek Buitenhuis
@ 2023-04-29 17:11 ` Nicolas George
2023-04-29 18:27 ` Anton Khirnov
2 siblings, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-04-29 17:11 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1746 bytes --]
Anton Khirnov (12023-04-29):
> As far as I can see, there are exactly two places in the codebase that
> produce JSON: af_loudnorm and ffprobe.
I think I remember finding a few other places, but it does not matter
much.
Users have been asking¹ for parse-friendly output from filters for years,
and JSON is often the format they prefer. Therefore, all the filters
that log useful information are candidates where a JSON writing API is
useful.
(See my answer to James for why “put it in frame metadata” is not a real
solution.)
> * IMO the filter should not be doing this at all and instead produce some
> sort of a struct and let the users process it as they wish
And to let users process it as they wish, it needs to be formatted into
a parse-friendly syntax like JSON.
> ffprobe:
> * is not one of the libraries, but rather their caller
> * we are not in business of providing random non-multimedia-related
> services to callers, unless they are useful in our libraries;
> if this code is only useful in ffprobe then it should live in fftools/
This is your opinion, not the project policy.
My opinion is that anything that is useful for fftools and can be made
to have a properly-defined API has its place in libavutil.
Let us see which opinion has more support.
> * it is not at all obvious that switching ffprobe to this code would
> be an improvement; a patch actually demonstrating this would be most
> useful
I distinctly remember writing the changes to ffprobe and getting the
same output except for the indentation level, but I do not find the
code.
1: To be aware what would be useful to the project, reading the users
mailing-lists helps.
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 14:06 ` James Almer
@ 2023-04-29 17:17 ` Nicolas George
0 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-04-29 17:17 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 579 bytes --]
James Almer (12023-04-29):
> It should be exporting frame metadata
Not really. Exporting information to frame data is a hack, “this looks
pointy, we have a hammer, let's nail it”.
It only works if the data is very flat.
It does not work for data that comes at the end of the stream.
It still requires a way to get the data out in a parse-friendly syntax
when the program running is not ffprobe.
Having an API and user interface for filters to return structured data
is one of the reasons I want this API in libavutil.
Regards,
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 17:11 ` Nicolas George
@ 2023-04-29 18:27 ` Anton Khirnov
2023-04-29 18:33 ` Nicolas George
2023-04-30 15:06 ` Michael Niedermayer
0 siblings, 2 replies; 38+ messages in thread
From: Anton Khirnov @ 2023-04-29 18:27 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Quoting Nicolas George (2023-04-29 19:11:52)
> Anton Khirnov (2023-04-29):
> > As far as I can see, there are exactly two places in the codebase that
> > produce JSON: af_loudnorm and ffprobe.
>
> I think I remember finding a few other places, but it does not matter
> much.
>
> Users have been asking¹ for parse-friendly output from filters for years,
> and JSON is often the format they prefer. Therefore, all the filters
> that log useful information are candidates where a JSON writing API is
> useful.
libavfilter is a C library with a C API. Any structured output from
filters should be in the form of a C object, typically a struct. I do
not see why are you so in love with strings, they make for terrible
APIs.
We could conceivably use e.g. the AVOption mechanism to allow automated
serialization of such structs, but the actual mechanism should be left
to the callers.
> (See my answer to James for why “put it in frame metadata” is not a real
> solution.)
I believe frame metadata is a mistake and should not exist, so I am
certainly not suggesting to use it for anything.
--
Anton Khirnov
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 18:27 ` Anton Khirnov
@ 2023-04-29 18:33 ` Nicolas George
2023-05-01 6:57 ` Leo Izen
2023-04-30 15:06 ` Michael Niedermayer
1 sibling, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-04-29 18:33 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 464 bytes --]
Anton Khirnov (12023-04-29):
> libavfilter is a C library with a C API. Any structured output from
> filters should be in the form of a C object, typically a struct. I do
> not see why are you so in love with strings, they make for terrible
> APIs.
Yes, strings are a terrible API, but our project is not only a set of
libraries, it is also a set of command-line tools, and command-line
tools work with strings and nothing else.
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 15:06 ` Derek Buitenhuis
@ 2023-04-30 0:29 ` Kieran Kunhya
2023-05-01 6:20 ` Vittorio Giovara
0 siblings, 1 reply; 38+ messages in thread
From: Kieran Kunhya @ 2023-04-30 0:29 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sat, 29 Apr 2023 at 05:07, Derek Buitenhuis <derek.buitenhuis@gmail.com>
wrote:
> On 4/29/2023 10:41 AM, Anton Khirnov wrote:
> > ffprobe:
> > * is not one of the libraries, but rather their caller
> > * we are not in business of providing random non-multimedia-related
> > services to callers, unless they are useful in our libraries;
> > if this code is only useful in ffprobe then it should live in fftools/
> > * it is not at all obvious that switching ffprobe to this code would
> > be an improvement; a patch actually demonstrating this would be most
> > useful
>
> +1
>
>
+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-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 18:27 ` Anton Khirnov
2023-04-29 18:33 ` Nicolas George
@ 2023-04-30 15:06 ` Michael Niedermayer
2023-04-30 21:51 ` Kieran Kunhya
2023-05-01 9:46 ` Nicolas George
1 sibling, 2 replies; 38+ messages in thread
From: Michael Niedermayer @ 2023-04-30 15:06 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1804 bytes --]
On Sat, Apr 29, 2023 at 08:27:32PM +0200, Anton Khirnov wrote:
> Quoting Nicolas George (2023-04-29 19:11:52)
> > Anton Khirnov (2023-04-29):
> > > As far as I can see, there are exactly two places in the codebase that
> > > produce JSON: af_loudnorm and ffprobe.
> >
> > I think I remember finding a few other places, but it does not matter
> > much.
> >
> > Users have been asking¹ for parse-friendly output from filters for years,
> > and JSON is often the format they prefer. Therefore, all the filters
> > that log useful information are candidates where a JSON writing API is
> > useful.
>
> libavfilter is a C library with a C API. Any structured output from
> filters should be in the form of a C object, typically a struct. I do
> not see why are you so in love with strings, they make for terrible
> APIs.
There are many projects which use libavcodec, format, filter
Human users use these projects
If the standarization is at a C struct level only then the human interface
for each application can be different.
Thats fine if the 2 cases use fundamentally different interfaces like a
GUI draging, droping and connecting components vs some command line
interface.
But if 2 applications both use command line / string based interfaces
it would be nice to the human user if she could use/learn the same syntax
and transfer a working example / script from one to the other.
So i think strings do matter for C libs because of that.
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
The day soldiers stop bringing you their problems is the day you have stopped
leading them. They have either lost confidence that you can help or concluded
you do not care. Either case is a failure of leadership. - Colin Powell
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-30 15:06 ` Michael Niedermayer
@ 2023-04-30 21:51 ` Kieran Kunhya
2023-05-01 9:46 ` Nicolas George
1 sibling, 0 replies; 38+ messages in thread
From: Kieran Kunhya @ 2023-04-30 21:51 UTC (permalink / raw)
To: FFmpeg development discussions and patches
> There are many projects which use libavcodec, format, filter
> Human users use these projects
>
> If the standarization is at a C struct level only then the human interface
> for each application can be different.
> Thats fine if the 2 cases use fundamentally different interfaces like a
> GUI draging, droping and connecting components vs some command line
> interface.
> But if 2 applications both use command line / string based interfaces
> it would be nice to the human user if she could use/learn the same syntax
> and transfer a working example / script from one to the other.
>
> So i think strings do matter for C libs because of that.
>
> thx
>
The interface and the exchange format are completely different things.
Kieran
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-30 0:29 ` Kieran Kunhya
@ 2023-05-01 6:20 ` Vittorio Giovara
0 siblings, 0 replies; 38+ messages in thread
From: Vittorio Giovara @ 2023-05-01 6:20 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sat, Apr 29, 2023 at 8:29 PM Kieran Kunhya <kierank@obe.tv> wrote:
> On Sat, 29 Apr 2023 at 05:07, Derek Buitenhuis <derek.buitenhuis@gmail.com
> >
> wrote:
>
> > On 4/29/2023 10:41 AM, Anton Khirnov wrote:
> > > ffprobe:
> > > * is not one of the libraries, but rather their caller
> > > * we are not in business of providing random non-multimedia-related
> > > services to callers, unless they are useful in our libraries;
> > > if this code is only useful in ffprobe then it should live in
> fftools/
> > > * it is not at all obvious that switching ffprobe to this code would
> > > be an improvement; a patch actually demonstrating this would be most
> > > useful
> >
> > +1
> >
> >
> +2
>
+3
--
Vittorio
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-29 18:33 ` Nicolas George
@ 2023-05-01 6:57 ` Leo Izen
2023-05-01 9:51 ` Nicolas George
2023-05-01 10:18 ` Jean-Baptiste Kempf
0 siblings, 2 replies; 38+ messages in thread
From: Leo Izen @ 2023-05-01 6:57 UTC (permalink / raw)
To: ffmpeg-devel
On 4/29/23 14:33, Nicolas George wrote:
> Anton Khirnov (12023-04-29):
>> libavfilter is a C library with a C API. Any structured output from
>> filters should be in the form of a C object, typically a struct. I do
>> not see why are you so in love with strings, they make for terrible
>> APIs.
>
> Yes, strings are a terrible API, but our project is not only a set of
> libraries, it is also a set of command-line tools, and command-line
> tools work with strings and nothing else.
>
This is a good argument for putting the code in fftools/ and not
libavutil, fwiw.
- Leo Izen (Traneptora / thebombzen)
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-04-30 15:06 ` Michael Niedermayer
2023-04-30 21:51 ` Kieran Kunhya
@ 2023-05-01 9:46 ` Nicolas George
1 sibling, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-05-01 9:46 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 2177 bytes --]
Michael Niedermayer (12023-04-30):
> There are many projects which use libavcodec, format, filter
> Human users use these projects
>
> If the standarization is at a C struct level only then the human interface
> for each application can be different.
> Thats fine if the 2 cases use fundamentally different interfaces like a
> GUI draging, droping and connecting components vs some command line
> interface.
> But if 2 applications both use command line / string based interfaces
> it would be nice to the human user if she could use/learn the same syntax
> and transfer a working example / script from one to the other.
>
> So i think strings do matter for C libs because of that.
Thank you for stating it that way.
I think I can make it even a little stronger:
The API of the avlibraries is so rich that applications cannot
realistically cover all of them, and this is why we have the options
system: so that applications can expose all the knobs and controls of
avlibs without having to maintain code for every one of them.
But the options system has severe limitations, including the occasional
need for half-a-dozen backslashes or more for escaping and the inability
to define AV_OPT_TYPE_SOMETHING if SOMETHING is defined in another
library than lavu or nor generic enough.
Overcoming the limitations of the options system is a project I have had
for a long time, and it connects to the project of embedding the
documentation into the libraries (which has received some support).
http://ffmpeg.org/pipermail/ffmpeg-devel/2015-December/184525.html
(the technical details in my mind have evolved a little, but not much)
http://ffmpeg.org/pipermail/ffmpeg-devel/2020-August/268389.html
And for that, we absolutely need an efficient strings API (this is now
supported by a majority of developers, thankfully) and standardized
serialization functions. In the libraries, not the avtools.
To say it in a more concise way:
The avlibraries must not only perform work for applications, they also
must help applications communicate with users about that work, and that
is done with text.
Regards,
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-05-01 6:57 ` Leo Izen
@ 2023-05-01 9:51 ` Nicolas George
2023-05-01 10:18 ` Jean-Baptiste Kempf
1 sibling, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-05-01 9:51 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 834 bytes --]
Leo Izen (12023-05-01):
> > Yes, strings are a terrible API, but our project is not only a set of
> > libraries, it is also a set of command-line tools, and command-line
> > tools work with strings and nothing else.
> This is a good argument for putting the code in fftools/ and not libavutil,
> fwiw.
This is not wrong. But I realize now my argument was widely incomplete.
GUI applications not of our own still need text to communicate with
users about things that do not have their specific widget, for example.
The avlibraries must not only perform work for applications, they also
must help applications communicate with users about that work, and that
is done with text.
See there for a more complete wording:
http://ffmpeg.org/pipermail/ffmpeg-devel/2023-May/309077.html
Regards,
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP)
2023-05-01 6:57 ` Leo Izen
2023-05-01 9:51 ` Nicolas George
@ 2023-05-01 10:18 ` Jean-Baptiste Kempf
1 sibling, 0 replies; 38+ messages in thread
From: Jean-Baptiste Kempf @ 2023-05-01 10:18 UTC (permalink / raw)
To: ffmpeg-devel
On Mon, 1 May 2023, at 08:57, Leo Izen wrote:
> On 4/29/23 14:33, Nicolas George wrote:
>> Anton Khirnov (12023-04-29):
>>> libavfilter is a C library with a C API. Any structured output from
>>> filters should be in the form of a C object, typically a struct. I do
>>> not see why are you so in love with strings, they make for terrible
>>> APIs.
>>
>> Yes, strings are a terrible API, but our project is not only a set of
>> libraries, it is also a set of command-line tools, and command-line
>> tools work with strings and nothing else.
>>
>
> This is a good argument for putting the code in fftools/ and not
> libavutil, fwiw.
This is also my understanding.
--
Jean-Baptiste Kempf - President
+33 672 704 734
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
` (7 preceding siblings ...)
2023-04-29 8:17 ` [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Anton Khirnov
@ 2023-05-02 15:36 ` Rémi Denis-Courmont
2023-05-02 16:42 ` Nicolas George
8 siblings, 1 reply; 38+ messages in thread
From: Rémi Denis-Courmont @ 2023-05-02 15:36 UTC (permalink / raw)
To: ffmpeg-devel
Le perjantaina 28. huhtikuuta 2023, 12.55.01 EEST Nicolas George a écrit :
> Signed-off-by: Nicolas George <george@nsup.org>
> ---
> libavutil/extendable.h | 59 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 59 insertions(+)
> create mode 100644 libavutil/extendable.h
>
>
> FFReservedPadding is used by the WIP JSON writer.
A JSON writer that requires forced alignment is a poorly-written JSON parser.
JSON has a very finite set of types that it can handle, so there should never
be a need to do this kind of pointer arithmetic kludgery.
> diff --git a/libavutil/extendable.h b/libavutil/extendable.h
> new file mode 100644
> index 0000000000..79980fa202
> --- /dev/null
> +++ b/libavutil/extendable.h
> @@ -0,0 +1,59 @@
> +/*
> + * 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_EXTENDABLE_H
> +#define AVUTIL_EXTENDABLE_H
> +
> +/**
> + * @defgroup ff_extendable FFExtendable
> + *
> + * Types and macros to help designing structures that can be allocated by
> + * the application, including on the stack, but will not break ABI when
> + * extendded.
> + *
> + * This should not be used outside FFmpeg.
> + *
> + * @{
> + */
> +
> +/**
> + * Define a value of type as a compound literal (hidden local variable)
> + * with the field self_size filled.
> + */
> +#define FF_NEW_SZ(type) ((type){ .self_size = sizeof(type) })
> +
> +/**
> + * Type suitable for paddign at the end of a structure, with maximum
> + * alignment.
> + */
> +typedef union FFReservedPadding {
> + union {
> + double d;
> + void *p;
> + void (*f)(void);
> + intmax_t i;
> + } dummy;
> +} FFReservedPadding;
This is reinventing standard max_align_t but with a more confusing name and
less portable implementation... Indeed this is not about "padding", but about
alignment.
> +
> +/**
> + * @}
> + */
> +
> +#endif /* AVUTIL_EXTENDABLE_H */
--
雷米‧德尼-库尔蒙
http://www.remlab.net/
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API Nicolas George
2023-04-28 10:37 ` Rodney Baker
@ 2023-05-02 15:53 ` Rémi Denis-Courmont
2023-05-02 16:53 ` Nicolas George
1 sibling, 1 reply; 38+ messages in thread
From: Rémi Denis-Courmont @ 2023-05-02 15:53 UTC (permalink / raw)
To: ffmpeg-devel
Le perjantaina 28. huhtikuuta 2023, 12.55.02 EEST Nicolas George a écrit :
> +/**************************************************************************
> * + * 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);
> +}
Indirecting the printf function seems pretty pointless. The last thing you
want are different implementations of printf() with different limitations. And
it makes writing different backends needlessly complex, while you could just
use vasprintf().
Typically, with this kind of abstraction, only the raw bytes writing is
abstracted away. Examples include funopen() and fopencookie().
As for hypothetical use cases whence vasprintf() wouldb e "too slow", then
should not use printf()-style or function pointers to begin with. Besides if
you _really_ want to avoid the heap allocation, you can also use fopencookie()
on systems that provide it or equivalent.
> +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);
That sounds like it belongs in whichever backend actually wants to heap-
allocate the output buffer, not the frontend.
> + } else if (wr.methods->vprintf) {
> + size_t i;
> + for (i = 0; i < size; i++)
> + printf_unchecked(wr, "%c", data[i]);
This is an abstraction inversion (and also highly inefficient) + what I noted
above.
> + } else {
> + av_writer_impossible(wr, "av_writer_write()");
> + }
> +}
> +
> +void av_writer_print(AVWriter wr, const char *str)
> +{
This is an analogue of puts/fputs, not "print".
> + 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()");
> + }
> +}
Same problems and overengineering as above.
> +
> +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;
> +}
> +
--
レミ・デニ-クールモン
http://www.remlab.net/
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
2023-05-02 15:36 ` Rémi Denis-Courmont
@ 2023-05-02 16:42 ` Nicolas George
2023-05-02 18:31 ` Rémi Denis-Courmont
0 siblings, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-05-02 16:42 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 570 bytes --]
Rémi Denis-Courmont (12023-05-02):
> A JSON writer that requires forced alignment is a poorly-written JSON parser.
This JSON writer requires that the structures that contain its state are
aligned, just like any other piece of C code. There is nothing
poorly-written here.
> This is reinventing standard max_align_t but with a more confusing name and
> less portable implementation...
I did not know it exists. Thanks for the pointer.
> Indeed this is not about "padding", but about alignment.
It is about both.
Regards,
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-05-02 15:53 ` Rémi Denis-Courmont
@ 2023-05-02 16:53 ` Nicolas George
2023-05-02 18:29 ` Rémi Denis-Courmont
0 siblings, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-05-02 16:53 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1948 bytes --]
Rémi Denis-Courmont (12023-05-02):
> Indirecting the printf function seems pretty pointless. The last thing you
Please re-read the code, there is no other way of obtaining a va_list
from an actual argument than making a call to a vararg function.
> want are different implementations of printf() with different limitations. And
> it makes writing different backends needlessly complex, while you could just
> use vasprintf().
>
> Typically, with this kind of abstraction, only the raw bytes writing is
> abstracted away. Examples include funopen() and fopencookie().
>
> As for hypothetical use cases whence vasprintf() wouldb e "too slow", then
> should not use printf()-style or function pointers to begin with. Besides if
> you _really_ want to avoid the heap allocation, you can also use fopencookie()
> on systems that provide it or equivalent.
Being portable and not doing dynamic allocation are two major points of
the design, so you will excuse me if I pass on that suggestion.
> That sounds like it belongs in whichever backend actually wants to heap-
> allocate the output buffer, not the frontend.
There is nothing about heap allocation in this code. And if code can be
in the framework common to all backends, then it is where it belongs,
not duplicated in each backend.
> This is an abstraction inversion (and also highly inefficient) + what I noted
> above.
See above, it is necessary. (If you think it is not, try doing better.)
> This is an analogue of puts/fputs, not "print".
This is an analogue of puts, fputs and print, and I decided to call it
print.
> Same problems and overengineering as above.
I think you are missing something important about the purpose of this
code, but I cannot guess what exactly. Please be more detailed about why
you think it is overengineered and point how you would do it simpler.
Thanks for the comments.
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-05-02 16:53 ` Nicolas George
@ 2023-05-02 18:29 ` Rémi Denis-Courmont
2023-05-02 18:36 ` Nicolas George
0 siblings, 1 reply; 38+ messages in thread
From: Rémi Denis-Courmont @ 2023-05-02 18:29 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Le tiistaina 2. toukokuuta 2023, 19.53.43 EEST Nicolas George a écrit :
> Rémi Denis-Courmont (12023-05-02):
> > Indirecting the printf function seems pretty pointless. The last thing you
>
> Please re-read the code, there is no other way of obtaining a va_list
> from an actual argument than making a call to a vararg function.
Please re-read the comments. You are totally misses the point.
> > As for hypothetical use cases whence vasprintf() wouldb e "too slow", then
> > should not use printf()-style or function pointers to begin with. Besides
> > if you _really_ want to avoid the heap allocation, you can also use
> > fopencookie() on systems that provide it or equivalent.
>
> Being portable and not doing dynamic allocation are two major points of
> the design, so you will excuse me if I pass on that suggestion.
Well, I'll add myself to the already long list of people publicly objecting to
your patchset then.
> There is nothing about heap allocation in this code. And if code can be
> in the framework common to all backends, then it is where it belongs,
> not duplicated in each backend.
This makes zero sense. Why the hell would you want to have more than one heap-
allocation backend.
> > This is an abstraction inversion (and also highly inefficient) + what I
> > noted above.
>
> See above, it is necessary. (If you think it is not, try doing better.)
Well duh, I wrote the VLC equivalent years ago. And glibc or FreeBSD libc did
them more than a decade before I did.
> > This is an analogue of puts/fputs, not "print".
>
> This is an analogue of puts, fputs and print, and I decided to call it
> print.
And I decided to object to that on the basis that it's dumb and confusing.
> I think you are missing something important about the purpose of this
> code, but I cannot guess what exactly. Please be more detailed about why
> you think it is overengineered and point how you would do it simpler.
I think I was pretty damn clear and you are just deliberately making up reason
to ignore other people's comments (not just mine).
-1
--
雷米‧德尼-库尔蒙
http://www.remlab.net/
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
2023-05-02 16:42 ` Nicolas George
@ 2023-05-02 18:31 ` Rémi Denis-Courmont
2023-05-02 18:38 ` Nicolas George
0 siblings, 1 reply; 38+ messages in thread
From: Rémi Denis-Courmont @ 2023-05-02 18:31 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Le tiistaina 2. toukokuuta 2023, 19.42.39 EEST Nicolas George a écrit :
> Rémi Denis-Courmont (12023-05-02):
> > A JSON writer that requires forced alignment is a poorly-written JSON
> > parser.
> This JSON writer requires that the structures that contain its state are
> aligned, just like any other piece of C code.
So it must be very poorly written because C union are supposed to avoid this
cleanly.
> There is nothing poorly-written here.
> > Indeed this is not about "padding", but about alignment.
>
> It is about both.
If you need padding where any sane implementation does not, then it is clearly
poorly written.
--
レミ・デニ-クールモン
http://www.remlab.net/
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-05-02 18:29 ` Rémi Denis-Courmont
@ 2023-05-02 18:36 ` Nicolas George
2023-05-02 18:46 ` Rémi Denis-Courmont
0 siblings, 1 reply; 38+ messages in thread
From: Nicolas George @ 2023-05-02 18:36 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1267 bytes --]
Rémi Denis-Courmont (12023-05-02):
> Please re-read the comments. You are totally misses the point.
I confess so, indeed, I completely failed to understand your point after
reading your comment multiple times.
> Well, I'll add myself to the already long list of people publicly objecting to
> your patchset then.
I will read your technical comments then.
> > There is nothing about heap allocation in this code. And if code can be
> > in the framework common to all backends, then it is where it belongs,
> > not duplicated in each backend.
> This makes zero sense. Why the hell would you want to have more than one heap-
> allocation backend.
FFmpeg only provides one. Applications can decide to provide their own
if they want. That is the point.
> Well duh, I wrote the VLC equivalent years ago. And glibc or FreeBSD libc did
> them more than a decade before I did.
Pointer?
> And I decided to object to that on the basis that it's dumb and confusing.
The only thing dumb and confusing I see here is the mail I am answering
to.
> I think I was pretty damn clear and you are just deliberately making up reason
> to ignore other people's comments (not just mine).
Think what you will about me.
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures
2023-05-02 18:31 ` Rémi Denis-Courmont
@ 2023-05-02 18:38 ` Nicolas George
0 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-05-02 18:38 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 378 bytes --]
Rémi Denis-Courmont (12023-05-02):
> So it must be very poorly written because C union are supposed to avoid this
> cleanly.
> If you need padding where any sane implementation does not, then it is clearly
> poorly written.
I must say, I am impressed by the rudeness and arrogance of such a
comment without even looking at the code itself.
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-05-02 18:36 ` Nicolas George
@ 2023-05-02 18:46 ` Rémi Denis-Courmont
2023-05-02 18:47 ` Nicolas George
0 siblings, 1 reply; 38+ messages in thread
From: Rémi Denis-Courmont @ 2023-05-02 18:46 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Le tiistaina 2. toukokuuta 2023, 21.36.53 EEST Nicolas George a écrit :
> The only thing dumb and confusing I see here is the mail I am answering
> to.
> I must say, I am impressed by the rudeness and arrogance of such a
> comment without even looking at the code itself.
I think that that is pretty rich coming from you, just judging by your earlier
responses to other people. That's as insulting as it is unsurprising.
I don't need to remind you that there is a TC here.
--
雷米‧德尼-库尔蒙
http://www.remlab.net/
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API
2023-05-02 18:46 ` Rémi Denis-Courmont
@ 2023-05-02 18:47 ` Nicolas George
0 siblings, 0 replies; 38+ messages in thread
From: Nicolas George @ 2023-05-02 18:47 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 187 bytes --]
Rémi Denis-Courmont (12023-05-02):
> I think that that is pretty rich coming from you, just judging by your earlier
> responses to other people.
Pointers?
--
Nicolas George
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2023-05-02 18:47 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-28 9:55 [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 2/8] lavu: new AVWriter API Nicolas George
2023-04-28 10:37 ` Rodney Baker
2023-04-28 11:20 ` Nicolas George
2023-05-02 15:53 ` Rémi Denis-Courmont
2023-05-02 16:53 ` Nicolas George
2023-05-02 18:29 ` Rémi Denis-Courmont
2023-05-02 18:36 ` Nicolas George
2023-05-02 18:46 ` Rémi Denis-Courmont
2023-05-02 18:47 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 3/8] lavu/writer: add test Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 4/8] lavf/dump: use a writer Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 5/8] lavu: add a JSON writer API (WIP) Nicolas George
2023-04-29 9:11 ` Nicolas George
2023-04-29 9:41 ` Anton Khirnov
2023-04-29 14:06 ` James Almer
2023-04-29 17:17 ` Nicolas George
2023-04-29 15:06 ` Derek Buitenhuis
2023-04-30 0:29 ` Kieran Kunhya
2023-05-01 6:20 ` Vittorio Giovara
2023-04-29 17:11 ` Nicolas George
2023-04-29 18:27 ` Anton Khirnov
2023-04-29 18:33 ` Nicolas George
2023-05-01 6:57 ` Leo Izen
2023-05-01 9:51 ` Nicolas George
2023-05-01 10:18 ` Jean-Baptiste Kempf
2023-04-30 15:06 ` Michael Niedermayer
2023-04-30 21:51 ` Kieran Kunhya
2023-05-01 9:46 ` Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 6/8] lavu: add JSON writer test (WIP) Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 7/8] lavf/options: add av_disposition_write() Nicolas George
2023-04-28 9:55 ` [FFmpeg-devel] [PATCH 8/8] lavf/dump: use av_disposition_write() Nicolas George
2023-04-29 8:17 ` [FFmpeg-devel] [PATCH 1/8] lavu: add macros to help making future-proof structures Anton Khirnov
2023-04-29 15:11 ` Derek Buitenhuis
2023-05-02 15:36 ` Rémi Denis-Courmont
2023-05-02 16:42 ` Nicolas George
2023-05-02 18:31 ` Rémi Denis-Courmont
2023-05-02 18:38 ` Nicolas George
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git