[FFmpeg-devel] [PATCH 2/2] avutil/opt: add support for children objects in av_opt_serialize
James Almer
jamrial at gmail.com
Sat Apr 13 02:16:58 EEST 2024
Signed-off-by: James Almer <jamrial at gmail.com>
---
libavutil/opt.c | 65 +++++++++++++++++++++++++++++--------------
libavutil/opt.h | 1 +
libavutil/tests/opt.c | 47 +++++++++++++++++++++++++++++--
tests/ref/fate/opt | 2 +-
4 files changed, 90 insertions(+), 25 deletions(-)
diff --git a/libavutil/opt.c b/libavutil/opt.c
index d11e9d2ac5..ecbf7efe5f 100644
--- a/libavutil/opt.c
+++ b/libavutil/opt.c
@@ -2386,26 +2386,22 @@ int av_opt_is_set_to_default_by_name(void *obj, const char *name, int search_fla
return av_opt_is_set_to_default(target, o);
}
-int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,
- const char key_val_sep, const char pairs_sep)
+static int opt_serialize(void *obj, int opt_flags, int flags, int *cnt,
+ AVBPrint *bprint, const char key_val_sep, const char pairs_sep)
{
const AVOption *o = NULL;
+ void *child = NULL;
uint8_t *buf;
- AVBPrint bprint;
- int ret, cnt = 0;
+ int ret;
const char special_chars[] = {pairs_sep, key_val_sep, '\0'};
- if (pairs_sep == '\0' || key_val_sep == '\0' || pairs_sep == key_val_sep ||
- pairs_sep == '\\' || key_val_sep == '\\') {
- av_log(obj, AV_LOG_ERROR, "Invalid separator(s) found.");
- return AVERROR(EINVAL);
- }
-
- if (!obj || !buffer)
- return AVERROR(EINVAL);
-
- *buffer = NULL;
- av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
+ if (flags & AV_OPT_SERIALIZE_SEARCH_CHILDREN)
+ while (child = av_opt_child_next(obj, child)) {
+ ret = opt_serialize(child, opt_flags, flags, cnt, bprint,
+ key_val_sep, pairs_sep);
+ if (ret < 0)
+ return ret;
+ }
while (o = av_opt_next(obj, o)) {
if (o->type == AV_OPT_TYPE_CONST)
@@ -2417,18 +2413,45 @@ int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,
if (flags & AV_OPT_SERIALIZE_SKIP_DEFAULTS && av_opt_is_set_to_default(obj, o) > 0)
continue;
if ((ret = av_opt_get(obj, o->name, 0, &buf)) < 0) {
- av_bprint_finalize(&bprint, NULL);
+ av_bprint_finalize(bprint, NULL);
return ret;
}
if (buf) {
- if (cnt++)
- av_bprint_append_data(&bprint, &pairs_sep, 1);
- av_bprint_escape(&bprint, o->name, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
- av_bprint_append_data(&bprint, &key_val_sep, 1);
- av_bprint_escape(&bprint, buf, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
+ if ((*cnt)++)
+ av_bprint_append_data(bprint, &pairs_sep, 1);
+ av_bprint_escape(bprint, o->name, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
+ av_bprint_append_data(bprint, &key_val_sep, 1);
+ av_bprint_escape(bprint, buf, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
av_freep(&buf);
}
}
+
+ return 0;
+}
+
+int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer,
+ const char key_val_sep, const char pairs_sep)
+{
+ AVBPrint bprint;
+ int ret, cnt = 0;
+
+ if (pairs_sep == '\0' || key_val_sep == '\0' || pairs_sep == key_val_sep ||
+ pairs_sep == '\\' || key_val_sep == '\\') {
+ av_log(obj, AV_LOG_ERROR, "Invalid separator(s) found.");
+ return AVERROR(EINVAL);
+ }
+
+ if (!obj || !buffer)
+ return AVERROR(EINVAL);
+
+ *buffer = NULL;
+ av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
+
+ ret = opt_serialize(obj, opt_flags, flags, &cnt, &bprint,
+ key_val_sep, pairs_sep);
+ if (ret < 0)
+ return ret;
+
ret = av_bprint_finalize(&bprint, buffer);
if (ret < 0)
return ret;
diff --git a/libavutil/opt.h b/libavutil/opt.h
index e6013662f6..855e363091 100644
--- a/libavutil/opt.h
+++ b/libavutil/opt.h
@@ -929,6 +929,7 @@ int av_opt_flag_is_set(void *obj, const char *field_name, const char *flag_name)
#define AV_OPT_SERIALIZE_SKIP_DEFAULTS 0x00000001 ///< Serialize options that are not set to default values only.
#define AV_OPT_SERIALIZE_OPT_FLAGS_EXACT 0x00000002 ///< Serialize options that exactly match opt_flags only.
+#define AV_OPT_SERIALIZE_SEARCH_CHILDREN 0x00000004 ///< Serialize options in possible children of the given object.
/**
* Serialize object's options.
diff --git a/libavutil/tests/opt.c b/libavutil/tests/opt.c
index 32301ba842..53d8951bcf 100644
--- a/libavutil/tests/opt.c
+++ b/libavutil/tests/opt.c
@@ -30,6 +30,7 @@
typedef struct TestContext {
const AVClass *class;
+ struct ChildContext *child;
int num;
int toggle;
char *string;
@@ -123,10 +124,46 @@ static const char *test_get_name(void *ctx)
return "test";
}
+typedef struct ChildContext {
+ const AVClass *class;
+ int64_t child_num64;
+ int child_num;
+} ChildContext;
+
+#undef OFFSET
+#define OFFSET(x) offsetof(ChildContext, x)
+
+static const AVOption child_options[]= {
+ {"child_num64", "set num 64bit", OFFSET(child_num64), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 100, 1 },
+ {"child_num", "set child_num", OFFSET(child_num), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 100, 1 },
+ { NULL },
+};
+
+static const char *child_get_name(void *ctx)
+{
+ return "child";
+}
+
+static const AVClass child_class = {
+ .class_name = "ChildContext",
+ .item_name = child_get_name,
+ .option = child_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static void *test_child_next(void *obj, void *prev)
+{
+ TestContext *test_ctx = obj;
+ if (!prev)
+ return test_ctx->child;
+ return NULL;
+}
+
static const AVClass test_class = {
.class_name = "TestContext",
.item_name = test_get_name,
.option = test_options,
+ .child_next = test_child_next,
.version = LIBAVUTIL_VERSION_INT,
};
@@ -277,11 +314,15 @@ int main(void)
av_set_options_string(&test_ctx, buf, "=", ",");
av_free(buf);
if (av_opt_serialize(&test_ctx, 0, 0, &buf, '=', ',') >= 0) {
+ ChildContext child_ctx = { 0 };
printf("%s\n", buf);
av_free(buf);
- if (av_opt_serialize(&test_ctx, 0, AV_OPT_SERIALIZE_SKIP_DEFAULTS, &buf, '=', ',') >= 0) {
- if (strlen(buf))
- printf("%s\n", buf);
+ test_ctx.child = &child_ctx;
+ child_ctx.class = &child_class;
+ if (av_opt_serialize(&test_ctx, 0,
+ AV_OPT_SERIALIZE_SKIP_DEFAULTS|AV_OPT_SERIALIZE_SEARCH_CHILDREN,
+ &buf, '=', ',') >= 0) {
+ printf("%s\n", buf);
av_free(buf);
}
}
diff --git a/tests/ref/fate/opt b/tests/ref/fate/opt
index 43bf0929a3..05d57066a1 100644
--- a/tests/ref/fate/opt
+++ b/tests/ref/fate/opt
@@ -179,7 +179,7 @@ Setting entry with key 'array_int' to value ''
Setting entry with key 'array_str' to value 'str0|str\|1|str\\2'
Setting entry with key 'array_dict' to value 'k00=v\\\\00:k01=v\,01,k10=v\\=1\\:0'
num=0,toggle=1,rational=1/1,string=default,escape=\\\=\,,flags=0x00000001,size=200x300,pix_fmt=0bgr,sample_fmt=s16,video_rate=25/1,duration=0.001,color=0xffc0cbff,cl=hexagonal,bin=62696E00,bin1=,bin2=,num64=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false,dict1=,dict2=happy\=\\:-),array_int=,array_str=str0|str\\|1|str\\\\2,array_dict=k00\=v\\\\\\\\00:k01\=v\\\,01\,k10\=v\\\\\=1\\\\:0
-flt=0.333333,dbl=0.333333,array_int=
+child_num=0,flt=0.333333,dbl=0.333333,array_int=
Testing av_set_options_string()
Setting options string ''
--
2.44.0
More information about the ffmpeg-devel
mailing list