From c5e3efa9824b85a3ff0db72c12d78fd1d1b74ac1 Mon Sep 17 00:00:00 2001 From: Raja Rathour Date: Sun, 21 Dec 2025 14:28:25 +0530 Subject: [PATCH] Signed-off-by: Raja Rathour avfilter/vf_alphamerge: add slice threading Move the main rendering logic into a slice function to enable multi-threading support. Benchmark (4K Video): Single-Threaded: 2.41s Multi-Threaded: 2.25s --- libavfilter/vf_alphamerge.c | 67 +++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/libavfilter/vf_alphamerge.c b/libavfilter/vf_alphamerge.c index c17a647dbe..cf959a5eec 100644 --- a/libavfilter/vf_alphamerge.c +++ b/libavfilter/vf_alphamerge.c @@ -37,6 +37,11 @@ enum { Y, U, V, A }; +typedef struct ThreadData { + AVFrame *main_buf; + AVFrame *alpha_buf; +} ThreadData; + typedef struct AlphaMergeContext { const AVClass *class; @@ -46,12 +51,47 @@ typedef struct AlphaMergeContext { FFFrameSync fs; } AlphaMergeContext; +static int alphamerge_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AlphaMergeContext *s = ctx->priv; // <--- THIS WAS MISSING + ThreadData *td = arg; + AVFrame *main_buf = td->main_buf; + AVFrame *alpha_buf = td->alpha_buf; + int slice_start = (main_buf->height * jobnr) / nb_jobs; + int slice_end = (main_buf->height * (jobnr+1)) / nb_jobs; + int y; + + if (s->is_packed_rgb) { + int x; + uint8_t *pin, *pout; + for (y = slice_start; y < slice_end; y++) { + pin = alpha_buf->data[0] + y * alpha_buf->linesize[0]; + pout = main_buf->data[0] + y * main_buf->linesize[0] + s->rgba_map[A]; + for (x = 0; x < main_buf->width; x++) { + *pout = *pin; + pin += 1; + pout += 4; + } + } + } else { + const int main_linesize = main_buf->linesize[A]; + const int alpha_linesize = alpha_buf->linesize[Y]; + av_image_copy_plane(main_buf->data[A] + slice_start * main_linesize, + main_linesize, + alpha_buf->data[Y] + slice_start * alpha_linesize, + alpha_linesize, + FFMIN(main_linesize, alpha_linesize), + slice_end - slice_start); + } + return 0; +} + static int do_alphamerge(FFFrameSync *fs) { AVFilterContext *ctx = fs->parent; AVFilterLink *outlink = ctx->outputs[0]; - AlphaMergeContext *s = ctx->priv; AVFrame *main_buf, *alpha_buf; + ThreadData td; int ret; ret = ff_framesync_dualinput_get_writable(fs, &main_buf, &alpha_buf); @@ -67,25 +107,10 @@ static int do_alphamerge(FFFrameSync *fs) av_color_range_name(alpha_buf->color_range)); } - if (s->is_packed_rgb) { - int x, y; - uint8_t *pin, *pout; - for (y = 0; y < main_buf->height; y++) { - pin = alpha_buf->data[0] + y * alpha_buf->linesize[0]; - pout = main_buf->data[0] + y * main_buf->linesize[0] + s->rgba_map[A]; - for (x = 0; x < main_buf->width; x++) { - *pout = *pin; - pin += 1; - pout += 4; - } - } - } else { - const int main_linesize = main_buf->linesize[A]; - const int alpha_linesize = alpha_buf->linesize[Y]; - av_image_copy_plane(main_buf->data[A], main_linesize, - alpha_buf->data[Y], alpha_linesize, - FFMIN(main_linesize, alpha_linesize), alpha_buf->height); - } + td.main_buf = main_buf; + td.alpha_buf = alpha_buf; + ff_filter_execute(ctx, alphamerge_slice, &td, NULL, + FFMIN(main_buf->height, ff_filter_get_nb_threads(ctx))); return ff_filter_frame(ctx->outputs[0], main_buf); } @@ -210,7 +235,7 @@ const FFFilter ff_vf_alphamerge = { .p.description = NULL_IF_CONFIG_SMALL("Copy the luma value of the second " "input into the alpha channel of the first input."), .p.priv_class = &alphamerge_class, - .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .preinit = alphamerge_framesync_preinit, .priv_size = sizeof(AlphaMergeContext), .init = init, -- 2.48.1