mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2025-01-11 18:09:36 +00:00
lavu/opt: add array options
This commit is contained in:
parent
fc706276c0
commit
efe4478778
@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
|
||||
|
||||
API changes, most recent first:
|
||||
|
||||
2024-03-08 - xxxxxxxxxx - lavu 59.1.100 - opt.h
|
||||
Add AV_OPT_TYPE_FLAG_ARRAY and AVOptionArrayDef.
|
||||
|
||||
2024-03-08 - xxxxxxxxxx - lavc 61.0.100 - vdpau.h
|
||||
Deprecate av_vdpau_alloc_context(), av_alloc_vdpaucontext(),
|
||||
av_vdpau_hwaccel_get_render2() and av_vdpau_hwaccel_set_render2().
|
||||
|
360
libavutil/opt.c
360
libavutil/opt.c
@ -43,6 +43,8 @@
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#define TYPE_BASE(type) ((type) & ~AV_OPT_TYPE_FLAG_ARRAY)
|
||||
|
||||
const AVOption *av_opt_next(const void *obj, const AVOption *last)
|
||||
{
|
||||
const AVClass *class;
|
||||
@ -100,6 +102,54 @@ static int opt_is_pod(enum AVOptionType type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t opt_array_sep(const AVOption *o)
|
||||
{
|
||||
const AVOptionArrayDef *d = o->default_val.arr;
|
||||
av_assert1(o->type & AV_OPT_TYPE_FLAG_ARRAY);
|
||||
return (d && d->sep) ? d->sep : ',';
|
||||
}
|
||||
|
||||
static void *opt_array_pelem(const AVOption *o, void *array, unsigned idx)
|
||||
{
|
||||
av_assert1(o->type & AV_OPT_TYPE_FLAG_ARRAY);
|
||||
return (uint8_t *)array + idx * opt_elem_size[TYPE_BASE(o->type)];
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
switch (TYPE_BASE(o->type)) {
|
||||
case AV_OPT_TYPE_STRING:
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
av_freep(ptr);
|
||||
break;
|
||||
|
||||
case AV_OPT_TYPE_DICT:
|
||||
av_dict_free((AVDictionary **)ptr);
|
||||
break;
|
||||
|
||||
case AV_OPT_TYPE_CHLAYOUT:
|
||||
av_channel_layout_uninit((AVChannelLayout *)ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
av_freep(parray);
|
||||
*count = 0;
|
||||
}
|
||||
|
||||
static int read_number(const AVOption *o, const void *dst, double *num, int *den, int64_t *intnum)
|
||||
{
|
||||
switch (o->type) {
|
||||
@ -140,14 +190,16 @@ static int read_number(const AVOption *o, const void *dst, double *num, int *den
|
||||
|
||||
static int write_number(void *obj, const AVOption *o, void *dst, double num, int den, int64_t intnum)
|
||||
{
|
||||
if (o->type != AV_OPT_TYPE_FLAGS &&
|
||||
const enum AVOptionType type = TYPE_BASE(o->type);
|
||||
|
||||
if (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, "Value %f for parameter '%s' out of range [%g - %g]\n",
|
||||
num, o->name, o->min, o->max);
|
||||
return AVERROR(ERANGE);
|
||||
}
|
||||
if (o->type == AV_OPT_TYPE_FLAGS) {
|
||||
if (type == AV_OPT_TYPE_FLAGS) {
|
||||
double d = num*intnum/den;
|
||||
if (d < -1.5 || d > 0xFFFFFFFF+0.5 || (llrint(d*256) & 255)) {
|
||||
av_log(obj, AV_LOG_ERROR,
|
||||
@ -157,7 +209,7 @@ static int write_number(void *obj, const AVOption *o, void *dst, double num, int
|
||||
}
|
||||
}
|
||||
|
||||
switch (o->type) {
|
||||
switch (type) {
|
||||
case AV_OPT_TYPE_PIXEL_FMT:
|
||||
*(enum AVPixelFormat *)dst = llrint(num / den) * intnum;
|
||||
break;
|
||||
@ -271,9 +323,10 @@ static int set_string(void *obj, const AVOption *o, const char *val, uint8_t **d
|
||||
|
||||
static int set_string_number(void *obj, void *target_obj, const AVOption *o, const char *val, void *dst)
|
||||
{
|
||||
const enum AVOptionType type = TYPE_BASE(o->type);
|
||||
int ret = 0;
|
||||
|
||||
if (o->type == AV_OPT_TYPE_RATIONAL || o->type == AV_OPT_TYPE_VIDEO_RATE) {
|
||||
if (type == AV_OPT_TYPE_RATIONAL || type == AV_OPT_TYPE_VIDEO_RATE) {
|
||||
int num, den;
|
||||
char c;
|
||||
if (sscanf(val, "%d%*1[:/]%d%c", &num, &den, &c) == 2) {
|
||||
@ -290,7 +343,7 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con
|
||||
double d;
|
||||
int64_t intnum = 1;
|
||||
|
||||
if (o->type == AV_OPT_TYPE_FLAGS) {
|
||||
if (type == AV_OPT_TYPE_FLAGS) {
|
||||
if (*val == '+' || *val == '-')
|
||||
cmd = *(val++);
|
||||
for (; i < sizeof(buf) - 1 && val[i] && val[i] != '+' && val[i] != '-'; i++)
|
||||
@ -346,7 +399,7 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con
|
||||
}
|
||||
}
|
||||
}
|
||||
if (o->type == AV_OPT_TYPE_FLAGS) {
|
||||
if (type == AV_OPT_TYPE_FLAGS) {
|
||||
intnum = *(unsigned int*)dst;
|
||||
if (cmd == '+')
|
||||
d = intnum | (int64_t)d;
|
||||
@ -531,18 +584,17 @@ static int set_string_channel_layout(void *obj, const AVOption *o,
|
||||
static int opt_set_elem(void *obj, void *target_obj, const AVOption *o,
|
||||
const char *val, void *dst)
|
||||
{
|
||||
const enum AVOptionType type = TYPE_BASE(o->type);
|
||||
int ret;
|
||||
|
||||
FF_DISABLE_DEPRECATION_WARNINGS
|
||||
if (!val && (o->type != AV_OPT_TYPE_STRING &&
|
||||
o->type != AV_OPT_TYPE_PIXEL_FMT && o->type != AV_OPT_TYPE_SAMPLE_FMT &&
|
||||
o->type != AV_OPT_TYPE_IMAGE_SIZE &&
|
||||
o->type != AV_OPT_TYPE_DURATION && o->type != AV_OPT_TYPE_COLOR &&
|
||||
o->type != AV_OPT_TYPE_BOOL))
|
||||
if (!val && (type != AV_OPT_TYPE_STRING &&
|
||||
type != AV_OPT_TYPE_PIXEL_FMT && type != AV_OPT_TYPE_SAMPLE_FMT &&
|
||||
type != AV_OPT_TYPE_IMAGE_SIZE &&
|
||||
type != AV_OPT_TYPE_DURATION && type != AV_OPT_TYPE_COLOR &&
|
||||
type != AV_OPT_TYPE_BOOL))
|
||||
return AVERROR(EINVAL);
|
||||
FF_ENABLE_DEPRECATION_WARNINGS
|
||||
|
||||
switch (o->type) {
|
||||
switch (type) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
return set_string_bool(obj, o, val, dst);
|
||||
case AV_OPT_TYPE_STRING:
|
||||
@ -604,6 +656,85 @@ FF_ENABLE_DEPRECATION_WARNINGS
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
static int opt_set_array(void *obj, void *target_obj, const AVOption *o,
|
||||
const char *val, void *dst)
|
||||
{
|
||||
const AVOptionArrayDef *arr = o->default_val.arr;
|
||||
const size_t elem_size = opt_elem_size[TYPE_BASE(o->type)];
|
||||
const uint8_t sep = opt_array_sep(o);
|
||||
uint8_t *str = NULL;
|
||||
|
||||
void *elems = NULL;
|
||||
unsigned nb_elems = 0;
|
||||
int ret;
|
||||
|
||||
if (val && *val) {
|
||||
str = av_malloc(strlen(val) + 1);
|
||||
if (!str)
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
// split and unescape the string
|
||||
while (val && *val) {
|
||||
uint8_t *p = str;
|
||||
void *tmp;
|
||||
|
||||
if (arr && arr->size_max && nb_elems >= arr->size_max) {
|
||||
av_log(obj, AV_LOG_ERROR,
|
||||
"Cannot assign more than %u elements to array option %s\n",
|
||||
arr->size_max, o->name);
|
||||
ret = AVERROR(EINVAL);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (; *val; val++, p++) {
|
||||
if (*val == '\\' && val[1])
|
||||
val++;
|
||||
else if (*val == sep) {
|
||||
val++;
|
||||
break;
|
||||
}
|
||||
*p = *val;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
tmp = av_realloc_array(elems, nb_elems + 1, elem_size);
|
||||
if (!tmp) {
|
||||
ret = AVERROR(ENOMEM);
|
||||
goto fail;
|
||||
}
|
||||
elems = tmp;
|
||||
|
||||
tmp = opt_array_pelem(o, elems, nb_elems);
|
||||
memset(tmp, 0, elem_size);
|
||||
|
||||
ret = opt_set_elem(obj, target_obj, o, str, tmp);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
nb_elems++;
|
||||
}
|
||||
av_freep(&str);
|
||||
|
||||
opt_free_array(o, dst, opt_array_pcount(dst));
|
||||
|
||||
if (arr && nb_elems < arr->size_min) {
|
||||
av_log(obj, AV_LOG_ERROR,
|
||||
"Cannot assign fewer than %u elements to array option %s\n",
|
||||
arr->size_min, o->name);
|
||||
ret = AVERROR(EINVAL);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*((void **)dst) = elems;
|
||||
*opt_array_pcount(dst) = nb_elems;
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
av_freep(&str);
|
||||
opt_free_array(o, &elems, &nb_elems);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
|
||||
{
|
||||
void *dst, *target_obj;
|
||||
@ -619,7 +750,8 @@ int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
|
||||
|
||||
dst = ((uint8_t *)target_obj) + o->offset;
|
||||
|
||||
return opt_set_elem(obj, target_obj, o, val, dst);
|
||||
return ((o->type & AV_OPT_TYPE_FLAG_ARRAY) ?
|
||||
opt_set_array : opt_set_elem)(obj, target_obj, o, val, dst);
|
||||
}
|
||||
|
||||
#define OPT_EVAL_NUMBER(name, opttype, vartype) \
|
||||
@ -647,7 +779,7 @@ static int set_number(void *obj, const char *name, double num, int den, int64_t
|
||||
if (!o || !target_obj)
|
||||
return AVERROR_OPTION_NOT_FOUND;
|
||||
|
||||
if (o->flags & AV_OPT_FLAG_READONLY)
|
||||
if ((o->flags & AV_OPT_FLAG_READONLY) || (o->type & AV_OPT_TYPE_FLAG_ARRAY))
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
dst = ((uint8_t *)target_obj) + o->offset;
|
||||
@ -730,7 +862,8 @@ int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int searc
|
||||
return AVERROR_OPTION_NOT_FOUND;
|
||||
if (o->type != AV_OPT_TYPE_VIDEO_RATE) {
|
||||
av_log(obj, AV_LOG_ERROR,
|
||||
"The value set by option '%s' is not a video rate.\n", o->name);
|
||||
"The value set by option '%s' is not a video rate.\n",
|
||||
o->name);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
if (val.num <= 0 || val.den <= 0)
|
||||
@ -852,7 +985,7 @@ static int opt_get_elem(const AVOption *o, uint8_t **pbuf, size_t buf_len,
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (o->type) {
|
||||
switch (TYPE_BASE(o->type)) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
ret = snprintf(*pbuf, buf_len, "%s", get_bool_name(*(int *)dst));
|
||||
break;
|
||||
@ -949,6 +1082,66 @@ static int opt_get_elem(const AVOption *o, uint8_t **pbuf, size_t buf_len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int opt_get_array(const AVOption *o, void *dst, uint8_t **out_val)
|
||||
{
|
||||
const unsigned count = *opt_array_pcount(dst);
|
||||
const uint8_t sep = opt_array_sep(o);
|
||||
|
||||
uint8_t *str = NULL;
|
||||
size_t str_len = 0;
|
||||
int ret;
|
||||
|
||||
*out_val = NULL;
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
uint8_t buf[128], *out = buf;
|
||||
size_t out_len;
|
||||
|
||||
ret = opt_get_elem(o, &out, sizeof(buf),
|
||||
opt_array_pelem(o, *(void **)dst, i), 0);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
out_len = strlen(out);
|
||||
if (out_len > SIZE_MAX / 2 - !!i ||
|
||||
!!i + out_len * 2 > SIZE_MAX - str_len - 1) {
|
||||
ret = AVERROR(ERANGE);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// terminator escaping separator
|
||||
// ↓ ↓ ↓
|
||||
ret = av_reallocp(&str, str_len + 1 + out_len * 2 + !!i);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
// add separator if needed
|
||||
if (i)
|
||||
str[str_len++] = sep;
|
||||
|
||||
// escape the element
|
||||
for (unsigned j = 0; j < out_len; j++) {
|
||||
uint8_t val = out[j];
|
||||
if (val == sep || val == '\\')
|
||||
str[str_len++] = '\\';
|
||||
str[str_len++] = val;
|
||||
}
|
||||
str[str_len] = 0;
|
||||
|
||||
fail:
|
||||
if (out != buf)
|
||||
av_freep(&out);
|
||||
if (ret < 0) {
|
||||
av_freep(&str);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
*out_val = str;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val)
|
||||
{
|
||||
void *dst, *target_obj;
|
||||
@ -964,8 +1157,19 @@ int av_opt_get(void *obj, const char *name, int search_flags, uint8_t **out_val)
|
||||
|
||||
dst = (uint8_t *)target_obj + o->offset;
|
||||
|
||||
buf[0] = 0;
|
||||
if (o->type & AV_OPT_TYPE_FLAG_ARRAY) {
|
||||
ret = opt_get_array(o, dst, out_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!*out_val && !(search_flags & AV_OPT_ALLOW_NULL)) {
|
||||
*out_val = av_strdup("");
|
||||
if (!*out_val)
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[0] = 0;
|
||||
out = buf;
|
||||
ret = opt_get_elem(o, &out, sizeof(buf), dst, search_flags);
|
||||
if (ret < 0)
|
||||
@ -988,6 +1192,8 @@ static int get_number(void *obj, const char *name, double *num, int *den, int64_
|
||||
const AVOption *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);
|
||||
|
||||
dst = ((uint8_t *)target_obj) + o->offset;
|
||||
|
||||
@ -1045,7 +1251,7 @@ int av_opt_get_image_size(void *obj, const char *name, int search_flags, int *w_
|
||||
return AVERROR_OPTION_NOT_FOUND;
|
||||
if (o->type != AV_OPT_TYPE_IMAGE_SIZE) {
|
||||
av_log(obj, AV_LOG_ERROR,
|
||||
"The value for option '%s' is not an image size.\n", name);
|
||||
"The value for option '%s' is not a image size.\n", name);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
@ -1252,11 +1458,16 @@ static void log_type(void *av_log_obj, const AVOption *o,
|
||||
[AV_OPT_TYPE_CHLAYOUT] = "<channel_layout>",
|
||||
[AV_OPT_TYPE_BOOL] = "<boolean>",
|
||||
};
|
||||
const enum AVOptionType type = TYPE_BASE(o->type);
|
||||
|
||||
if (o->type == AV_OPT_TYPE_CONST && parent_type == AV_OPT_TYPE_INT)
|
||||
if (o->type == AV_OPT_TYPE_CONST && TYPE_BASE(parent_type) == AV_OPT_TYPE_INT)
|
||||
av_log(av_log_obj, AV_LOG_INFO, "%-12"PRId64" ", o->default_val.i64);
|
||||
else if (o->type < FF_ARRAY_ELEMS(desc) && desc[o->type])
|
||||
av_log(av_log_obj, AV_LOG_INFO, "%-12s ", desc[o->type]);
|
||||
else if (type < FF_ARRAY_ELEMS(desc) && desc[type]) {
|
||||
if (o->type & AV_OPT_TYPE_FLAG_ARRAY)
|
||||
av_log(av_log_obj, AV_LOG_INFO, "[%-10s]", desc[type]);
|
||||
else
|
||||
av_log(av_log_obj, AV_LOG_INFO, "%-12s ", desc[type]);
|
||||
}
|
||||
else
|
||||
av_log(av_log_obj, AV_LOG_INFO, "%-12s ", "");
|
||||
}
|
||||
@ -1274,6 +1485,13 @@ static void log_default(void *obj, void *av_log_obj, const AVOption *opt)
|
||||
!opt->default_val.str)
|
||||
return;
|
||||
|
||||
if (opt->type & AV_OPT_TYPE_FLAG_ARRAY) {
|
||||
const AVOptionArrayDef *arr = opt->default_val.arr;
|
||||
if (arr && arr->def)
|
||||
av_log(av_log_obj, AV_LOG_INFO, " (default %s)", arr->def);
|
||||
return;
|
||||
}
|
||||
|
||||
av_log(av_log_obj, AV_LOG_INFO, " (default ");
|
||||
switch (opt->type) {
|
||||
case AV_OPT_TYPE_BOOL:
|
||||
@ -1434,6 +1652,21 @@ void av_opt_set_defaults2(void *s, int mask, int flags)
|
||||
if (opt->flags & AV_OPT_FLAG_READONLY)
|
||||
continue;
|
||||
|
||||
if (opt->type & AV_OPT_TYPE_FLAG_ARRAY) {
|
||||
const AVOptionArrayDef *arr = opt->default_val.arr;
|
||||
const char sep = opt_array_sep(opt);
|
||||
|
||||
av_assert0(sep && sep != '\\' &&
|
||||
(sep < 'a' || sep > 'z') &&
|
||||
(sep < 'A' || sep > 'Z') &&
|
||||
(sep < '0' || sep > '9'));
|
||||
|
||||
if (arr && arr->def)
|
||||
opt_set_array(s, s, opt, arr->def, dst);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (opt->type) {
|
||||
case AV_OPT_TYPE_CONST:
|
||||
/* Nothing to be done here */
|
||||
@ -1676,23 +1909,12 @@ void av_opt_free(void *obj)
|
||||
{
|
||||
const AVOption *o = NULL;
|
||||
while ((o = av_opt_next(obj, o))) {
|
||||
switch (o->type) {
|
||||
case AV_OPT_TYPE_STRING:
|
||||
case AV_OPT_TYPE_BINARY:
|
||||
av_freep((uint8_t *)obj + o->offset);
|
||||
break;
|
||||
void *pitem = (uint8_t *)obj + o->offset;
|
||||
|
||||
case AV_OPT_TYPE_DICT:
|
||||
av_dict_free((AVDictionary **)(((uint8_t *)obj) + o->offset));
|
||||
break;
|
||||
|
||||
case AV_OPT_TYPE_CHLAYOUT:
|
||||
av_channel_layout_uninit((AVChannelLayout *)(((uint8_t *)obj) + o->offset));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (o->type & AV_OPT_TYPE_FLAG_ARRAY)
|
||||
opt_free_array(o, pitem, opt_array_pcount(pitem));
|
||||
else
|
||||
opt_free_elem(o, pitem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1794,7 +2016,9 @@ const AVClass *av_opt_child_class_iterate(const AVClass *parent, void **iter)
|
||||
void *av_opt_ptr(const AVClass *class, void *obj, const char *name)
|
||||
{
|
||||
const AVOption *opt= av_opt_find2(&class, name, NULL, 0, AV_OPT_SEARCH_FAKE_OBJ, NULL);
|
||||
if(!opt)
|
||||
|
||||
// no direct access to array-type options
|
||||
if (!opt || (opt->type & AV_OPT_TYPE_FLAG_ARRAY))
|
||||
return NULL;
|
||||
return (uint8_t*)obj + opt->offset;
|
||||
}
|
||||
@ -1844,6 +2068,40 @@ static int opt_copy_elem(void *logctx, enum AVOptionType type,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_copy_array(void *logctx, const AVOption *o,
|
||||
void **pdst, const void * const *psrc)
|
||||
{
|
||||
unsigned nb_elems = *opt_array_pcount(psrc);
|
||||
void *dst = NULL;
|
||||
int ret;
|
||||
|
||||
if (*pdst == *psrc) {
|
||||
*pdst = NULL;
|
||||
*opt_array_pcount(pdst) = 0;
|
||||
}
|
||||
|
||||
opt_free_array(o, pdst, opt_array_pcount(pdst));
|
||||
|
||||
dst = av_calloc(nb_elems, opt_elem_size[TYPE_BASE(o->type)]);
|
||||
if (!dst)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
for (unsigned i = 0; i < nb_elems; i++) {
|
||||
ret = opt_copy_elem(logctx, TYPE_BASE(o->type),
|
||||
opt_array_pelem(o, dst, i),
|
||||
opt_array_pelem(o, *(void**)psrc, i));
|
||||
if (ret < 0) {
|
||||
opt_free_array(o, &dst, &nb_elems);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
*pdst = dst;
|
||||
*opt_array_pcount(pdst) = nb_elems;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int av_opt_copy(void *dst, const void *src)
|
||||
{
|
||||
const AVOption *o = NULL;
|
||||
@ -1861,7 +2119,9 @@ int av_opt_copy(void *dst, const void *src)
|
||||
void *field_dst = (uint8_t *)dst + o->offset;
|
||||
void *field_src = (uint8_t *)src + o->offset;
|
||||
|
||||
int err = opt_copy_elem(dst, o->type, field_dst, field_src);
|
||||
int err = (o->type & AV_OPT_TYPE_FLAG_ARRAY) ?
|
||||
opt_copy_array(dst, o, field_dst, field_src) :
|
||||
opt_copy_elem (dst, o->type, field_dst, field_src);
|
||||
if (err < 0)
|
||||
ret = err;
|
||||
}
|
||||
@ -1990,6 +2250,24 @@ int av_opt_is_set_to_default(void *obj, const AVOption *o)
|
||||
|
||||
dst = ((uint8_t*)obj) + o->offset;
|
||||
|
||||
if (o->type & AV_OPT_TYPE_FLAG_ARRAY) {
|
||||
const char *def = o->default_val.arr ? o->default_val.arr->def : NULL;
|
||||
uint8_t *val;
|
||||
|
||||
ret = opt_get_array(o, dst, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!!val != !!def)
|
||||
ret = 0;
|
||||
else if (val)
|
||||
ret = !strcmp(val, def);
|
||||
|
||||
av_freep(&val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (o->type) {
|
||||
case AV_OPT_TYPE_CONST:
|
||||
return 1;
|
||||
|
@ -250,6 +250,17 @@ enum AVOptionType{
|
||||
AV_OPT_TYPE_COLOR,
|
||||
AV_OPT_TYPE_BOOL,
|
||||
AV_OPT_TYPE_CHLAYOUT,
|
||||
|
||||
/**
|
||||
* May be combined with another regular option type to declare an array
|
||||
* option.
|
||||
*
|
||||
* For array options, @ref AVOption.offset should refer to a pointer
|
||||
* corresponding to the option type. The pointer should be immediately
|
||||
* followed by an unsigned int that will store the number of elements in the
|
||||
* array.
|
||||
*/
|
||||
AV_OPT_TYPE_FLAG_ARRAY = (1 << 16),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -295,6 +306,40 @@ enum AVOptionType{
|
||||
*/
|
||||
#define AV_OPT_FLAG_CHILD_CONSTS (1 << 18)
|
||||
|
||||
/**
|
||||
* May be set as default_val for AV_OPT_TYPE_FLAG_ARRAY options.
|
||||
*/
|
||||
typedef struct AVOptionArrayDef {
|
||||
/**
|
||||
* Native access only.
|
||||
*
|
||||
* Default value of the option, as would be serialized by av_opt_get() (i.e.
|
||||
* using the value of sep as the separator).
|
||||
*/
|
||||
const char *def;
|
||||
|
||||
/**
|
||||
* Minimum number of elements in the array. When this field is non-zero, def
|
||||
* must be non-NULL and contain at least this number of elements.
|
||||
*/
|
||||
unsigned size_min;
|
||||
/**
|
||||
* Maximum number of elements in the array, 0 when unlimited.
|
||||
*/
|
||||
unsigned size_max;
|
||||
|
||||
/**
|
||||
* Separator between array elements in string representations of this
|
||||
* option, used by av_opt_set() and av_opt_get(). It must be a printable
|
||||
* ASCII character, excluding alphanumeric and the backslash. A comma is
|
||||
* used when sep=0.
|
||||
*
|
||||
* The separator and the backslash must be backslash-escaped in order to
|
||||
* appear in string representations of the option value.
|
||||
*/
|
||||
char sep;
|
||||
} AVOptionArrayDef;
|
||||
|
||||
/**
|
||||
* AVOption
|
||||
*/
|
||||
@ -317,8 +362,7 @@ typedef struct AVOption {
|
||||
enum AVOptionType type;
|
||||
|
||||
/**
|
||||
* Native access only.
|
||||
*
|
||||
* Native access only, except when documented otherwise.
|
||||
* the default value for scalar options
|
||||
*/
|
||||
union {
|
||||
@ -327,6 +371,14 @@ typedef struct AVOption {
|
||||
const char *str;
|
||||
/* TODO those are unused now */
|
||||
AVRational q;
|
||||
|
||||
/**
|
||||
* Used for AV_OPT_TYPE_FLAG_ARRAY options. May be NULL.
|
||||
*
|
||||
* Foreign access to some members allowed, as noted in AVOptionArrayDef
|
||||
* documentation.
|
||||
*/
|
||||
const AVOptionArrayDef *arr;
|
||||
} default_val;
|
||||
double min; ///< minimum valid value for the option
|
||||
double max; ///< maximum valid value for the option
|
||||
|
@ -57,6 +57,15 @@ typedef struct TestContext {
|
||||
int bool3;
|
||||
AVDictionary *dict1;
|
||||
AVDictionary *dict2;
|
||||
|
||||
int **array_int;
|
||||
unsigned nb_array_int;
|
||||
|
||||
char **array_str;
|
||||
unsigned nb_array_str;
|
||||
|
||||
AVDictionary **array_dict;
|
||||
unsigned nb_array_dict;
|
||||
} TestContext;
|
||||
|
||||
#define OFFSET(x) offsetof(TestContext, x)
|
||||
@ -65,6 +74,16 @@ typedef struct TestContext {
|
||||
#define TEST_FLAG_LAME 02
|
||||
#define TEST_FLAG_MU 04
|
||||
|
||||
static const AVOptionArrayDef array_str = {
|
||||
.sep = '|',
|
||||
.def = "str0|str\\|1|str\\\\2",
|
||||
};
|
||||
|
||||
static const AVOptionArrayDef array_dict = {
|
||||
// there are three levels of escaping - C string, array option, dict - so 8 backslashes are needed to get a literal one inside a dict key/val
|
||||
.def = "k00=v\\\\\\\\00:k01=v\\,01,k10=v\\\\=1\\\\:0",
|
||||
};
|
||||
|
||||
static const AVOption test_options[]= {
|
||||
{"num", "set num", OFFSET(num), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, 1 },
|
||||
{"toggle", "set toggle", OFFSET(toggle), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, 1 },
|
||||
@ -93,6 +112,9 @@ static const AVOption test_options[]= {
|
||||
{"bool3", "set boolean value", OFFSET(bool3), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, 1 },
|
||||
{"dict1", "set dictionary value", OFFSET(dict1), AV_OPT_TYPE_DICT, { .str = NULL}, 0, 0, 1 },
|
||||
{"dict2", "set dictionary value", OFFSET(dict2), AV_OPT_TYPE_DICT, { .str = "happy=':-)'"}, 0, 0, 1 },
|
||||
{"array_int", "array of ints", OFFSET(array_int), AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, .max = INT_MAX, .flags = AV_OPT_FLAG_RUNTIME_PARAM },
|
||||
{"array_str", "array of strings", OFFSET(array_str), AV_OPT_TYPE_STRING | AV_OPT_TYPE_FLAG_ARRAY, { .arr = &array_str }, .flags = AV_OPT_FLAG_RUNTIME_PARAM },
|
||||
{"array_dict", "array of dicts", OFFSET(array_dict), AV_OPT_TYPE_DICT | AV_OPT_TYPE_FLAG_ARRAY, { .arr = &array_dict }, .flags = AV_OPT_FLAG_RUNTIME_PARAM },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
@ -146,6 +168,17 @@ int main(void)
|
||||
printf("flt=%.6f\n", test_ctx.flt);
|
||||
printf("dbl=%.6f\n", test_ctx.dbl);
|
||||
|
||||
for (unsigned i = 0; i < test_ctx.nb_array_str; i++)
|
||||
printf("array_str[%u]=%s\n", i, test_ctx.array_str[i]);
|
||||
|
||||
for (unsigned i = 0; i < test_ctx.nb_array_dict; i++) {
|
||||
AVDictionary *d = test_ctx.array_dict[i];
|
||||
const AVDictionaryEntry *e = NULL;
|
||||
|
||||
while ((e = av_dict_iterate(d, e)))
|
||||
printf("array_dict[%u]: %s\t%s\n", i, e->key, e->value);
|
||||
}
|
||||
|
||||
av_opt_show2(&test_ctx, NULL, -1, 0);
|
||||
|
||||
av_opt_free(&test_ctx);
|
||||
@ -177,6 +210,9 @@ int main(void)
|
||||
TestContext test_ctx = { 0 };
|
||||
TestContext test2_ctx = { 0 };
|
||||
const AVOption *o = NULL;
|
||||
char *val = NULL;
|
||||
int ret;
|
||||
|
||||
test_ctx.class = &test_class;
|
||||
test2_ctx.class = &test_class;
|
||||
|
||||
@ -209,6 +245,17 @@ int main(void)
|
||||
av_free(value1);
|
||||
av_free(value2);
|
||||
}
|
||||
|
||||
// av_opt_set(NULL) with an array option resets it
|
||||
ret = av_opt_set(&test_ctx, "array_dict", NULL, 0);
|
||||
printf("av_opt_set(\"array_dict\", NULL) -> %d\n", ret);
|
||||
printf("array_dict=%sNULL; nb_array_dict=%u\n",
|
||||
test_ctx.array_dict ? "non-" : "", test_ctx.nb_array_dict);
|
||||
|
||||
// av_opt_get() on an empty array should return a NULL string
|
||||
ret = av_opt_get(&test_ctx, "array_dict", AV_OPT_ALLOW_NULL, (uint8_t**)&val);
|
||||
printf("av_opt_get(\"array_dict\") -> %s\n", val ? val : "NULL");
|
||||
|
||||
av_opt_free(&test_ctx);
|
||||
av_opt_free(&test2_ctx);
|
||||
}
|
||||
@ -303,6 +350,8 @@ int main(void)
|
||||
"bool1=true",
|
||||
"bool2=auto",
|
||||
"dict1='happy=\\:-):sad=\\:-('",
|
||||
"array_int=0,32,2147483647",
|
||||
"array_int=2147483648", // out of range, should fail
|
||||
};
|
||||
|
||||
test_ctx.class = &test_class;
|
||||
|
@ -79,7 +79,7 @@
|
||||
*/
|
||||
|
||||
#define LIBAVUTIL_VERSION_MAJOR 59
|
||||
#define LIBAVUTIL_VERSION_MINOR 0
|
||||
#define LIBAVUTIL_VERSION_MINOR 1
|
||||
#define LIBAVUTIL_VERSION_MICRO 100
|
||||
|
||||
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
|
||||
|
@ -17,6 +17,12 @@ binary_size=4
|
||||
num64=1
|
||||
flt=0.333333
|
||||
dbl=0.333333
|
||||
array_str[0]=str0
|
||||
array_str[1]=str|1
|
||||
array_str[2]=str\2
|
||||
array_dict[0]: k00 v\00
|
||||
array_dict[0]: k01 v,01
|
||||
array_dict[1]: k10 v=1:0
|
||||
TestContext AVOptions:
|
||||
-num <int> E.......... set num (from 0 to 100) (default 0)
|
||||
-toggle <int> E.......... set toggle (from 0 to 1) (default 1)
|
||||
@ -45,6 +51,9 @@ TestContext AVOptions:
|
||||
-bool3 <boolean> E.......... set boolean value (default false)
|
||||
-dict1 <dictionary> E.......... set dictionary value
|
||||
-dict2 <dictionary> E.......... set dictionary value (default "happy=':-)'")
|
||||
-array_int [<int> ].........T. array of ints
|
||||
-array_str [<string> ].........T. array of strings (default str0|str\|1|str\\2)
|
||||
-array_dict [<dictionary>].........T. array of dicts (default k00=v\\\\00:k01=v\,01,k10=v\\=1\\:0)
|
||||
|
||||
Testing av_opt_is_set_to_default()
|
||||
name: num default:1 error:
|
||||
@ -74,6 +83,9 @@ name: bool2 default:0 error:
|
||||
name: bool3 default:1 error:
|
||||
name: dict1 default:1 error:
|
||||
name: dict2 default:0 error:
|
||||
name: array_int default:0 error:
|
||||
name: array_str default:0 error:
|
||||
name:array_dict default:0 error:
|
||||
name: num default:1 error:
|
||||
name: toggle default:1 error:
|
||||
name: rational default:1 error:
|
||||
@ -101,6 +113,9 @@ name: bool2 default:1 error:
|
||||
name: bool3 default:1 error:
|
||||
name: dict1 default:1 error:
|
||||
name: dict2 default:1 error:
|
||||
name: array_int default:0 error:
|
||||
name: array_str default:1 error:
|
||||
name:array_dict default:1 error:
|
||||
|
||||
Testing av_opt_get/av_opt_set()
|
||||
name: num get: 0 set: OK get: 0 OK
|
||||
@ -127,9 +142,15 @@ name: bool2 get: true set: OK get: true
|
||||
name: bool3 get: false set: OK get: false OK
|
||||
name: dict1 get: set: OK get: OK
|
||||
name: dict2 get: happy=\:-) set: OK get: happy=\:-) OK
|
||||
name: array_int get: set: OK get: OK
|
||||
name: array_str get: str0|str\|1|str\\2 set: OK get: str0|str\|1|str\\2 OK
|
||||
name: array_dict get: k00=v\\\\00:k01=v\,01,k10=v\\=1\\:0 set: OK get: k00=v\\\\00:k01=v\,01,k10=v\\=1\\:0 OK
|
||||
av_opt_set("array_dict", NULL) -> 0
|
||||
array_dict=NULL; nb_array_dict=0
|
||||
av_opt_get("array_dict") -> NULL
|
||||
|
||||
Test av_opt_serialize()
|
||||
num=0,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=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false,dict1=,dict2=happy\=\\:-)
|
||||
num=0,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=1,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'
|
||||
Setting entry with key 'toggle' to value '1'
|
||||
Setting entry with key 'rational' to value '1/1'
|
||||
@ -154,7 +175,10 @@ Setting entry with key 'bool2' to value 'true'
|
||||
Setting entry with key 'bool3' to value 'false'
|
||||
Setting entry with key 'dict1' to value ''
|
||||
Setting entry with key 'dict2' to value 'happy=\:-)'
|
||||
num=0,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=1,flt=0.333333,dbl=0.333333,bool1=auto,bool2=true,bool3=false,dict1=,dict2=happy\=\\:-)
|
||||
Setting entry with key 'array_int' to value ''
|
||||
Setting entry with key 'array_str' to value 'str0|str\|1|str\\2'
|
||||
Setting entry with key 'array_dict' to value 'k00=v\\\\00:k01=v\,01,k10=v\\=1\\:0'
|
||||
num=0,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=1,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
|
||||
|
||||
Testing av_set_options_string()
|
||||
Setting options string ''
|
||||
@ -378,6 +402,13 @@ OK 'bool2=auto'
|
||||
Setting options string 'dict1='happy=\:-):sad=\:-(''
|
||||
Setting entry with key 'dict1' to value 'happy=\:-):sad=\:-('
|
||||
OK 'dict1='happy=\:-):sad=\:-(''
|
||||
Setting options string 'array_int=0,32,2147483647'
|
||||
Setting entry with key 'array_int' to value '0,32,2147483647'
|
||||
OK 'array_int=0,32,2147483647'
|
||||
Setting options string 'array_int=2147483648'
|
||||
Setting entry with key 'array_int' to value '2147483648'
|
||||
Value 2147483648.000000 for parameter 'array_int' out of range [0 - 2.14748e+09]
|
||||
Error 'array_int=2147483648'
|
||||
|
||||
Testing av_opt_set_from_string()
|
||||
Setting options string ''
|
||||
|
Loading…
Reference in New Issue
Block a user