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
2 2
3API changes, most recent first:3API changes, most recent first:
4 4
52024-08-xx - xxxxxxxxx - lavu 59.35.100 - opt.h
6 Add av_opt_get_array_size() and av_opt_get_array().
7
52024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h82024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h
6 Clarify the documentation for get_buffer*() functions, making it9 Clarify the documentation for get_buffer*() functions, making it
7 clear that the memory returned by them should not contain sensitive10 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
121 return (unsigned *)((const void * const *)parray + 1);121 return (unsigned *)((const void * const *)parray + 1);
122}122}
123 123
124static void opt_free_elem(const AVOption *o, void *ptr)124static void opt_free_elem(enum AVOptionType type, void *ptr)
125{125{
126 switch (TYPE_BASE(o->type)) {126 switch (TYPE_BASE(type)) {
127 case AV_OPT_TYPE_STRING:127 case AV_OPT_TYPE_STRING:
128 case AV_OPT_TYPE_BINARY:128 case AV_OPT_TYPE_BINARY:
129 av_freep(ptr);129 av_freep(ptr);
@@ -145,7 +145,7 @@ static void opt_free_elem(const AVOption *o, void *ptr) · 15 unmodified lines
145static void opt_free_array(const AVOption *o, void *parray, unsigned *count)145static void opt_free_array(const AVOption *o, void *parray, unsigned *count)
146{146{
147 for (unsigned i = 0; i < *count; i++)147 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));
149 149
150 av_freep(parray);150 av_freep(parray);
151 *count = 0;151 *count = 0;
@@ -153,7 +153,7 @@ static void opt_free_array(const AVOption *o, void *parray, unsigned *count) · 1 unmodified line
153 153
154static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum)154static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum)
155{155{
156 switch (o->type) {156 switch (TYPE_BASE(o->type)) {
157 case AV_OPT_TYPE_FLAGS:157 case AV_OPT_TYPE_FLAGS:
158 *intnum = *(unsigned int*)dst;158 *intnum = *(unsigned int*)dst;
159 return 0;159 return 0;
@@ -992,7 +992,7 @@ static void format_duration(char *buf, size_t size, int64_t d) · 832 unmodified lines
992}992}
993 993
994static int opt_get_elem(const AVOption *o, uint8_t **pbuf, size_t buf_len,994static 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)
996{996{
997 int ret;997 int ret;
998 998
@@ -1920,7 +1920,7 @@ void av_opt_free(void *obj) · 921 unmodified lines
1920 if (o->type & AV_OPT_TYPE_FLAG_ARRAY)1920 if (o->type & AV_OPT_TYPE_FLAG_ARRAY)
1921 opt_free_array(o, pitem, opt_array_pcount(pitem));1921 opt_free_array(o, pitem, opt_array_pcount(pitem));
1922 else1922 else
1923 opt_free_elem(o, pitem);1923 opt_free_elem(o->type, pitem);
1924 }1924 }
1925}1925}
1926 1926
@@ -2137,6 +2137,113 @@ int av_opt_copy(void *dst, const void *src) · 210 unmodified lines
2137 return ret;2137 return ret;
2138}2138}
2139 2139
2140int 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
2158int 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;
2241fail:
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
2140int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags)2247int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags)
2141{2248{
2142 int ret;2249 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
944 * be freed with av_dict_free() by the caller944 * be freed with av_dict_free() by the caller
945 */945 */
946int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val);946int 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 */
951int 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 */
984int 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);
947/**987/**
948 * @}988 * @}
949 */989 */
libavutil/tests/opt.c+63−0
@@ -300,6 +300,69 @@ int main(void) · 299 unmodified lines
300 av_opt_free(&test2_ctx);300 av_opt_free(&test2_ctx);
301 }301 }
302 302
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
303 printf("\nTest av_opt_serialize()\n");366 printf("\nTest av_opt_serialize()\n");
304 {367 {
305 TestContext test_ctx = { 0 };368 TestContext test_ctx = { 0 };
libavutil/version.h+1−1
@@ -79,7 +79,7 @@ · 78 unmodified lines
79 */79 */
80 80
81#define LIBAVUTIL_VERSION_MAJOR 5981#define LIBAVUTIL_VERSION_MAJOR 59
82#define LIBAVUTIL_VERSION_MINOR 3482#define LIBAVUTIL_VERSION_MINOR 35
83#define LIBAVUTIL_VERSION_MICRO 10083#define LIBAVUTIL_VERSION_MICRO 100
84 84
85#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \85#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
154array_dict=NULL; nb_array_dict=0154array_dict=NULL; nb_array_dict=0
155av_opt_get("array_dict") -> NULL155av_opt_get("array_dict") -> NULL
156 156
157Testing av_opt_get_array()
158av_opt_get_array("array_int", 1, 3, INT)=0 -> [ 0, 42, 137 ]
159av_opt_get_array("array_int", 3, 2, DOUBLE)=0 -> [ 137.00, 2147483647.00 ]
160av_opt_get_array("array_int", 0, 5, STRING)=0 -> [ 5, 0, 42, 137, 2147483647 ]
161av_opt_get_array("array_dict", 0, 2, DICT)=0
162
157Test av_opt_serialize()163Test av_opt_serialize()
158num=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\\\\:0164num=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
159Setting entry with key 'num' to value '0'165Setting 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