From 28ce461a84f8dc94a15615201380be59f2ea84c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Thu, 30 Apr 2026 20:53:46 +0200 Subject: [PATCH] options/m_option: add m_option_type_prop_array Add adaptor for arrays of properties, this is not used as real option, as those are always string list. But as a connector struct for creating typed node arrays, in (sub)_properties. --- options/m_option.c | 103 +++++++++++++++++++++++++++++++++++++++++++ options/m_option.h | 9 ++++ options/m_property.h | 10 +++++ 3 files changed, 122 insertions(+) diff --git a/options/m_option.c b/options/m_option.c index 63eee2225a..0e7815a801 100644 --- a/options/m_option.c +++ b/options/m_option.c @@ -1666,6 +1666,109 @@ const m_option_type_t m_option_type_string_list = { }, }; +//////////// Property array +// Adaptor that converts a typed C array into a MPV_FORMAT_NODE_ARRAY +// sub-property. This is for read-only access, parse/set are not intentionally +// omitted. + +static int prop_array_get(const m_option_t *opt, void *ta_parent, + struct mpv_node *dst, void *src) +{ + struct m_prop_arr *arr = src; + mp_assert(arr->elem_type); + mp_assert(arr->elem_type->size > 0); + size_t elem_size = arr->elem_type->size; + const char *base = arr->data; + dst->format = MPV_FORMAT_NODE_ARRAY; + dst->u.list = talloc_zero(ta_parent, mpv_node_list); + struct mpv_node_list *list = dst->u.list; + struct m_option elem_opt = {.type = arr->elem_type}; + for (int i = 0; i < arr->count; i++) { + MP_TARRAY_GROW(list, list->values, list->num); + if (m_option_get_node(&elem_opt, list, &list->values[list->num], + (void *)&base[i * elem_size]) >= 0) + list->num++; + } + return 1; +} + +static char *print_prop_array(const m_option_t *opt, const void *val) +{ + const struct m_prop_arr *arr = val; + mp_assert(arr->elem_type); + mp_assert(arr->elem_type->size > 0); + size_t elem_size = arr->elem_type->size; + const char *base = arr->data; + struct m_option elem_opt = {.type = arr->elem_type}; + char *ret = talloc_strdup(NULL, ""); + for (int i = 0; i < arr->count; i++) { + char *s = m_option_print(&elem_opt, &base[i * elem_size]); + if (i > 0) + ret = talloc_strdup_append(ret, ","); + ret = talloc_strdup_append(ret, s ? s : "?"); + talloc_free(s); + } + return ret; +} + +static void free_prop_array(void *val) +{ + struct m_prop_arr *arr = val; + if (!arr->data) + return; + size_t elem_size = arr->elem_type->size; + char *base = (char *)arr->data; + struct m_option elem_opt = {.type = arr->elem_type}; + for (int i = 0; i < arr->count; i++) + m_option_free(&elem_opt, &base[i * elem_size]); + talloc_free(base); + arr->data = NULL; + arr->count = 0; +} + +static void copy_prop_array(const m_option_t *opt, void *dst, const void *src) +{ + const struct m_prop_arr *s = src; + struct m_prop_arr *d = dst; + free_prop_array(d); + *d = (struct m_prop_arr){.elem_type = s->elem_type, .count = s->count}; + if (!s->data || !s->count) + return; + size_t elem_size = s->elem_type->size; + const char *sbase = s->data; + char *dbase = talloc_zero_size(NULL, (size_t)s->count * elem_size); + struct m_option elem_opt = {.type = s->elem_type}; + for (int i = 0; i < s->count; i++) + m_option_copy(&elem_opt, &dbase[i * elem_size], &sbase[i * elem_size]); + d->data = dbase; +} + +static bool equal_prop_array(const m_option_t *opt, void *a, void *b) +{ + struct m_prop_arr *pa = a, *pb = b; + if (pa->count != pb->count || pa->elem_type != pb->elem_type) + return false; + size_t elem_size = pa->elem_type->size; + char *abase = pa->data; + char *bbase = pb->data; + struct m_option elem_opt = {.type = pa->elem_type}; + for (int i = 0; i < pa->count; i++) { + if (!m_option_equal(&elem_opt, &abase[i * elem_size], &bbase[i * elem_size])) + return false; + } + return true; +} + +const m_option_type_t m_option_type_prop_array = { + .name = "Property array", + .size = sizeof(struct m_prop_arr), + .print = print_prop_array, + .copy = copy_prop_array, + .free = free_prop_array, + .get = prop_array_get, + .equal = equal_prop_array, +}; + static int read_subparam(struct mp_log *log, bstr optname, char *termset, bstr *str, bstr *out_subparam); diff --git a/options/m_option.h b/options/m_option.h index a91896962a..25dd84525d 100644 --- a/options/m_option.h +++ b/options/m_option.h @@ -50,6 +50,7 @@ extern const m_option_type_t m_option_type_float; extern const m_option_type_t m_option_type_double; extern const m_option_type_t m_option_type_string; extern const m_option_type_t m_option_type_string_list; +extern const m_option_type_t m_option_type_prop_array; extern const m_option_type_t m_option_type_string_append_list; extern const m_option_type_t m_option_type_keyvalue_list; extern const m_option_type_t m_option_type_time; @@ -227,6 +228,13 @@ struct m_sub_options { bool (*get_sub_options)(int index, const struct m_sub_options **sub); }; +// A typed C array value for use with m_option_type_prop_array. +struct m_prop_arr { + void *data; // first element; owned iff produced by copy + int count; // number of elements + const m_option_type_t *elem_type; // element type (must have a .get callback) +}; + #define CONF_TYPE_BOOL (&m_option_type_bool) #define CONF_TYPE_FLAG (&m_option_type_flag) #define CONF_TYPE_INT (&m_option_type_int) @@ -257,6 +265,7 @@ union m_option_value { char *string; char **string_list; char **keyvalue_list; + struct m_prop_arr prop_arr; int imgfmt; unsigned int fourcc; int afmt; diff --git a/options/m_property.h b/options/m_property.h index 7d854f4ea8..7d00baaa9a 100644 --- a/options/m_property.h +++ b/options/m_property.h @@ -226,6 +226,16 @@ struct m_sub_property { .type = {.type = &m_option_type_time}, .value = {.double_ = (f)} #define SUB_PROP_KEYVALUE_LIST(l) \ .type = {.type = &m_option_type_keyvalue_list}, .value = {.keyvalue_list = (l)} +// Expose a C array as a read-only array sub-property. The caller retains +// ownership of the data pointer. It will be copied if needed. Used for +// to expose arrays as sub-properties. +#define SUB_PROP_TYPE_ARRAY(etype, ptr, n) \ + .type = {.type = &m_option_type_prop_array}, \ + .value = {.prop_arr = {.data = (ptr), .count = (n), .elem_type = (etype)}} +#define SUB_PROP_INT_ARRAY(ptr, n) SUB_PROP_TYPE_ARRAY(CONF_TYPE_INT, ptr, n) +#define SUB_PROP_INT64_ARRAY(ptr, n) SUB_PROP_TYPE_ARRAY(CONF_TYPE_INT64, ptr, n) +#define SUB_PROP_FLOAT_ARRAY(ptr, n) SUB_PROP_TYPE_ARRAY(CONF_TYPE_FLOAT, ptr, n) +#define SUB_PROP_DOUBLE_ARRAY(ptr, n) SUB_PROP_TYPE_ARRAY(CONF_TYPE_DOUBLE, ptr, n) int m_property_read_sub_validate(void *ctx, struct m_property *prop, int action, void *arg);