History

lavu/opt: add API for retrieving array-type option values

Previously one could only convert the entire array to a string, not access individual elements.

Anton Khirnov committed 2 years ago

Commit d89930f1 parent 4a5bb846 files · +226 −7Browse files

Unified · Split

6 files
  1. doc/APIchanges +3 −0
  2. libavutil/opt.c +113 −6
  3. libavutil/opt.h +40 −0
  4. libavutil/tests/opt.c +63 −0
  5. libavutil/version.h +1 −1
  6. tests/ref/fate/opt +6 −0
doc/APIchanges+3−0
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07 · 1 unmodified line
22
33 API changes, most recent first:
44
5+2024-08-xx - xxxxxxxxx - lavu 59.35.100 - opt.h
6+ Add av_opt_get_array_size() and av_opt_get_array().
7+
58 2024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h
69 Clarify the documentation for get_buffer*() functions, making it
710 clear that the memory returned by them should not contain sensitive
libavutil/opt.c+113−6
@@ -121,9 +121,9 @@ static unsigned *opt_array_pcount(const void *parray) · 120 unmodified lines
121121 return (unsigned *)((const void * const *)parray + 1);
122122 }
123123
124−static void opt_free_elem(const AVOption *o, void *ptr)
124+static void opt_free_elem(enum AVOptionType type, void *ptr)
125125 {
126− switch (TYPE_BASE(o->type)) {
126+ switch (TYPE_BASE(type)) {
127127 case AV_OPT_TYPE_STRING:
128128 case AV_OPT_TYPE_BINARY:
129129 av_freep(ptr);
@@ -145,7 +145,7 @@ static void opt_free_elem(const AVOption *o, void *ptr) · 15 unmodified lines
145145 static void opt_free_array(const AVOption *o, void *parray, unsigned *count)
146146 {
147147 for (unsigned i = 0; i < *count; i++)
148− opt_free_elem(o, opt_array_pelem(o, *(void **)parray, i));
148+ opt_free_elem(o->type, opt_array_pelem(o, *(void **)parray, i));
149149
150150 av_freep(parray);
151151 *count = 0;
@@ -153,7 +153,7 @@ static void opt_free_array(const AVOption *o, void *parray, unsigned *count) · 1 unmodified line
153153
154154 static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum)
155155 {
156− switch (o->type) {
156+ switch (TYPE_BASE(o->type)) {
157157 case AV_OPT_TYPE_FLAGS:
158158 *intnum = *(unsigned int*)dst;
159159 return 0;
@@ -992,7 +992,7 @@ static void format_duration(char *buf, size_t size, int64_t d) · 832 unmodified lines
992992 }
993993
994994 static int opt_get_elem(const AVOption *o, uint8_t **pbuf, size_t buf_len,
995− void *dst, int search_flags)
995+ const void *dst, int search_flags)
996996 {
997997 int ret;
998998
@@ -1920,7 +1920,7 @@ void av_opt_free(void *obj) · 921 unmodified lines
19201920 if (o->type & AV_OPT_TYPE_FLAG_ARRAY)
19211921 opt_free_array(o, pitem, opt_array_pcount(pitem));
19221922 else
1923− opt_free_elem(o, pitem);
1923+ opt_free_elem(o->type, pitem);
19241924 }
19251925 }
19261926
@@ -2137,6 +2137,113 @@ int av_opt_copy(void *dst, const void *src) · 210 unmodified lines
21372137 return ret;
21382138 }
21392139
2140+int av_opt_get_array_size(void *obj, const char *name, int search_flags,
2141+ unsigned int *out_val)
2142+{
2143+ void *target_obj, *parray;
2144+ const AVOption *o;
2145+
2146+ o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
2147+ if (!o || !target_obj)
2148+ return AVERROR_OPTION_NOT_FOUND;
2149+ if (!(o->type & AV_OPT_TYPE_FLAG_ARRAY))
2150+ return AVERROR(EINVAL);
2151+
2152+ parray = (uint8_t *)target_obj + o->offset;
2153+ *out_val = *opt_array_pcount(parray);
2154+
2155+ return 0;
2156+}
2157+
2158+int av_opt_get_array(void *obj, const char *name, int search_flags,
2159+ unsigned int start_elem, unsigned int nb_elems,
2160+ enum AVOptionType out_type, void *out_val)
2161+{
2162+ const size_t elem_size_out = opt_elem_size[TYPE_BASE(out_type)];
2163+
2164+ const AVOption *o;
2165+ void *target_obj;
2166+
2167+ const void *parray;
2168+ unsigned array_size;
2169+
2170+ int ret;
2171+
2172+ o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
2173+ if (!o || !target_obj)
2174+ return AVERROR_OPTION_NOT_FOUND;
2175+ if (!(o->type & AV_OPT_TYPE_FLAG_ARRAY) ||
2176+ (out_type & AV_OPT_TYPE_FLAG_ARRAY))
2177+ return AVERROR(EINVAL);
2178+
2179+ parray = (uint8_t *)target_obj + o->offset;
2180+ array_size = *opt_array_pcount(parray);
2181+
2182+ if (start_elem >= array_size ||
2183+ array_size - start_elem < nb_elems)
2184+ return AVERROR(EINVAL);
2185+
2186+ for (unsigned i = 0; i < nb_elems; i++) {
2187+ const void *src = opt_array_pelem(o, *(void**)parray, start_elem + i);
2188+ void *dst = (uint8_t*)out_val + i * elem_size_out;
2189+
2190+ if (out_type == TYPE_BASE(o->type)) {
2191+ ret = opt_copy_elem(obj, out_type, dst, src);
2192+ if (ret < 0)
2193+ goto fail;
2194+ } else if (out_type == AV_OPT_TYPE_STRING) {
2195+ uint8_t buf[128], *out = buf;
2196+
2197+ ret = opt_get_elem(o, &out, sizeof(buf), src, search_flags);
2198+ if (ret < 0)
2199+ goto fail;
2200+
2201+ if (out == buf) {
2202+ out = av_strdup(buf);
2203+ if (!out) {
2204+ ret = AVERROR(ENOMEM);
2205+ goto fail;
2206+ }
2207+ }
2208+
2209+ *(uint8_t**)dst = out;
2210+ } else if (out_type == AV_OPT_TYPE_INT64 ||
2211+ out_type == AV_OPT_TYPE_DOUBLE ||
2212+ out_type == AV_OPT_TYPE_RATIONAL) {
2213+ double num = 1.0;
2214+ int den = 1;
2215+ int64_t intnum = 1;
2216+ int ret;
2217+
2218+ ret = read_number(o, src, &num, &den, &intnum);
2219+ if (ret < 0)
2220+ goto fail;
2221+
2222+ switch (out_type) {
2223+ case AV_OPT_TYPE_INT64:
2224+ *(int64_t*)dst = (num == den) ? intnum : num * intnum / den;
2225+ break;
2226+ case AV_OPT_TYPE_DOUBLE:
2227+ *(double*)dst = num * intnum / den;
2228+ break;
2229+ case AV_OPT_TYPE_RATIONAL:
2230+ *(AVRational*)dst = (num == 1.0 && (int)intnum == intnum) ?
2231+ (AVRational){ intnum, den } :
2232+ av_d2q(num * intnum / den, 1<<24);
2233+ break;
2234+ default: av_assert0(0);
2235+ }
2236+ } else
2237+ return AVERROR(ENOSYS);
2238+ }
2239+
2240+ return 0;
2241+fail:
2242+ for (unsigned i = 0; i < nb_elems; i++)
2243+ opt_free_elem(out_type, (uint8_t*)out_val + i * elem_size_out);
2244+ return ret;
2245+}
2246+
21402247 int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags)
21412248 {
21422249 int ret;
libavutil/opt.h+40−0
@@ -944,6 +944,46 @@ int av_opt_get_chlayout(void *obj, const char *name, int search_flags, AVChannel · 943 unmodified lines
944944 * be freed with av_dict_free() by the caller
945945 */
946946 int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val);
947+
948+/**
949+ * For an array-type option, get the number of elements in the array.
950+ */
951+int av_opt_get_array_size(void *obj, const char *name, int search_flags,
952+ unsigned int *out_val);
953+
954+/**
955+ * For an array-type option, retrieve the values of one or more array elements.
956+ *
957+ * @param start_elem index of the first array element to retrieve
958+ * @param nb_elems number of array elements to retrieve; start_elem+nb_elems
959+ * must not be larger than array size as returned by
960+ * av_opt_get_array_size()
961+ *
962+ * @param out_type Option type corresponding to the desired output.
963+ *
964+ * The array elements produced by this function will
965+ * will be as if av_opt_getX() was called for each element,
966+ * where X is specified by out_type. E.g. AV_OPT_TYPE_STRING
967+ * corresponds to av_opt_get().
968+ *
969+ * Typically this should be the same as the scalarized type of
970+ * the AVOption being retrieved, but certain conversions are
971+ * also possible - the same as those done by the corresponding
972+ * av_opt_get*() function. E.g. any option type can be retrieved
973+ * as a string, numeric types can be retrieved as int64, double,
974+ * or rational, etc.
975+ *
976+ * @param out_val Array with nb_elems members into which the output will be
977+ * written. The array type must match the underlying C type as
978+ * documented for out_type, and be zeroed on entry to this
979+ * function.
980+ *
981+ * For dynamically allocated types (strings, binary, dicts,
982+ * etc.), the result is owned and freed by the caller.
983+ */
984+int av_opt_get_array(void *obj, const char *name, int search_flags,
985+ unsigned int start_elem, unsigned int nb_elems,
986+ enum AVOptionType out_type, void *out_val);
947987 /**
948988 * @}
949989 */
libavutil/tests/opt.c+63−0
@@ -300,6 +300,69 @@ int main(void) · 299 unmodified lines
300300 av_opt_free(&test2_ctx);
301301 }
302302
303+ printf("\nTesting av_opt_get_array()\n");
304+ {
305+ static const int int_array[] = { 5, 0, 42, 137, INT_MAX };
306+
307+ TestContext test_ctx = { 0 };
308+
309+ int out_int [FF_ARRAY_ELEMS(int_array)] = { 0 };
310+ double out_double[FF_ARRAY_ELEMS(int_array)] = { 0. };
311+ char *out_str [FF_ARRAY_ELEMS(int_array)] = { NULL };
312+ AVDictionary *out_dict[2] = { NULL };
313+
314+ int ret;
315+
316+ test_ctx.class = &test_class;
317+
318+ av_log_set_level(AV_LOG_QUIET);
319+
320+ av_opt_set_defaults(&test_ctx);
321+
322+ test_ctx.array_int = av_memdup(int_array, sizeof(int_array));
323+ test_ctx.nb_array_int = FF_ARRAY_ELEMS(int_array);
324+
325+ // retrieve as int
326+ ret = av_opt_get_array(&test_ctx, "array_int", 0,
327+ 1, 3, AV_OPT_TYPE_INT, out_int);
328+ printf("av_opt_get_array(\"array_int\", 1, 3, INT)=%d -> [ %d, %d, %d ]\n",
329+ ret, out_int[0], out_int[1], out_int[2]);
330+
331+ // retrieve as double
332+ ret = av_opt_get_array(&test_ctx, "array_int", 0,
333+ 3, 2, AV_OPT_TYPE_DOUBLE, out_double);
334+ printf("av_opt_get_array(\"array_int\", 3, 2, DOUBLE)=%d -> [ %.2f, %.2f ]\n",
335+ ret, out_double[0], out_double[1]);
336+
337+ // retrieve as str
338+ ret = av_opt_get_array(&test_ctx, "array_int", 0,
339+ 0, 5, AV_OPT_TYPE_STRING, out_str);
340+ printf("av_opt_get_array(\"array_int\", 0, 5, STRING)=%d -> "
341+ "[ %s, %s, %s, %s, %s ]\n", ret,
342+ out_str[0], out_str[1], out_str[2], out_str[3], out_str[4]);
343+
344+ for (int i = 0; i < FF_ARRAY_ELEMS(out_str); i++)
345+ av_freep(&out_str[i]);
346+
347+ ret = av_opt_get_array(&test_ctx, "array_dict", 0, 0, 2,
348+ AV_OPT_TYPE_DICT, out_dict);
349+ printf("av_opt_get_array(\"array_dict\", 0, 2, DICT)=%d\n", ret);
350+
351+ for (int i = 0; i < test_ctx.nb_array_dict; i++) {
352+ const AVDictionaryEntry *e = NULL;
353+ while ((e = av_dict_iterate(test_ctx.array_dict[i], e))) {
354+ const AVDictionaryEntry *e1 = av_dict_get(out_dict[i], e->key, NULL, 0);
355+ if (!e1 || strcmp(e->value, e1->value)) {
356+ printf("mismatching dict entry %s: %s/%s\n",
357+ e->key, e->value, e1 ? e1->value : "<missing>");
358+ }
359+ }
360+ av_dict_free(&out_dict[i]);
361+ }
362+
363+ av_opt_free(&test_ctx);
364+ }
365+
303366 printf("\nTest av_opt_serialize()\n");
304367 {
305368 TestContext test_ctx = { 0 };
libavutil/version.h+1−1
@@ -79,7 +79,7 @@ · 78 unmodified lines
7979 */
8080
8181 #define LIBAVUTIL_VERSION_MAJOR 59
82−#define LIBAVUTIL_VERSION_MINOR 34
82+#define LIBAVUTIL_VERSION_MINOR 35
8383 #define LIBAVUTIL_VERSION_MICRO 100
8484
8585 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
tests/ref/fate/opt+6−0
@@ -154,6 +154,12 @@ av_opt_set("array_dict", NULL) -> 0 · 153 unmodified lines
154154 array_dict=NULL; nb_array_dict=0
155155 av_opt_get("array_dict") -> NULL
156156
157+Testing av_opt_get_array()
158+av_opt_get_array("array_int", 1, 3, INT)=0 -> [ 0, 42, 137 ]
159+av_opt_get_array("array_int", 3, 2, DOUBLE)=0 -> [ 137.00, 2147483647.00 ]
160+av_opt_get_array("array_int", 0, 5, STRING)=0 -> [ 5, 0, 42, 137, 2147483647 ]
161+av_opt_get_array("array_dict", 0, 2, DICT)=0
162+
157163 Test av_opt_serialize()
158164 num=0,unum=2147483648,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=4294967296,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
159165 Setting entry with key 'num' to value '0'

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t