From 8616cfe0890e49437d2b373f97a9c791eb1b7c4c Mon Sep 17 00:00:00 2001 From: James Almer Date: Fri, 12 Apr 2024 19:10:25 -0300 Subject: [PATCH] avutil/opt: add support for children objects in av_opt_serialize Signed-off-by: James Almer --- doc/APIchanges | 3 ++ libavutil/opt.c | 73 ++++++++++++++++++++++++++++--------------- libavutil/opt.h | 1 + libavutil/tests/opt.c | 49 +++++++++++++++++++++++++++-- libavutil/version.h | 2 +- tests/ref/fate/opt | 2 +- 6 files changed, 100 insertions(+), 30 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 63e7a47126..05912d2ed0 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07 API changes, most recent first: +2024-04-11 - xxxxxxxxxx - lavu 59.16.100 - opt.h + Add AV_OPT_SERIALIZE_SEARCH_CHILDREN. + 2024-04-11 - xxxxxxxxxx - lavc 61.5.102 - avcodec.h AVCodecContext.decoded_side_data may now be set by libavcodec after calling avcodec_open2(). diff --git a/libavutil/opt.c b/libavutil/opt.c index d11e9d2ac5..ecbf7efe5f 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -2386,14 +2386,54 @@ 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); } +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; + int ret; + const char special_chars[] = {pairs_sep, key_val_sep, '\0'}; + + 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) + continue; + if ((flags & AV_OPT_SERIALIZE_OPT_FLAGS_EXACT) && o->flags != opt_flags) + continue; + else if (((o->flags & opt_flags) != opt_flags)) + continue; + 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); + 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); + 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) { - const AVOption *o = NULL; - uint8_t *buf; AVBPrint bprint; int ret, cnt = 0; - 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 == '\\') { @@ -2407,28 +2447,11 @@ int av_opt_serialize(void *obj, int opt_flags, int flags, char **buffer, *buffer = NULL; av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED); - while (o = av_opt_next(obj, o)) { - if (o->type == AV_OPT_TYPE_CONST) - continue; - if ((flags & AV_OPT_SERIALIZE_OPT_FLAGS_EXACT) && o->flags != opt_flags) - continue; - else if (((o->flags & opt_flags) != opt_flags)) - continue; - 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); - 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); - av_freep(&buf); - } - } + 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 d43391025a..abe1b0dee4 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,13 +314,19 @@ 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); + child_ctx.class = &child_class; + test_ctx.child = &child_ctx; + 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); } + av_opt_free(&child_ctx); + test_ctx.child = NULL; } } av_opt_free(&test_ctx); diff --git a/libavutil/version.h b/libavutil/version.h index 1f2bddc022..ea289c406f 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 59 -#define LIBAVUTIL_VERSION_MINOR 15 +#define LIBAVUTIL_VERSION_MINOR 16 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ diff --git a/tests/ref/fate/opt b/tests/ref/fate/opt index f780097622..0a3362e8fa 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 ''