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]
