mirror of https://git.ffmpeg.org/ffmpeg.git
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.
This commit is contained in:
parent
4a5bb84515
commit
d89930f866
|
@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
|
|||
|
||||
API changes, most recent first:
|
||||
|
||||
2024-08-xx - xxxxxxxxx - lavu 59.35.100 - opt.h
|
||||
Add av_opt_get_array_size() and av_opt_get_array().
|
||||
|
||||
2024-08-xx - xxxxxxxxx - lavc 61.11.100- avcodec.h
|
||||
Clarify the documentation for get_buffer*() functions, making it
|
||||
clear that the memory returned by them should not contain sensitive
|
||||
|
|
119
libavutil/opt.c
119
libavutil/opt.c
|
@ -121,9 +121,9 @@ static unsigned *opt_array_pcount(const void *parray)
|
|||
return (unsigned *)((const void * const *)parray + 1);
|
||||
}
|
||||
|
||||
static void opt_free_elem(const AVOption *o, void *ptr)
|
||||
static void opt_free_elem(enum AVOptionType type, void *ptr)
|
||||
{
|
||||
switch (TYPE_BASE(o->type)) {
|
||||
switch (TYPE_BASE(type)) {
|
||||
case AV_OPT_TYPE_STRING:
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
av_freep(ptr);
|
||||
|
@ -145,7 +145,7 @@ static void opt_free_elem(const AVOption *o, void *ptr)
|
|||
static void opt_free_array(const AVOption *o, void *parray, unsigned *count)
|
||||
{
|
||||
for (unsigned i = 0; i < *count; i++)
|
||||
opt_free_elem(o, opt_array_pelem(o, *(void **)parray, i));
|
||||
opt_free_elem(o->type, opt_array_pelem(o, *(void **)parray, i));
|
||||
|
||||
av_freep(parray);
|
||||
*count = 0;
|
||||
|
@ -153,7 +153,7 @@ static void opt_free_array(const AVOption *o, void *parray, unsigned *count)
|
|||
|
||||
static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum)
|
||||
{
|
||||
switch (o->type) {
|
||||
switch (TYPE_BASE(o->type)) {
|
||||
case AV_OPT_TYPE_FLAGS:
|
||||
*intnum = *(unsigned int*)dst;
|
||||
return 0;
|
||||
|
@ -992,7 +992,7 @@ static void format_duration(char *buf, size_t size, int64_t d)
|
|||
}
|
||||
|
||||
static int opt_get_elem(const AVOption *o, uint8_t **pbuf, size_t buf_len,
|
||||
void *dst, int search_flags)
|
||||
const void *dst, int search_flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -1920,7 +1920,7 @@ void av_opt_free(void *obj)
|
|||
if (o->type & AV_OPT_TYPE_FLAG_ARRAY)
|
||||
opt_free_array(o, pitem, opt_array_pcount(pitem));
|
||||
else
|
||||
opt_free_elem(o, pitem);
|
||||
opt_free_elem(o->type, pitem);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2137,6 +2137,113 @@ int av_opt_copy(void *dst, const void *src)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int av_opt_get_array_size(void *obj, const char *name, int search_flags,
|
||||
unsigned int *out_val)
|
||||
{
|
||||
void *target_obj, *parray;
|
||||
const AVOption *o;
|
||||
|
||||
o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
|
||||
if (!o || !target_obj)
|
||||
return AVERROR_OPTION_NOT_FOUND;
|
||||
if (!(o->type & AV_OPT_TYPE_FLAG_ARRAY))
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
parray = (uint8_t *)target_obj + o->offset;
|
||||
*out_val = *opt_array_pcount(parray);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int av_opt_get_array(void *obj, const char *name, int search_flags,
|
||||
unsigned int start_elem, unsigned int nb_elems,
|
||||
enum AVOptionType out_type, void *out_val)
|
||||
{
|
||||
const size_t elem_size_out = opt_elem_size[TYPE_BASE(out_type)];
|
||||
|
||||
const AVOption *o;
|
||||
void *target_obj;
|
||||
|
||||
const void *parray;
|
||||
unsigned array_size;
|
||||
|
||||
int ret;
|
||||
|
||||
o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);
|
||||
if (!o || !target_obj)
|
||||
return AVERROR_OPTION_NOT_FOUND;
|
||||
if (!(o->type & AV_OPT_TYPE_FLAG_ARRAY) ||
|
||||
(out_type & AV_OPT_TYPE_FLAG_ARRAY))
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
parray = (uint8_t *)target_obj + o->offset;
|
||||
array_size = *opt_array_pcount(parray);
|
||||
|
||||
if (start_elem >= array_size ||
|
||||
array_size - start_elem < nb_elems)
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
for (unsigned i = 0; i < nb_elems; i++) {
|
||||
const void *src = opt_array_pelem(o, *(void**)parray, start_elem + i);
|
||||
void *dst = (uint8_t*)out_val + i * elem_size_out;
|
||||
|
||||
if (out_type == TYPE_BASE(o->type)) {
|
||||
ret = opt_copy_elem(obj, out_type, dst, src);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
} else if (out_type == AV_OPT_TYPE_STRING) {
|
||||
uint8_t buf[128], *out = buf;
|
||||
|
||||
ret = opt_get_elem(o, &out, sizeof(buf), src, search_flags);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (out == buf) {
|
||||
out = av_strdup(buf);
|
||||
if (!out) {
|
||||
ret = AVERROR(ENOMEM);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
*(uint8_t**)dst = out;
|
||||
} else if (out_type == AV_OPT_TYPE_INT64 ||
|
||||
out_type == AV_OPT_TYPE_DOUBLE ||
|
||||
out_type == AV_OPT_TYPE_RATIONAL) {
|
||||
double num = 1.0;
|
||||
int den = 1;
|
||||
int64_t intnum = 1;
|
||||
int ret;
|
||||
|
||||
ret = read_number(o, src, &num, &den, &intnum);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
switch (out_type) {
|
||||
case AV_OPT_TYPE_INT64:
|
||||
*(int64_t*)dst = (num == den) ? intnum : num * intnum / den;
|
||||
break;
|
||||
case AV_OPT_TYPE_DOUBLE:
|
||||
*(double*)dst = num * intnum / den;
|
||||
break;
|
||||
case AV_OPT_TYPE_RATIONAL:
|
||||
*(AVRational*)dst = (num == 1.0 && (int)intnum == intnum) ?
|
||||
(AVRational){ intnum, den } :
|
||||
av_d2q(num * intnum / den, 1<<24);
|
||||
break;
|
||||
default: av_assert0(0);
|
||||
}
|
||||
} else
|
||||
return AVERROR(ENOSYS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for (unsigned i = 0; i < nb_elems; i++)
|
||||
opt_free_elem(out_type, (uint8_t*)out_val + i * elem_size_out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags)
|
||||
{
|
||||
int ret;
|
||||
|
|
|
@ -944,6 +944,46 @@ int av_opt_get_chlayout(void *obj, const char *name, int search_flags, AVChannel
|
|||
* be freed with av_dict_free() by the caller
|
||||
*/
|
||||
int av_opt_get_dict_val(void *obj, const char *name, int search_flags, AVDictionary **out_val);
|
||||
|
||||
/**
|
||||
* For an array-type option, get the number of elements in the array.
|
||||
*/
|
||||
int av_opt_get_array_size(void *obj, const char *name, int search_flags,
|
||||
unsigned int *out_val);
|
||||
|
||||
/**
|
||||
* For an array-type option, retrieve the values of one or more array elements.
|
||||
*
|
||||
* @param start_elem index of the first array element to retrieve
|
||||
* @param nb_elems number of array elements to retrieve; start_elem+nb_elems
|
||||
* must not be larger than array size as returned by
|
||||
* av_opt_get_array_size()
|
||||
*
|
||||
* @param out_type Option type corresponding to the desired output.
|
||||
*
|
||||
* The array elements produced by this function will
|
||||
* will be as if av_opt_getX() was called for each element,
|
||||
* where X is specified by out_type. E.g. AV_OPT_TYPE_STRING
|
||||
* corresponds to av_opt_get().
|
||||
*
|
||||
* Typically this should be the same as the scalarized type of
|
||||
* the AVOption being retrieved, but certain conversions are
|
||||
* also possible - the same as those done by the corresponding
|
||||
* av_opt_get*() function. E.g. any option type can be retrieved
|
||||
* as a string, numeric types can be retrieved as int64, double,
|
||||
* or rational, etc.
|
||||
*
|
||||
* @param out_val Array with nb_elems members into which the output will be
|
||||
* written. The array type must match the underlying C type as
|
||||
* documented for out_type, and be zeroed on entry to this
|
||||
* function.
|
||||
*
|
||||
* For dynamically allocated types (strings, binary, dicts,
|
||||
* etc.), the result is owned and freed by the caller.
|
||||
*/
|
||||
int av_opt_get_array(void *obj, const char *name, int search_flags,
|
||||
unsigned int start_elem, unsigned int nb_elems,
|
||||
enum AVOptionType out_type, void *out_val);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -300,6 +300,69 @@ int main(void)
|
|||
av_opt_free(&test2_ctx);
|
||||
}
|
||||
|
||||
printf("\nTesting av_opt_get_array()\n");
|
||||
{
|
||||
static const int int_array[] = { 5, 0, 42, 137, INT_MAX };
|
||||
|
||||
TestContext test_ctx = { 0 };
|
||||
|
||||
int out_int [FF_ARRAY_ELEMS(int_array)] = { 0 };
|
||||
double out_double[FF_ARRAY_ELEMS(int_array)] = { 0. };
|
||||
char *out_str [FF_ARRAY_ELEMS(int_array)] = { NULL };
|
||||
AVDictionary *out_dict[2] = { NULL };
|
||||
|
||||
int ret;
|
||||
|
||||
test_ctx.class = &test_class;
|
||||
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
|
||||
av_opt_set_defaults(&test_ctx);
|
||||
|
||||
test_ctx.array_int = av_memdup(int_array, sizeof(int_array));
|
||||
test_ctx.nb_array_int = FF_ARRAY_ELEMS(int_array);
|
||||
|
||||
// retrieve as int
|
||||
ret = av_opt_get_array(&test_ctx, "array_int", 0,
|
||||
1, 3, AV_OPT_TYPE_INT, out_int);
|
||||
printf("av_opt_get_array(\"array_int\", 1, 3, INT)=%d -> [ %d, %d, %d ]\n",
|
||||
ret, out_int[0], out_int[1], out_int[2]);
|
||||
|
||||
// retrieve as double
|
||||
ret = av_opt_get_array(&test_ctx, "array_int", 0,
|
||||
3, 2, AV_OPT_TYPE_DOUBLE, out_double);
|
||||
printf("av_opt_get_array(\"array_int\", 3, 2, DOUBLE)=%d -> [ %.2f, %.2f ]\n",
|
||||
ret, out_double[0], out_double[1]);
|
||||
|
||||
// retrieve as str
|
||||
ret = av_opt_get_array(&test_ctx, "array_int", 0,
|
||||
0, 5, AV_OPT_TYPE_STRING, out_str);
|
||||
printf("av_opt_get_array(\"array_int\", 0, 5, STRING)=%d -> "
|
||||
"[ %s, %s, %s, %s, %s ]\n", ret,
|
||||
out_str[0], out_str[1], out_str[2], out_str[3], out_str[4]);
|
||||
|
||||
for (int i = 0; i < FF_ARRAY_ELEMS(out_str); i++)
|
||||
av_freep(&out_str[i]);
|
||||
|
||||
ret = av_opt_get_array(&test_ctx, "array_dict", 0, 0, 2,
|
||||
AV_OPT_TYPE_DICT, out_dict);
|
||||
printf("av_opt_get_array(\"array_dict\", 0, 2, DICT)=%d\n", ret);
|
||||
|
||||
for (int i = 0; i < test_ctx.nb_array_dict; i++) {
|
||||
const AVDictionaryEntry *e = NULL;
|
||||
while ((e = av_dict_iterate(test_ctx.array_dict[i], e))) {
|
||||
const AVDictionaryEntry *e1 = av_dict_get(out_dict[i], e->key, NULL, 0);
|
||||
if (!e1 || strcmp(e->value, e1->value)) {
|
||||
printf("mismatching dict entry %s: %s/%s\n",
|
||||
e->key, e->value, e1 ? e1->value : "<missing>");
|
||||
}
|
||||
}
|
||||
av_dict_free(&out_dict[i]);
|
||||
}
|
||||
|
||||
av_opt_free(&test_ctx);
|
||||
}
|
||||
|
||||
printf("\nTest av_opt_serialize()\n");
|
||||
{
|
||||
TestContext test_ctx = { 0 };
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
*/
|
||||
|
||||
#define LIBAVUTIL_VERSION_MAJOR 59
|
||||
#define LIBAVUTIL_VERSION_MINOR 34
|
||||
#define LIBAVUTIL_VERSION_MINOR 35
|
||||
#define LIBAVUTIL_VERSION_MICRO 100
|
||||
|
||||
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
|
||||
|
|
|
@ -154,6 +154,12 @@ av_opt_set("array_dict", NULL) -> 0
|
|||
array_dict=NULL; nb_array_dict=0
|
||||
av_opt_get("array_dict") -> NULL
|
||||
|
||||
Testing av_opt_get_array()
|
||||
av_opt_get_array("array_int", 1, 3, INT)=0 -> [ 0, 42, 137 ]
|
||||
av_opt_get_array("array_int", 3, 2, DOUBLE)=0 -> [ 137.00, 2147483647.00 ]
|
||||
av_opt_get_array("array_int", 0, 5, STRING)=0 -> [ 5, 0, 42, 137, 2147483647 ]
|
||||
av_opt_get_array("array_dict", 0, 2, DICT)=0
|
||||
|
||||
Test av_opt_serialize()
|
||||
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
|
||||
Setting entry with key 'num' to value '0'
|
||||
|
|
Loading…
Reference in New Issue