|
|
|
@ -47,6 +47,7 @@ typedef struct BlendContext { |
|
|
|
|
FilterParams params[4]; |
|
|
|
|
int tblend; |
|
|
|
|
AVFrame *prev_frame; /* only used with tblend */ |
|
|
|
|
int nb_threads; |
|
|
|
|
} BlendContext; |
|
|
|
|
|
|
|
|
|
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; \
|
|
|
|
|
int starty = sliceparam->starty; \
|
|
|
|
|
type *dst = (type*)_dst; \
|
|
|
|
|
AVExpr *e = param->e; \
|
|
|
|
|
AVExpr *e = sliceparam->e; \
|
|
|
|
|
int y, x; \
|
|
|
|
|
dst_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]; |
|
|
|
|
uint8_t *dst = td->dst->data[td->plane]; |
|
|
|
|
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_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 }; |
|
|
|
|
|
|
|
|
|
ff_filter_execute(ctx, filter_slice, &td, NULL, |
|
|
|
|
FFMIN(outh, ff_filter_get_nb_threads(ctx))); |
|
|
|
|
FFMIN(outh, s->nb_threads)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!s->tblend) |
|
|
|
@ -250,6 +251,7 @@ static av_cold int init(AVFilterContext *ctx) |
|
|
|
|
BlendContext *s = ctx->priv; |
|
|
|
|
|
|
|
|
|
s->tblend = !strcmp(ctx->filter->name, "tblend"); |
|
|
|
|
s->nb_threads = ff_filter_get_nb_threads(ctx); |
|
|
|
|
|
|
|
|
|
s->fs.on_event = blend_frame_for_dualinput; |
|
|
|
|
return 0; |
|
|
|
@ -284,8 +286,14 @@ static av_cold void uninit(AVFilterContext *ctx) |
|
|
|
|
ff_framesync_uninit(&s->fs); |
|
|
|
|
av_frame_free(&s->prev_frame); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(s->params); i++) |
|
|
|
|
av_expr_free(s->params[i].e); |
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(s->params); i++) { |
|
|
|
|
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) |
|
|
|
@ -309,10 +317,19 @@ static int config_params(AVFilterContext *ctx) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
if (param->expr_str) { |
|
|
|
|
ret = av_expr_parse(¶m->e, param->expr_str, var_names, |
|
|
|
|
NULL, NULL, NULL, NULL, 0, ctx); |
|
|
|
|
if (ret < 0) |
|
|
|
|
return ret; |
|
|
|
|
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(¶m->e[i], param->expr_str, var_names, |
|
|
|
|
NULL, NULL, NULL, NULL, 0, ctx); |
|
|
|
|
if (ret < 0) |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
param->blend = s->depth > 8 ? s->depth > 16 ? blend_expr_32bit : blend_expr_16bit : blend_expr_8bit; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|