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 | 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 | 8 | 2024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h |
| 6 | 9 | Clarify the documentation for get_buffer*() functions, making it |
| 7 | 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 | 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 | 127 | case AV_OPT_TYPE_STRING: |
| 128 | 128 | case AV_OPT_TYPE_BINARY: |
| 129 | 129 | av_freep(ptr); |
| @@ -145,7 +145,7 @@ static void opt_free_elem(const AVOption *o, void *ptr) · 15 unmodified lines | ||
| 145 | 145 | static void opt_free_array(const AVOption *o, void *parray, unsigned *count) |
| 146 | 146 | { |
| 147 | 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 | 150 | av_freep(parray); |
| 151 | 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 | 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 | 157 | case AV_OPT_TYPE_FLAGS: |
| 158 | 158 | *intnum = *(unsigned int*)dst; |
| 159 | 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 | 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 | 997 | int ret; |
| 998 | 998 | |
| @@ -1920,7 +1920,7 @@ void av_opt_free(void *obj) · 921 unmodified lines | ||
| 1920 | 1920 | if (o->type & AV_OPT_TYPE_FLAG_ARRAY) |
| 1921 | 1921 | opt_free_array(o, pitem, opt_array_pcount(pitem)); |
| 1922 | 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 | 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 | 2247 | int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags) |
| 2141 | 2248 | { |
| 2142 | 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 | 944 | * be freed with av_dict_free() by the caller |
| 945 | 945 | */ |
| 946 | 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 | 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 | 366 | printf("\nTest av_opt_serialize()\n"); |
| 304 | 367 | { |
| 305 | 368 | TestContext test_ctx = { 0 }; |
| @@ -79,7 +79,7 @@ · 78 unmodified lines | ||
| 79 | 79 | */ |
| 80 | 80 | |
| 81 | 81 | #define LIBAVUTIL_VERSION_MAJOR 59 |
| 82 | −#define LIBAVUTIL_VERSION_MINOR 34 | |
| 82 | +#define LIBAVUTIL_VERSION_MINOR 35 | |
| 83 | 83 | #define LIBAVUTIL_VERSION_MICRO 100 |
| 84 | 84 | |
| 85 | 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 | 154 | array_dict=NULL; nb_array_dict=0 |
| 155 | 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 | 163 | Test av_opt_serialize() |
| 158 | 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 | 165 | Setting entry with key 'num' to value '0' |