PR #24558 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24558 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24558.patch
I was clearly drunk when I wrote this code. I have no idea how I arrived at the conclusion that this was a reasonable solution to the issue. >From 96b99edba6eb736fe8f0266611b477f275f554db Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 18 Sep 2026 14:00:30 +0200 Subject: [PATCH 1/3] avfilter/vf_limiter: resolve per-plane limits during config_input() Instead of in the slice thread. These values do not differ by slice. Signed-off-by: Niklas Haas <[email protected]> --- libavfilter/vf_limiter.c | 49 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c index 197688225e..16bcab05cc 100644 --- a/libavfilter/vf_limiter.c +++ b/libavfilter/vf_limiter.c @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" @@ -35,10 +36,11 @@ typedef struct ThreadData { typedef struct LimiterContext { const AVClass *class; - int min; - int max; + int min, max; /* user-facing option */ int planes; int nb_planes; + int plane_min[4]; /* resolved per-plane limits */ + int plane_max[4]; int linesize[4]; int width[4]; int height[4]; @@ -135,8 +137,29 @@ static int config_input(AVFilterLink *inlink) s->width[1] = s->width[2] = AV_CEIL_RSHIFT(inlink->w, hsub); s->width[0] = s->width[3] = inlink->w; - s->max = FFMIN(s->max, (1 << depth) - 1); - s->min = FFMIN(s->min, (1 << depth) - 1); + const int is_mpeg = inlink->color_range == AVCOL_RANGE_MPEG && + !(desc->flags & AV_PIX_FMT_FLAG_RGB); + + const int full_range = (1 << depth) - 1; + const int max = FFMIN(s->max, full_range); + const int min = FFMIN(s->min, full_range); + + for (int p = 0; p < s->nb_planes; p++) { + const int mpeg_min = 16 << (depth - 8); + const int mpeg_max = (p ? 240 : 235) << (depth - 8); + s->plane_min[p] = min; + s->plane_max[p] = max; + if (min < 0) + s->plane_min[p] = (is_mpeg && p != 3) ? mpeg_min : 0; + if (max < 0) + s->plane_max[p] = (is_mpeg && p != 3) ? mpeg_max : full_range; + + if (((1 << p) & s->planes) && s->plane_max[p] < s->plane_min[p]) { + av_log(ctx, AV_LOG_ERROR, "Invalid min/max values for plane %d: " + "min=%d > max=%d\n", p, s->plane_min[p], s->plane_max[p]); + return AVERROR(EINVAL); + } + } if (depth == 8) { s->dsp.limiter = limiter8; @@ -162,22 +185,13 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(in->format); const int depth = desc->comp[0].depth; const int full_range = (1 << depth) - 1; - const int is_mpeg = in->color_range == AVCOL_RANGE_MPEG && - !(desc->flags & AV_PIX_FMT_FLAG_RGB); for (p = 0; p < s->nb_planes; p++) { const int h = s->height[p]; const int slice_start = ff_slice_pos(h, jobnr, nb_jobs); const int slice_end = ff_slice_pos(h, jobnr + 1, nb_jobs); - const int mpeg_min = 16 << (depth - 8); - const int mpeg_max = (p ? 240 : 235) << (depth - 8); - - int min = s->min, max = s->max; - if (min < 0) - min = (is_mpeg && p != 3) ? mpeg_min : 0; - if (max < 0) - max = (is_mpeg && p != 3) ? mpeg_max : full_range; - + const int min = s->plane_min[p]; + const int max = s->plane_max[p]; if (!((1 << p) & s->planes) || (min == 0 && max == full_range)) { if (out != in) av_image_copy_plane(out->data[p] + slice_start * out->linesize[p], @@ -188,10 +202,7 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) continue; } - /* check only after resolving no-op planes */ - if (min > max) - return AVERROR(EINVAL); - + av_assert1(max >= min); s->dsp.limiter(in->data[p] + slice_start * in->linesize[p], out->data[p] + slice_start * out->linesize[p], in->linesize[p], out->linesize[p], -- 2.52.0 >From c29e5c86f4f3fad237a04416e0ac30d0256a2b8d Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 18 Sep 2026 14:01:21 +0200 Subject: [PATCH 2/3] avfilter/vf_limiter: drop unneeded init() This is redundant with the check in config_input(), and in particular, rejects an arguably valid configuration that applies an invalid limit to zero selected planes. Signed-off-by: Niklas Haas <[email protected]> --- libavfilter/vf_limiter.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c index 16bcab05cc..4c7b04fe27 100644 --- a/libavfilter/vf_limiter.c +++ b/libavfilter/vf_limiter.c @@ -62,15 +62,6 @@ static const AVOption limiter_options[] = { AVFILTER_DEFINE_CLASS(limiter); -static av_cold int init(AVFilterContext *ctx) -{ - LimiterContext *s = ctx->priv; - - if (s->min >= 0 && s->max >= 0 && s->min > s->max) - return AVERROR(EINVAL); - return 0; -} - static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, @@ -280,7 +271,6 @@ const FFFilter ff_vf_limiter = { .p.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .priv_size = sizeof(LimiterContext), - .init = init, FILTER_INPUTS(inputs), FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), -- 2.52.0 >From 256cd9171243e716f8e0a8c2296fe717bf2a2bcc Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 18 Sep 2026 14:02:20 +0200 Subject: [PATCH 3/3] Revert "avfilter/vf_limiter: gather per-slice return values" This reverts commit 2886aa54c566de980a37a3121b17deb4ac7b91eb. --- libavfilter/vf_limiter.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c index 4c7b04fe27..92312b1d2a 100644 --- a/libavfilter/vf_limiter.c +++ b/libavfilter/vf_limiter.c @@ -27,8 +27,6 @@ #include "limiter.h" #include "video.h" -#define MAX_THREADS 64 - typedef struct ThreadData { AVFrame *in; AVFrame *out; @@ -44,7 +42,6 @@ typedef struct LimiterContext { int linesize[4]; int width[4]; int height[4]; - int rets[MAX_THREADS]; LimiterDSPContext dsp; } LimiterContext; @@ -212,9 +209,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) ThreadData td; AVFrame *out; - const int nb_jobs = FFMIN3(ff_filter_get_nb_threads(ctx), - MAX_THREADS, s->height[2]); - if (av_frame_is_writable(in)) { out = in; } else { @@ -228,18 +222,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) td.out = out; td.in = in; - memset(s->rets, 0, sizeof(s->rets)); - ff_filter_execute(ctx, filter_slice, &td, s->rets, nb_jobs); + ff_filter_execute(ctx, filter_slice, &td, NULL, + FFMIN(s->height[2], ff_filter_get_nb_threads(ctx))); if (out != in) av_frame_free(&in); - for (int i = 0; i < nb_jobs; i++) { - if (s->rets[i] < 0) { - av_frame_free(&out); - return s->rets[i]; - } - } - return ff_filter_frame(outlink, out); } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
