Commit 40a836bbb8 for ffmpeg
commit 40a836bbb86532990e3ec7cec143a48b0dc0b3f7
Author: Niklas Haas <git@haasn.dev>
Date: Fri Sep 18 14:00:30 2026 +0200
avfilter/vf_limiter: resolve per-plane limits once per frame
Instead of in the slice thread. These values do not differ by slice.
We do it per-frame, rather than in config_input(), to respect dynamically
changing filter settings.
Signed-off-by: Niklas Haas <git@haasn.dev>
diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c
index 197688225e..19dbcfd30e 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,9 +137,6 @@ 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);
-
if (depth == 8) {
s->dsp.limiter = limiter8;
} else {
@@ -162,22 +161,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 +178,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],
@@ -210,6 +197,33 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
ThreadData td;
AVFrame *out;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(in->format);
+ const int is_mpeg = in->color_range == AVCOL_RANGE_MPEG &&
+ !(desc->flags & AV_PIX_FMT_FLAG_RGB);
+
+ const int depth = desc->comp[0].depth;
+ 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]);
+ av_frame_free(&in);
+ return AVERROR(EINVAL);
+ }
+ }
+
const int nb_jobs = FFMIN3(ff_filter_get_nb_threads(ctx),
MAX_THREADS, s->height[2]);