PR #24349 opened by Niklas Haas (haasn)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24349
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24349.patch

This is actually the only way currently of setting a different level for
the Y and UV planes, which matters as this filter *requires* a plane copy
for passthrough planes.

I'm tempted to just make "auto" the default, but it would possibly regress
existing users, so I decided not to touch it for now.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <[email protected]>

# Summary of changes

Briefly describe what this PR does and why.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From 8de5f81bdd6f9267eede823aea91fd57b68eff11 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Wed, 2 Sep 2026 12:59:06 +0200
Subject: [PATCH] avfilter/vf_limiter: allow min/max=auto to infer TV signal
 range

This is actually the only way currently of setting a different level for
the Y and UV planes, which matters as this filter *requires* a plane copy
for passthrough planes.

I'm tempted to just make "auto" the default, but it would possibly regress
existing users, so I decided not to touch it for now.

Sponsored-by: nxtedition AB
Signed-off-by: Niklas Haas <[email protected]>
---
 doc/filters.texi         |  8 ++++++--
 libavfilter/vf_limiter.c | 21 ++++++++++++++++++---
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index b7379d3060..6efaac71f6 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -17844,10 +17844,14 @@ The filter accepts the following options:
 
 @table @option
 @item min
-Lower bound. Defaults to the lowest allowed value for the input.
+Lower bound. Defaults to the lowest representable value for the input.
+Set to @var{auto} to use the minimum signal level for the input colorspace
+(only affects TV-range YUV signals).
 
 @item max
-Upper bound. Defaults to the highest allowed value for the input.
+Upper bound. Defaults to the highest representable value for the input.
+Set to @var{auto} to use the maximum signal level for the input colorspace
+(only affects TV-range YUV signals).
 
 @item planes
 Specify which planes will be processed. Defaults to all available.
diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c
index 68e7e9d98d..7b0bede054 100644
--- a/libavfilter/vf_limiter.c
+++ b/libavfilter/vf_limiter.c
@@ -48,8 +48,9 @@ typedef struct LimiterContext {
 #define FLAGS 
AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
 
 static const AVOption limiter_options[] = {
-    { "min",    "set min value", OFFSET(min),    AV_OPT_TYPE_INT, {.i64=0},    
 0, 65535, .flags = FLAGS },
-    { "max",    "set max value", OFFSET(max),    AV_OPT_TYPE_INT, 
{.i64=65535}, 0, 65535, .flags = FLAGS },
+    { "min",    "set min value", OFFSET(min),    AV_OPT_TYPE_INT, {.i64=0},    
 -1, 65535, .flags = FLAGS, .unit = "value" },
+    { "max",    "set max value", OFFSET(max),    AV_OPT_TYPE_INT, 
{.i64=65535}, -1, 65535, .flags = FLAGS, .unit = "value" },
+        { "auto", "automatically use tagged signal range", 0, 
AV_OPT_TYPE_CONST, {.i64=-1}, .flags = FLAGS, .unit = "value" },
     { "planes", "set planes",    OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15},   
 0,    15, .flags = FLAGS },
     { NULL }
 };
@@ -155,10 +156,24 @@ static int filter_slice(AVFilterContext *ctx, void *arg, 
int jobnr, int nb_jobs)
     AVFrame *out = td->out;
     int p;
 
+    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_full = in->color_range == AVCOL_RANGE_JPEG ||
+                        (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_full || p == 3) ? 0 : mpeg_min;
+        if (max < 0)
+            max = (is_full || p == 3) ? full_range : mpeg_max;
 
         if (!((1 << p) & s->planes)) {
             if (out != in)
@@ -174,7 +189,7 @@ static int filter_slice(AVFilterContext *ctx, void *arg, 
int jobnr, int nb_jobs)
                        out->data[p] + slice_start * out->linesize[p],
                        in->linesize[p], out->linesize[p],
                        s->width[p], slice_end - slice_start,
-                       s->min, s->max);
+                       min, max);
     }
 
     return 0;
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to