PR #25448 opened by jackboykin
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25448
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25448.patch

crop breaks hardware frames that already carry a crop. mpv runs into this with 
a Matroska pixel-crop and hwdec:

```
mpv --hwdec=vulkan --vf='crop=ih*2/3:ih' pixel-cropped.mkv
Ignoring invalid --video-crop= for 1920x1080 image
```

Two crop filters in a row on hardware frames fail the same way.

The fix treats the visible part of a hardware frame as the input, the same way 
software frames and ffmpeg's container cropping already work.


>From 86a6e15d45802dab5f0e3a759b1fbe5ff8550a8b Mon Sep 17 00:00:00 2001
From: Jack Boykin <[email protected]>
Date: Sun, 25 Oct 2026 09:37:00 -0500
Subject: [PATCH] avfilter/vf_crop: respect the incoming crop of hardware
 frames

A hardware frame that arrived already cropped left with negative
margins, because the incoming bottom and right crop were subtracted
twice.

Crop the visible part instead. iw, ih, x and y refer to it.

Fixes: f1b359aaf5 ("vf_crop: Add support for cropping hardware frames")
---
 libavfilter/vf_crop.c | 50 ++++++++++++++++++++++++-------------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c
index f25e0564aa7..4b7373e124a 100644
--- a/libavfilter/vf_crop.c
+++ b/libavfilter/vf_crop.c
@@ -125,7 +125,7 @@ static inline int normalize_double(int *n, double d)
     return ret;
 }
 
-static int config_crop_area(AVFilterLink *link)
+static int config_crop_area(AVFilterLink *link, int in_w, int in_h)
 {
     AVFilterContext *ctx = link->dst;
     CropContext *s = ctx->priv;
@@ -134,9 +134,9 @@ static int config_crop_area(AVFilterLink *link)
     const char *expr;
     double res;
 
-    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = ctx->inputs[0]->w;
-    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = ctx->inputs[0]->h;
-    s->var_values[VAR_A]     = (float) link->w / link->h;
+    s->var_values[VAR_IN_W]  = s->var_values[VAR_IW] = in_w;
+    s->var_values[VAR_IN_H]  = s->var_values[VAR_IH] = in_h;
+    s->var_values[VAR_A]     = (float) in_w / in_h;
     s->var_values[VAR_SAR]   = link->sample_aspect_ratio.num ? 
av_q2d(link->sample_aspect_ratio) : 1;
     s->var_values[VAR_DAR]   = s->var_values[VAR_A] * s->var_values[VAR_SAR];
     s->var_values[VAR_HSUB]  = 1<<pix_desc->log2_chroma_w;
@@ -188,18 +188,18 @@ static int config_crop_area(AVFilterLink *link)
 
     if (s->keep_aspect) {
         AVRational dar = av_mul_q(link->sample_aspect_ratio,
-                                  (AVRational){ link->w, link->h });
+                                  (AVRational){ in_w, in_h });
         av_reduce(&s->out_sar.num, &s->out_sar.den,
                   (int64_t)dar.num * s->h, (int64_t)dar.den * s->w, INT_MAX);
     } else
         s->out_sar = link->sample_aspect_ratio;
 
     av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d sar:%d/%d -> w:%d h:%d sar:%d/%d\n",
-           link->w, link->h, link->sample_aspect_ratio.num, 
link->sample_aspect_ratio.den,
+           in_w, in_h, link->sample_aspect_ratio.num, 
link->sample_aspect_ratio.den,
            s->w, s->h, s->out_sar.num, s->out_sar.den);
 
     if (s->w <= 0 || s->h <= 0 ||
-        s->w > link->w || s->h > link->h) {
+        s->w > in_w || s->h > in_h) {
         av_log(ctx, AV_LOG_ERROR,
                "Invalid too big or non positive size for width '%d' or height 
'%d'\n",
                s->w, s->h);
@@ -207,14 +207,14 @@ static int config_crop_area(AVFilterLink *link)
     }
 
     /* set default, required in the case the first computed value for x/y is 
NAN */
-    s->x = (link->w - s->w) / 2;
-    s->y = (link->h - s->h) / 2;
+    s->x = (in_w - s->w) / 2;
+    s->y = (in_h - s->h) / 2;
     if (!s->exact) {
         s->x &= ~((1 << s->hsub) - 1);
         s->y &= ~((1 << s->vsub) - 1);
     }
-    s->in_w = link->w;
-    s->in_h = link->h;
+    s->in_w = in_w;
+    s->in_h = in_h;
     return 0;
 
 fail_expr:
@@ -241,7 +241,7 @@ static int config_input(AVFilterLink *link)
     s->x_pexpr = x_pexpr;
     s->y_pexpr = y_pexpr;
 
-    return config_crop_area(link);
+    return config_crop_area(link, link->w, link->h);
 }
 
 static int config_output(AVFilterLink *link)
@@ -267,12 +267,18 @@ static int filter_frame(AVFilterLink *link, AVFrame 
*frame)
     AVFilterContext *ctx = link->dst;
     CropContext *s = ctx->priv;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
+    int in_w = frame->width, in_h = frame->height;
     int i, ret;
 
-    if (frame->width != s->in_w || frame->height != s->in_h) {
+    if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
+        in_w -= frame->crop_left + frame->crop_right;
+        in_h -= frame->crop_top  + frame->crop_bottom;
+    }
+
+    if (in_w != s->in_w || in_h != s->in_h) {
         link->w = frame->width;
         link->h = frame->height;
-        if ((ret = config_crop_area(link)) < 0 ||
+        if ((ret = config_crop_area(link, in_w, in_h)) < 0 ||
             (ret = config_output(ctx->outputs[0])) < 0) {
             s->in_w = -1;
             av_frame_free(&frame);
@@ -295,10 +301,10 @@ static int filter_frame(AVFilterLink *link, AVFrame 
*frame)
         s->x = 0;
     if (s->y < 0)
         s->y = 0;
-    if ((unsigned)s->x + (unsigned)s->w > link->w)
-        s->x = link->w - s->w;
-    if ((unsigned)s->y + (unsigned)s->h > link->h)
-        s->y = link->h - s->h;
+    if ((unsigned)s->x + (unsigned)s->w > s->in_w)
+        s->x = s->in_w - s->w;
+    if ((unsigned)s->y + (unsigned)s->h > s->in_h)
+        s->y = s->in_h - s->h;
     if (!s->exact) {
         s->x &= ~((1 << s->hsub) - 1);
         s->y &= ~((1 << s->vsub) - 1);
@@ -309,10 +315,10 @@ static int filter_frame(AVFilterLink *link, AVFrame 
*frame)
             s->x, s->y, s->x+s->w, s->y+s->h);
 
     if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) {
-        frame->crop_top   += s->y;
-        frame->crop_left  += s->x;
-        frame->crop_bottom = frame->height - frame->crop_top - 
frame->crop_bottom - s->h;
-        frame->crop_right  = frame->width  - frame->crop_left - 
frame->crop_right - s->w;
+        frame->crop_top    += s->y;
+        frame->crop_left   += s->x;
+        frame->crop_bottom += s->in_h - s->y - s->h;
+        frame->crop_right  += s->in_w - s->x - s->w;
     } else {
         frame->width  = s->w;
         frame->height = s->h;
-- 
2.54.0

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

Reply via email to