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:
parent
2a6f84718b
commit
450a3f58ed
@ -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().
|
||||
|
||||
|
186
libavutil/opt.c
186
libavutil/opt.c
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
@ -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, \
|
||||
|
Loading…
x
Reference in New Issue
Block a user