Previously one could only convert the entire array to a string, not access individual elements.
Unified · Split
| @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07 · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | API changes, most recent first: | 3 | API changes, most recent first: |
| 4 | 4 | ||
| 5 | 2024-08-xx - xxxxxxxxx - lavu 59.35.100 - opt.h | ||
| 6 | Add av_opt_get_array_size() and av_opt_get_array(). | ||
| 7 | |||
| 5 | 2024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h | 8 | 2024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h |
| 6 | Clarify the documentation for get_buffer*() functions, making it | 9 | Clarify the documentation for get_buffer*() functions, making it |
| 7 | clear that the memory returned by them should not contain sensitive | 10 | clear that the memory returned by them should not contain sensitive |
| @@ -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 | ||
| 124 | static void opt_free_elem(const AVOption *o, void *ptr) | 124 | static 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 | |||
| 145 | static void opt_free_array(const AVOption *o, void *parray, unsigned *count) | 145 | static 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 | ||
| 154 | static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum) | 154 | static 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 | ||
| 994 | static int opt_get_elem(const AVOption *o, uint8_t **pbuf, size_t buf_len, | 994 | 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) |
| 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 | else | 1922 | 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 | ||
| 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 | |||
| 2140 | int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags) | 2247 | int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags) |
| 2141 | { | 2248 | { |
| 2142 | int ret; | 2249 | int ret; |
| @@ -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 caller | 944 | * be freed with av_dict_free() by the caller |
| 945 | */ | 945 | */ |
| 946 | int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val); | 946 | 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); | ||
| 947 | /** | 987 | /** |
| 948 | * @} | 988 | * @} |
| 949 | */ | 989 | */ |
| @@ -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 }; |
| @@ -79,7 +79,7 @@ · 78 unmodified lines | |||
| 79 | */ | 79 | */ |
| 80 | 80 | ||
| 81 | #define LIBAVUTIL_VERSION_MAJOR 59 | 81 | #define LIBAVUTIL_VERSION_MAJOR 59 |
| 82 | #define LIBAVUTIL_VERSION_MINOR 34 | 82 | #define LIBAVUTIL_VERSION_MINOR 35 |
| 83 | #define LIBAVUTIL_VERSION_MICRO 100 | 83 | #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, \ |
| @@ -154,6 +154,12 @@ av_opt_set("array_dict", NULL) -> 0 · 153 unmodified lines | |||
| 154 | array_dict=NULL; nb_array_dict=0 | 154 | array_dict=NULL; nb_array_dict=0 |
| 155 | av_opt_get("array_dict") -> NULL | 155 | av_opt_get("array_dict") -> NULL |
| 156 | 156 | ||
| 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 | |||
| 157 | Test av_opt_serialize() | 163 | Test av_opt_serialize() |
| 158 | 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 | 164 | 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 |
| 159 | Setting entry with key 'num' to value '0' | 165 | Setting entry with key 'num' to value '0' |