From 05721c5df8595f086667a638b13d3fc39684bc21 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Thu, 10 Nov 2022 14:37:06 +0100 Subject: avfilter/vf_thumbnail: add slice threading support --- libavfilter/vf_thumbnail.c | 79 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/libavfilter/vf_thumbnail.c b/libavfilter/vf_thumbnail.c index fb67d4fdbd..0622e3706e 100644 --- a/libavfilter/vf_thumbnail.c +++ b/libavfilter/vf_thumbnail.c @@ -46,6 +46,9 @@ typedef struct ThumbContext { struct thumb_frame *frames; ///< the n_frames frames AVRational tb; ///< copy of the input timebase to ease access + int nb_threads; + int *thread_histogram; + int planewidth[4]; int planeheight[4]; } ThumbContext; @@ -132,24 +135,24 @@ static AVFrame *get_best_frame(AVFilterContext *ctx) return picref; } -static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +static int do_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { - int i, j; - AVFilterContext *ctx = inlink->dst; - ThumbContext *s = ctx->priv; - AVFilterLink *outlink = ctx->outputs[0]; - int *hist = s->frames[s->n].histogram; - const uint8_t *p = frame->data[0]; + ThumbContext *s = ctx->priv; + AVFrame *frame = arg; + int *hist = s->thread_histogram + HIST_SIZE * jobnr; + const int h = frame->height; + const int w = frame->width; + const int slice_start = (h * jobnr) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; + const uint8_t *p = frame->data[0] + slice_start * frame->linesize[0]; - // keep a reference of each frame - s->frames[s->n].buf = frame; + memset(hist, 0, sizeof(*hist) * HIST_SIZE); - // update current frame histogram - switch (inlink->format) { + switch (frame->format) { case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24: - for (j = 0; j < inlink->h; j++) { - for (i = 0; i < inlink->w; i++) { + for (int j = slice_start; j < slice_end; j++) { + for (int i = 0; i < w; i++) { hist[0*256 + p[i*3 ]]++; hist[1*256 + p[i*3 + 1]]++; hist[2*256 + p[i*3 + 2]]++; @@ -161,8 +164,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) case AV_PIX_FMT_BGR0: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_BGRA: - for (j = 0; j < inlink->h; j++) { - for (i = 0; i < inlink->w; i++) { + for (int j = slice_start; j < slice_end; j++) { + for (int i = 0; i < w; i++) { hist[0*256 + p[i*4 ]]++; hist[1*256 + p[i*4 + 1]]++; hist[2*256 + p[i*4 + 2]]++; @@ -174,8 +177,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) case AV_PIX_FMT_0BGR: case AV_PIX_FMT_ARGB: case AV_PIX_FMT_ABGR: - for (j = 0; j < inlink->h; j++) { - for (i = 0; i < inlink->w; i++) { + for (int j = slice_start; j < slice_end; j++) { + for (int i = 0; i < w; i++) { hist[0*256 + p[i*4 + 1]]++; hist[1*256 + p[i*4 + 2]]++; hist[2*256 + p[i*4 + 3]]++; @@ -185,9 +188,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) break; default: for (int plane = 0; plane < 3; plane++) { - const uint8_t *p = frame->data[plane]; - for (j = 0; j < s->planeheight[plane]; j++) { - for (i = 0; i < s->planewidth[plane]; i++) + const int slice_start = (s->planeheight[plane] * jobnr) / nb_jobs; + const int slice_end = (s->planeheight[plane] * (jobnr+1)) / nb_jobs; + const uint8_t *p = frame->data[plane] + slice_start * frame->linesize[plane]; + + for (int j = slice_start; j < slice_end; j++) { + for (int i = 0; i < s->planewidth[plane]; i++) hist[256*plane + p[i]]++; p += frame->linesize[plane]; } @@ -195,6 +201,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) break; } + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + ThumbContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int *hist = s->frames[s->n].histogram; + + // keep a reference of each frame + s->frames[s->n].buf = frame; + + ff_filter_execute(ctx, do_slice, frame, NULL, + FFMIN(frame->height, s->nb_threads)); + + // update current frame histogram + for (int j = 0; j < FFMIN(frame->height, s->nb_threads); j++) { + int *thread_histogram = s->thread_histogram + HIST_SIZE * j; + + for (int i = 0; i < HIST_SIZE; i++) + hist[i] += thread_histogram[i]; + } + // no selection until the buffer of N frames is filled up s->n++; if (s->n < s->n_frames) @@ -210,6 +240,7 @@ static av_cold void uninit(AVFilterContext *ctx) for (i = 0; i < s->n_frames && s->frames && s->frames[i].buf; i++) av_frame_free(&s->frames[i].buf); av_freep(&s->frames); + av_freep(&s->thread_histogram); } static int request_frame(AVFilterLink *link) @@ -235,6 +266,11 @@ static int config_props(AVFilterLink *inlink) ThumbContext *s = ctx->priv; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + s->nb_threads = ff_filter_get_nb_threads(ctx); + s->thread_histogram = av_calloc(HIST_SIZE, s->nb_threads * sizeof(*s->thread_histogram)); + if (!s->thread_histogram) + return AVERROR(ENOMEM); + s->tb = inlink->time_base; s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); s->planewidth[0] = s->planewidth[3] = inlink->w; @@ -288,5 +324,6 @@ const AVFilter ff_vf_thumbnail = { FILTER_OUTPUTS(thumbnail_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &thumbnail_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | + AVFILTER_FLAG_SLICE_THREADS, }; -- cgit v1.2.3