1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-02-04 06:08:26 +02:00

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

Previously one could only replace the entire array with a new one
deserialized from a string. The new API allows inserting, replacing, and
removing arbitrary element ranges.
This commit is contained in:
Anton Khirnov 2024-08-23 15:38:47 +02:00
parent 2a6f84718b
commit 450a3f58ed
4 changed files with 246 additions and 1 deletions

View File

@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
API changes, most recent first:
2024-09-xx - xxxxxxxxx - lavu 59.36.100 - opt.h
Add av_opt_set_array() and AV_OPT_ARRAY_REPLACE.
2024-08-xx - xxxxxxxxx - lavu 59.35.100 - opt.h
Add av_opt_get_array_size() and av_opt_get_array().

View File

@ -2244,6 +2244,192 @@ fail:
return ret;
}
int av_opt_set_array(void *obj, const char *name, int search_flags,
unsigned int start_elem, unsigned int nb_elems,
enum AVOptionType val_type, const void *val)
{
const size_t elem_size_val = opt_elem_size[TYPE_BASE(val_type)];
const AVOption *o;
const AVOptionArrayDef *arr;
void *target_obj;
void *parray;
void *new_elems;
unsigned *array_size, new_size;
size_t elem_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) ||
(val_type & AV_OPT_TYPE_FLAG_ARRAY))
return AVERROR(EINVAL);
arr = o->default_val.arr;
parray = (uint8_t *)target_obj + o->offset;
array_size = opt_array_pcount(parray);
elem_size = opt_elem_size[TYPE_BASE(o->type)];
if (start_elem > *array_size)
return AVERROR(EINVAL);
// compute new array size
if (!val) {
if (*array_size - start_elem < nb_elems)
return AVERROR(EINVAL);
new_size = *array_size - nb_elems;
} else if (search_flags & AV_OPT_ARRAY_REPLACE) {
if (start_elem >= UINT_MAX - nb_elems)
return AVERROR(EINVAL);
new_size = FFMAX(*array_size, start_elem + nb_elems);
} else {
if (nb_elems >= UINT_MAX - *array_size)
return AVERROR(EINVAL);
new_size = *array_size + nb_elems;
}
if (arr &&
((arr->size_max && new_size > arr->size_max) ||
(arr->size_min && new_size < arr->size_min)))
return AVERROR(EINVAL);
// desired operation is shrinking the array
if (!val) {
void *array = *(void**)parray;
for (unsigned i = 0; i < nb_elems; i++) {
opt_free_elem(o->type,
opt_array_pelem(o, array, start_elem + i));
}
if (new_size > 0) {
memmove(opt_array_pelem(o, array, start_elem),
opt_array_pelem(o, array, start_elem + nb_elems),
elem_size * (*array_size - start_elem - nb_elems));
array = av_realloc_array(array, new_size, elem_size);
if (!array)
return AVERROR(ENOMEM);
*(void**)parray = array;
} else
av_freep(parray);
*array_size = new_size;
return 0;
}
// otherwise, desired operation is insert/replace;
// first, store new elements in a separate array to simplify
// rollback on failure
new_elems = av_calloc(nb_elems, elem_size);
if (!new_elems)
return AVERROR(ENOMEM);
// convert/validate each new element
for (unsigned i = 0; i < nb_elems; i++) {
void *dst = opt_array_pelem(o, new_elems, i);
const void *src = (uint8_t*)val + i * elem_size_val;
double num = 1.0;
int den = 1;
int64_t intnum = 1;
if (val_type == TYPE_BASE(o->type)) {
ret = opt_copy_elem(obj, val_type, dst, src);
if (ret < 0)
goto fail;
// validate the range for numeric options
ret = read_number(o, dst, &num, &den, &intnum);
if (ret >= 0 && TYPE_BASE(o->type) != AV_OPT_TYPE_FLAGS &&
(!den || o->max * den < num * intnum || o->min * den > num * intnum)) {
num = den ? num * intnum / den : (num && intnum ? INFINITY : NAN);
av_log(obj, AV_LOG_ERROR, "Cannot set array element %u for "
"parameter '%s': value %f out of range [%g - %g]\n",
start_elem + i, o->name, num, o->min, o->max);
ret = AVERROR(ERANGE);
goto fail;
}
} else if (val_type == AV_OPT_TYPE_STRING) {
ret = opt_set_elem(obj, target_obj, o, *(const char **)src, dst);
if (ret < 0)
goto fail;
} if (val_type == AV_OPT_TYPE_INT ||
val_type == AV_OPT_TYPE_INT64 ||
val_type == AV_OPT_TYPE_FLOAT ||
val_type == AV_OPT_TYPE_DOUBLE ||
val_type == AV_OPT_TYPE_RATIONAL) {
int ret;
switch (val_type) {
case AV_OPT_TYPE_INT: intnum = *(int*)src; break;
case AV_OPT_TYPE_INT64: intnum = *(int64_t*)src; break;
case AV_OPT_TYPE_FLOAT: num = *(float*)src; break;
case AV_OPT_TYPE_DOUBLE: num = *(double*)src; break;
case AV_OPT_TYPE_RATIONAL: intnum = ((AVRational*)src)->num;
den = ((AVRational*)src)->den; break;
default: av_assert0(0);
}
ret = write_number(obj, o, dst, num, den, intnum);
if (ret < 0)
goto fail;
} else {
ret = AVERROR(ENOSYS);
goto fail;
}
}
// commit new elements to the array
if (start_elem == 0 && nb_elems == new_size) {
// replacing the existing array entirely
opt_free_array(o, parray, array_size);
*(void**)parray = new_elems;
*array_size = nb_elems;
new_elems = NULL;
nb_elems = 0;
} else {
void *array = av_realloc_array(*(void**)parray, new_size, elem_size);
if (!array) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (search_flags & AV_OPT_ARRAY_REPLACE) {
// free the elements being overwritten
for (unsigned i = start_elem; i < FFMIN(start_elem + nb_elems, *array_size); i++)
opt_free_elem(o->type, opt_array_pelem(o, array, i));
} else {
// shift existing elements to the end
memmove(opt_array_pelem(o, array, start_elem + nb_elems),
opt_array_pelem(o, array, start_elem),
elem_size * (*array_size - start_elem));
}
memcpy((uint8_t*)array + elem_size * start_elem, new_elems, elem_size * nb_elems);
av_freep(&new_elems);
nb_elems = 0;
*(void**)parray = array;
*array_size = new_size;
}
fail:
opt_free_array(o, &new_elems, &nb_elems);
return ret;
}
int av_opt_query_ranges(AVOptionRanges **ranges_arg, void *obj, const char *key, int flags)
{
int ret;

View File

@ -618,6 +618,12 @@ const AVClass *av_opt_child_class_iterate(const AVClass *parent, void **iter);
*/
#define AV_OPT_ALLOW_NULL (1 << 2)
/**
* May be used with av_opt_set_array() to signal that new elements should
* replace the existing ones in the indicated range.
*/
#define AV_OPT_ARRAY_REPLACE (1 << 3)
/**
* Allows av_opt_query_ranges and av_opt_query_ranges_default to return more than
* one component for certain option types.
@ -896,6 +902,56 @@ int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val, in
av_opt_set_bin(obj, name, (const uint8_t *)(val), \
av_int_list_length(val, term) * sizeof(*(val)), flags))
/**
* Add, replace, or remove elements for an array option. Which of these
* operations is performed depends on the values of val and search_flags.
*
* @param start_elem Index of the first array element to modify; must not be
* larger than array size as returned by
* av_opt_get_array_size().
* @param nb_elems number of array elements to modify; when val is NULL,
* start_elem+nb_elems must not be larger than array size as
* returned by av_opt_get_array_size()
*
* @param val_type Option type corresponding to the type of val, ignored when val is
* NULL.
*
* The effect of this function will will be as if av_opt_setX()
* was called for each element, where X is specified by type.
* E.g. AV_OPT_TYPE_STRING corresponds to av_opt_set().
*
* Typically this should be the same as the scalarized type of
* the AVOption being set, but certain conversions are also
* possible - the same as those done by the corresponding
* av_opt_set*() function. E.g. any option type can be set from
* a string, numeric types can be set from int64, double, or
* rational, etc.
*
* @param val Array with nb_elems elements or NULL.
*
* When NULL, nb_elems array elements starting at start_elem are
* removed from the array. Any array elements remaining at the end
* are shifted by nb_elems towards the first element in order to keep
* the array contiguous.
*
* Otherwise (val is non-NULL), the type of val must match the
* underlying C type as documented for val_type.
*
* When AV_OPT_ARRAY_REPLACE is not set in search_flags, the array is
* enlarged by nb_elems, and the contents of val are inserted at
* start_elem. Previously existing array elements from start_elem
* onwards (if present) are shifted by nb_elems away from the first
* element in order to make space for the new elements.
*
* When AV_OPT_ARRAY_REPLACE is set in search_flags, the contents
* of val replace existing array elements from start_elem to
* start_elem+nb_elems (if present). New array size is
* max(start_elem + nb_elems, old array size).
*/
int av_opt_set_array(void *obj, const char *name, int search_flags,
unsigned int start_elem, unsigned int nb_elems,
enum AVOptionType val_type, const void *val);
/**
* @}
* @}

View File

@ -79,7 +79,7 @@
*/
#define LIBAVUTIL_VERSION_MAJOR 59
#define LIBAVUTIL_VERSION_MINOR 35
#define LIBAVUTIL_VERSION_MINOR 36
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \