1
0
mirror of https://git.ffmpeg.org/ffmpeg.git synced 2026-10-24 02:09:03 +03:00

avutil/opt: allow passing a fake object to av_opt_set()

The result will be a dry run of the process, where no value is set but all
the checks are made.
Useful for, as the AV_OPT_SEARCH_FAKE_OBJ flag states, run the relevant
process without having to allocate the corresponding object.

Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
James Almer
2025-02-16 21:17:53 -03:00
parent f49ed6235e
commit bd68a61304
2 changed files with 114 additions and 37 deletions

View File

@@ -166,18 +166,21 @@ static int opt_set_init(void *obj, const char *name, int search_flags,
int require_type,
void **ptgt, const AVOption **po, void **pdst)
{
void *logctx;
const AVOption *o;
void *tgt;
o = av_opt_find2(obj, name, NULL, 0, search_flags, &tgt);
if (!o || !tgt)
if (!o || (!tgt && !(search_flags & AV_OPT_SEARCH_FAKE_OBJ)))
return AVERROR_OPTION_NOT_FOUND;
if (o->flags & AV_OPT_FLAG_READONLY)
return AVERROR(EINVAL);
logctx = tgt ? obj : NULL;
if (require_type && (o->type != require_type)) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Tried to set option '%s' of type %s from value of type %s, "
"this is not supported\n", o->name, opt_type_desc[o->type].name,
opt_type_desc[require_type].name);
@@ -200,7 +203,7 @@ static int opt_set_init(void *obj, const char *name, int search_flags,
}
if (state_flags && (*state_flags & AV_CLASS_STATE_INITIALIZED)) {
av_log(obj, AV_LOG_ERROR, "Option '%s' is not a runtime option and "
av_log(logctx, AV_LOG_ERROR, "Option '%s' is not a runtime option and "
"so cannot be set after the object has been initialized\n",
o->name);
return AVERROR(EINVAL);
@@ -208,14 +211,14 @@ static int opt_set_init(void *obj, const char *name, int search_flags,
}
if (o->flags & AV_OPT_FLAG_DEPRECATED)
av_log(obj, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n", name, o->help);
av_log(logctx, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n", name, o->help);
if (po)
*po = o;
if (ptgt)
*ptgt = tgt;
if (pdst)
*pdst = ((uint8_t *)tgt) + o->offset;
*pdst = tgt ? ((uint8_t *)tgt) + o->offset : NULL;
return 0;
}
@@ -271,6 +274,7 @@ 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)
{
void *logctx = dst ? obj : NULL;
const enum AVOptionType type = TYPE_BASE(o->type);
if (type != AV_OPT_TYPE_FLAGS &&
@@ -283,13 +287,16 @@ static int write_number(void *obj, const AVOption *o, void *dst, double num, int
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,
av_log(logctx, AV_LOG_ERROR,
"Value %f for parameter '%s' is not a valid set of 32bit integer flags\n",
num*intnum/den, o->name);
return AVERROR(ERANGE);
}
}
if (!dst)
return 0;
switch (type) {
case AV_OPT_TYPE_PIXEL_FMT:
*(enum AVPixelFormat *)dst = llrint(num / den) * intnum;
@@ -356,12 +363,15 @@ static int hexchar2int(char c) {
static int set_string_binary(void *obj, const AVOption *o, const char *val, uint8_t **dst)
{
int *lendst = (int *)(dst + 1);
int *lendst;
uint8_t *bin, *ptr;
int len;
if (dst) {
lendst = (int *)(dst + 1);
av_freep(dst);
*lendst = 0;
}
if (!val || !(len = strlen(val)))
return 0;
@@ -382,14 +392,19 @@ static int set_string_binary(void *obj, const AVOption *o, const char *val, uint
}
*ptr++ = (a << 4) | b;
}
if (dst) {
*dst = bin;
*lendst = len;
} else
av_free(bin);
return 0;
}
static int set_string(void *obj, const AVOption *o, const char *val, uint8_t **dst)
{
if (!dst)
return 0;
av_freep(dst);
if (!val)
return 0;
@@ -408,6 +423,7 @@ 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)
{
void *logctx = dst ? obj : NULL;
const enum AVOptionType type = TYPE_BASE(o->type);
int ret = 0;
@@ -446,7 +462,7 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con
if (o_named && o_named->type == AV_OPT_TYPE_CONST) {
d = DEFAULT_NUMVAL(o_named);
if (o_named->flags & AV_OPT_FLAG_DEPRECATED)
av_log(obj, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n",
av_log(logctx, AV_LOG_WARNING, "The \"%s\" option is deprecated: %s\n",
o_named->name, o_named->help);
} else {
if (o->unit) {
@@ -455,7 +471,7 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con
o_named->unit &&
!strcmp(o_named->unit, o->unit)) {
if (ci + 6 >= FF_ARRAY_ELEMS(const_values)) {
av_log(obj, AV_LOG_ERROR, "const_values array too small for %s\n", o->unit);
av_log(logctx, AV_LOG_ERROR, "const_values array too small for %s\n", o->unit);
return AVERROR_PATCHWELCOME;
}
const_names [ci ] = o_named->name;
@@ -479,12 +495,12 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con
res = av_expr_parse_and_eval(&d, i ? buf : val, const_names,
const_values, NULL, NULL, NULL, NULL, NULL, 0, obj);
if (res < 0) {
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\"\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\"\n", o->name, val);
return res;
}
}
}
if (type == AV_OPT_TYPE_FLAGS) {
if (type == AV_OPT_TYPE_FLAGS && dst) {
intnum = *(unsigned int*)dst;
if (cmd == '+')
d = intnum | (int64_t)d;
@@ -502,37 +518,51 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con
static int set_string_image_size(void *obj, const AVOption *o, const char *val, int *dst)
{
void *logctx = dst ? obj : NULL;
int tmp[2];
int ret;
if (!val || !strcmp(val, "none")) {
if (dst) {
dst[0] =
dst[1] = 0;
}
return 0;
}
ret = av_parse_video_size(dst, dst + 1, val);
ret = av_parse_video_size(&tmp[0], &tmp[1], val);
if (ret < 0)
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as image size\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as image size\n", o->name, val);
if (dst)
memcpy(dst, tmp, sizeof(tmp));
return ret;
}
static int set_string_video_rate(void *obj, const AVOption *o, const char *val, AVRational *dst)
{
int ret = av_parse_video_rate(dst, val);
void *logctx = dst ? obj : NULL;
AVRational tmp;
int ret = av_parse_video_rate(&tmp, val);
if (ret < 0)
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as video rate\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as video rate\n", o->name, val);
if (dst)
*dst = tmp;
return ret;
}
static int set_string_color(void *obj, const AVOption *o, const char *val, uint8_t *dst)
{
void *logctx = dst ? obj : NULL;
int ret;
if (!val) {
return 0;
} else {
ret = av_parse_color(dst, val, -1, obj);
uint8_t tmp[4];
ret = av_parse_color(tmp, val, -1, obj);
if (ret < 0)
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as color\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as color\n", o->name, val);
if (dst)
memcpy(dst, tmp, sizeof(tmp));
return ret;
}
return 0;
@@ -547,6 +577,7 @@ static const char *get_bool_name(int val)
static int set_string_bool(void *obj, const AVOption *o, const char *val, int *dst)
{
void *logctx = dst ? obj : NULL;
int n;
if (!val)
@@ -568,11 +599,12 @@ static int set_string_bool(void *obj, const AVOption *o, const char *val, int *d
if (n < o->min || n > o->max)
goto fail;
if (dst)
*dst = n;
return 0;
fail:
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as boolean\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as boolean\n", o->name, val);
return AVERROR(EINVAL);
}
@@ -580,6 +612,7 @@ static int set_string_fmt(void *obj, const AVOption *o, const char *val, uint8_t
int fmt_nb, int ((*get_fmt)(const char *)), const char *desc,
enum AVOptionType type)
{
void *logctx = dst ? obj : NULL;
int fmt, min, max;
if (!val || !strcmp(val, "none")) {
@@ -590,7 +623,7 @@ static int set_string_fmt(void *obj, const AVOption *o, const char *val, uint8_t
char *tail;
fmt = strtol(val, &tail, 0);
if (*tail || (unsigned)fmt >= fmt_nb) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Unable to parse \"%s\" option value \"%s\" as %s\n", o->name, val, desc);
return AVERROR(EINVAL);
}
@@ -607,12 +640,13 @@ static int set_string_fmt(void *obj, const AVOption *o, const char *val, uint8_t
}
if (fmt < min || fmt > max) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Value %d for parameter '%s' out of %s format range [%d - %d]\n",
fmt, o->name, desc, min, max);
return AVERROR(ERANGE);
}
if (dst)
switch (type) {
case AV_OPT_TYPE_PIXEL_FMT: *(enum AVPixelFormat *)dst = fmt; break;
case AV_OPT_TYPE_SAMPLE_FMT: *(enum AVSampleFormat*)dst = fmt; break;
@@ -657,6 +691,11 @@ static int set_string_dict(void *obj, const AVOption *o, const char *val, uint8_
}
}
if (!dst) {
av_dict_free(&options);
return 0;
}
av_dict_free((AVDictionary **)dst);
*dst = (uint8_t *)options;
@@ -666,16 +705,27 @@ static int set_string_dict(void *obj, const AVOption *o, const char *val, uint8_
static int set_string_channel_layout(void *obj, const AVOption *o,
const char *val, void *dst)
{
AVChannelLayout tmp;
AVChannelLayout *channel_layout = dst;
int ret;
if (dst)
av_channel_layout_uninit(channel_layout);
else
channel_layout = &tmp;
if (!val)
return 0;
return av_channel_layout_from_string(channel_layout, val);
ret = av_channel_layout_from_string(channel_layout, val);
if (ret < 0)
return ret;
if (!dst)
av_channel_layout_uninit(channel_layout);
return 0;
}
static int opt_set_elem(void *obj, void *target_obj, const AVOption *o,
const char *val, void *dst)
{
void *logctx = dst ? obj : NULL;
const enum AVOptionType type = TYPE_BASE(o->type);
int ret;
@@ -720,15 +770,16 @@ static int opt_set_elem(void *obj, void *target_obj, const AVOption *o,
int64_t usecs = 0;
if (val) {
if ((ret = av_parse_time(&usecs, val, 1)) < 0) {
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as duration\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as duration\n", o->name, val);
return ret;
}
}
if (usecs < o->min || usecs > o->max) {
av_log(obj, AV_LOG_ERROR, "Value %f for parameter '%s' out of range [%g - %g]\n",
av_log(logctx, AV_LOG_ERROR, "Value %f for parameter '%s' out of range [%g - %g]\n",
usecs / 1000000.0, o->name, o->min / 1000000.0, o->max / 1000000.0);
return AVERROR(ERANGE);
}
if (dst)
*(int64_t *)dst = usecs;
return 0;
}
@@ -737,7 +788,7 @@ static int opt_set_elem(void *obj, void *target_obj, const AVOption *o,
case AV_OPT_TYPE_CHLAYOUT:
ret = set_string_channel_layout(obj, o, val, dst);
if (ret < 0) {
av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as channel layout\n", o->name, val);
av_log(logctx, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as channel layout\n", o->name, val);
ret = AVERROR(EINVAL);
}
return ret;
@@ -745,7 +796,7 @@ static int opt_set_elem(void *obj, void *target_obj, const AVOption *o,
return set_string_dict(obj, o, val, dst);
}
av_log(obj, AV_LOG_ERROR, "Invalid option type.\n");
av_log(logctx, AV_LOG_ERROR, "Invalid option type.\n");
return AVERROR(EINVAL);
}
@@ -757,6 +808,7 @@ static int opt_set_array(void *obj, void *target_obj, const AVOption *o,
const uint8_t sep = opt_array_sep(o);
uint8_t *str = NULL;
void *logctx = dst ? obj : NULL;
void *elems = NULL;
unsigned nb_elems = 0;
int ret;
@@ -773,7 +825,7 @@ static int opt_set_array(void *obj, void *target_obj, const AVOption *o,
void *tmp;
if (arr && arr->size_max && nb_elems >= arr->size_max) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Cannot assign more than %u elements to array option %s\n",
arr->size_max, o->name);
ret = AVERROR(EINVAL);
@@ -808,18 +860,22 @@ static int opt_set_array(void *obj, void *target_obj, const AVOption *o,
}
av_freep(&str);
if (dst)
opt_free_array(o, dst, opt_array_pcount(dst));
if (arr && nb_elems < arr->size_min) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Cannot assign fewer than %u elements to array option %s\n",
arr->size_min, o->name);
ret = AVERROR(EINVAL);
goto fail;
}
if (dst) {
*((void **)dst) = elems;
*opt_array_pcount(dst) = nb_elems;
} else
opt_free_array(o, &elems, &nb_elems);
return 0;
fail:
@@ -869,8 +925,10 @@ static int set_number(void *obj, const char *name, double num, int den, int64_t
ret = opt_set_init(obj, name, search_flags, require_type, NULL, &o, &dst);
if (ret < 0)
return ret;
if (dst)
ret = write_number(obj, o, dst, num, den, intnum);
return write_number(obj, o, dst, num, den, intnum);
return ret;
}
int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
@@ -900,6 +958,7 @@ int av_opt_set_bin(void *obj, const char *name, const uint8_t *val, int len, int
if (ret < 0)
return ret;
if (dst) {
ptr = len ? av_malloc(len) : NULL;
if (len && !ptr)
return AVERROR(ENOMEM);
@@ -911,6 +970,7 @@ int av_opt_set_bin(void *obj, const char *name, const uint8_t *val, int len, int
*lendst = len;
if (len)
memcpy(ptr, val, len);
}
return 0;
}
@@ -918,6 +978,7 @@ int av_opt_set_bin(void *obj, const char *name, const uint8_t *val, int len, int
int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags)
{
const AVOption *o;
void *logctx;
int *dst;
int ret;
@@ -926,14 +987,18 @@ int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_
if (ret < 0)
return ret;
logctx = dst ? obj : NULL;
if (w<0 || h<0) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Invalid negative size value %dx%d for size '%s'\n", w, h, o->name);
return AVERROR(EINVAL);
}
if (dst) {
dst[0] = w;
dst[1] = h;
}
return 0;
}
@@ -947,6 +1012,7 @@ static int set_format(void *obj, const char *name, int fmt, int search_flags,
enum AVOptionType type, const char *desc, int nb_fmts)
{
const AVOption *o;
void *logctx;
int *dst;
int min, max, ret;
@@ -954,15 +1020,18 @@ static int set_format(void *obj, const char *name, int fmt, int search_flags,
if (ret < 0)
return ret;
logctx = dst ? obj : NULL;
min = FFMAX(o->min, -1);
max = FFMIN(o->max, nb_fmts-1);
if (fmt < min || fmt > max) {
av_log(obj, AV_LOG_ERROR,
av_log(logctx, AV_LOG_ERROR,
"Value %d for parameter '%s' out of %s format range [%d - %d]\n",
fmt, name, desc, min, max);
return AVERROR(ERANGE);
}
if (dst)
*dst = fmt;
return 0;
}
@@ -988,9 +1057,13 @@ int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val,
if (ret < 0)
return ret;
if (dst) {
av_dict_free(dst);
return av_dict_copy(dst, val, 0);
ret = av_dict_copy(dst, val, 0);
}
return ret;
}
int av_opt_set_chlayout(void *obj, const char *name,
@@ -1005,7 +1078,10 @@ int av_opt_set_chlayout(void *obj, const char *name,
if (ret < 0)
return ret;
return av_channel_layout_copy(dst, channel_layout);
if (dst)
ret = av_channel_layout_copy(dst, channel_layout);
return ret;
}
static void format_duration(char *buf, size_t size, int64_t d)

View File

@@ -604,10 +604,10 @@ const AVClass *av_opt_child_class_iterate(const AVClass *parent, void **iter);
#define AV_OPT_SEARCH_CHILDREN (1 << 0) /**< Search in possible children of the
given object first. */
/**
* The obj passed to av_opt_find() is fake -- only a double pointer to AVClass
* instead of a required pointer to a struct containing AVClass. This is
* useful for searching for options without needing to allocate the corresponding
* object.
* The obj passed to av_opt_find() or av_opt_set() is fake -- only a double pointer
* to AVClass instead of a required pointer to a struct containing AVClass. This is
* useful for searching for options or verifying they can be applied without needing
* to allocate the corresponding object.
*/
#define AV_OPT_SEARCH_FAKE_OBJ (1 << 1)
@@ -841,7 +841,8 @@ int av_opt_copy(void *dest, const void *src);
* @{
* Those functions set the field of obj with the given name to value.
*
* @param[in] obj A struct whose first element is a pointer to an AVClass.
* @param[in] obj A struct whose first element is a pointer to an AVClass. Alternatively
* a double pointer to an AVClass, if AV_OPT_SEARCH_FAKE_OBJ search flag is set.
* @param[in] name the name of the field to set
* @param[in] val The value to set. In case of av_opt_set() if the field is not
* of a string type, then the given string is parsed.