avfilter/blend: use a per-thread AVExpr

Otherwise expression state is accessed and changed from multiple threads.

Fixes ticket #10987.

Signed-off-by: Marton Balint <cus@passwd.hu>
Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
Marton Balint 2024-05-09 00:07:26 +02:00 committed by Paul B Mahol
parent a43487a25b
commit 63b97cfe94
2 changed files with 28 additions and 10 deletions

View File

@ -72,12 +72,13 @@ enum BlendMode {
typedef struct SliceParams { typedef struct SliceParams {
double *values; double *values;
int starty; int starty;
AVExpr *e;
} SliceParams; } SliceParams;
typedef struct FilterParams { typedef struct FilterParams {
enum BlendMode mode; enum BlendMode mode;
double opacity; double opacity;
AVExpr *e; AVExpr **e;
char *expr_str; char *expr_str;
void (*blend)(const uint8_t *top, ptrdiff_t top_linesize, void (*blend)(const uint8_t *top, ptrdiff_t top_linesize,
const uint8_t *bottom, ptrdiff_t bottom_linesize, const uint8_t *bottom, ptrdiff_t bottom_linesize,

View File

@ -47,6 +47,7 @@ typedef struct BlendContext {
FilterParams params[4]; FilterParams params[4];
int tblend; int tblend;
AVFrame *prev_frame; /* only used with tblend */ AVFrame *prev_frame; /* only used with tblend */
int nb_threads;
} BlendContext; } BlendContext;
static const char *const var_names[] = { "X", "Y", "W", "H", "SW", "SH", "T", "N", "A", "B", "TOP", "BOTTOM", NULL }; static const char *const var_names[] = { "X", "Y", "W", "H", "SW", "SH", "T", "N", "A", "B", "TOP", "BOTTOM", NULL };
@ -139,7 +140,7 @@ static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize,
double *values = sliceparam->values; \ double *values = sliceparam->values; \
int starty = sliceparam->starty; \ int starty = sliceparam->starty; \
type *dst = (type*)_dst; \ type *dst = (type*)_dst; \
AVExpr *e = param->e; \ AVExpr *e = sliceparam->e; \
int y, x; \ int y, x; \
dst_linesize /= div; \ dst_linesize /= div; \
top_linesize /= div; \ top_linesize /= div; \
@ -173,7 +174,7 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
const uint8_t *bottom = td->bottom->data[td->plane]; const uint8_t *bottom = td->bottom->data[td->plane];
uint8_t *dst = td->dst->data[td->plane]; uint8_t *dst = td->dst->data[td->plane];
double values[VAR_VARS_NB]; double values[VAR_VARS_NB];
SliceParams sliceparam = {.values = &values[0], .starty = slice_start}; SliceParams sliceparam = {.values = &values[0], .starty = slice_start, .e = td->param->e ? td->param->e[jobnr] : NULL};
values[VAR_N] = td->inlink->frame_count_out; values[VAR_N] = td->inlink->frame_count_out;
values[VAR_T] = td->dst->pts == AV_NOPTS_VALUE ? NAN : td->dst->pts * av_q2d(td->inlink->time_base); values[VAR_T] = td->dst->pts == AV_NOPTS_VALUE ? NAN : td->dst->pts * av_q2d(td->inlink->time_base);
@ -221,7 +222,7 @@ static AVFrame *blend_frame(AVFilterContext *ctx, AVFrame *top_buf,
.inlink = inlink }; .inlink = inlink };
ff_filter_execute(ctx, filter_slice, &td, NULL, ff_filter_execute(ctx, filter_slice, &td, NULL,
FFMIN(outh, ff_filter_get_nb_threads(ctx))); FFMIN(outh, s->nb_threads));
} }
if (!s->tblend) if (!s->tblend)
@ -250,6 +251,7 @@ static av_cold int init(AVFilterContext *ctx)
BlendContext *s = ctx->priv; BlendContext *s = ctx->priv;
s->tblend = !strcmp(ctx->filter->name, "tblend"); s->tblend = !strcmp(ctx->filter->name, "tblend");
s->nb_threads = ff_filter_get_nb_threads(ctx);
s->fs.on_event = blend_frame_for_dualinput; s->fs.on_event = blend_frame_for_dualinput;
return 0; return 0;
@ -284,8 +286,14 @@ static av_cold void uninit(AVFilterContext *ctx)
ff_framesync_uninit(&s->fs); ff_framesync_uninit(&s->fs);
av_frame_free(&s->prev_frame); av_frame_free(&s->prev_frame);
for (i = 0; i < FF_ARRAY_ELEMS(s->params); i++) for (i = 0; i < FF_ARRAY_ELEMS(s->params); i++) {
av_expr_free(s->params[i].e); if (s->params[i].e) {
for (int j = 0; j < s->nb_threads; j++)
av_expr_free(s->params[i].e[j]);
av_freep(&s->params[i].e);
}
}
} }
static int config_params(AVFilterContext *ctx) static int config_params(AVFilterContext *ctx)
@ -309,10 +317,19 @@ static int config_params(AVFilterContext *ctx)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
} }
if (param->expr_str) { if (param->expr_str) {
ret = av_expr_parse(&param->e, param->expr_str, var_names, if (!param->e) {
param->e = av_calloc(s->nb_threads, sizeof(*param->e));
if (!param->e)
return AVERROR(ENOMEM);
}
for (int i = 0; i < s->nb_threads; i++) {
av_expr_free(param->e[i]);
param->e[i] = NULL;
ret = av_expr_parse(&param->e[i], param->expr_str, var_names,
NULL, NULL, NULL, NULL, 0, ctx); NULL, NULL, NULL, NULL, 0, ctx);
if (ret < 0) if (ret < 0)
return ret; return ret;
}
param->blend = s->depth > 8 ? s->depth > 16 ? blend_expr_32bit : blend_expr_16bit : blend_expr_8bit; param->blend = s->depth > 8 ? s->depth > 16 ? blend_expr_32bit : blend_expr_16bit : blend_expr_8bit;
} }
} }