Commit da3293f4d2 for ffmpeg

commit da3293f4d210591d37c6bec0453bae6e406595e7
Author: Michael Niedermayer <michael@niedermayer.cc>
Date:   Tue Oct 6 00:13:39 2026 +0200

    avfilter/vf_crop: reconfigure when the input frame size changes

    Found during triage of the security report RqgTntCAW2qN.

diff --git a/doc/filters.texi b/doc/filters.texi
index 406ead7198..04496d338f 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -10829,13 +10829,13 @@ It accepts the following parameters:
 @table @option
 @item w, out_w
 The width of the output video. It defaults to @code{iw}.
-This expression is evaluated only once during the filter
-configuration, or when the @samp{w} or @samp{out_w} command is sent.
+This expression is evaluated during the filter configuration, when the
+@samp{w} or @samp{out_w} command is sent, or when the input frame size changes.

 @item h, out_h
 The height of the output video. It defaults to @code{ih}.
-This expression is evaluated only once during the filter
-configuration, or when the @samp{h} or @samp{out_h} command is sent.
+This expression is evaluated during the filter configuration, when the
+@samp{h} or @samp{out_h} command is sent, or when the input frame size changes.

 @item x
 The horizontal position, in the input video, of the left edge of the output
diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c
index 433fcb3c90..e06be77302 100644
--- a/libavfilter/vf_crop.c
+++ b/libavfilter/vf_crop.c
@@ -77,6 +77,7 @@ typedef struct CropContext {
     int  y;             ///< y offset of the non-cropped area with respect to the input area
     int  w;             ///< width of the cropped area
     int  h;             ///< height of the cropped area
+    int in_w, in_h;     ///< input size the cropped area was computed for

     AVRational out_sar; ///< output sample aspect ratio
     int keep_aspect;    ///< keep display aspect ratio when cropping
@@ -124,7 +125,7 @@ static inline int normalize_double(int *n, double d)
     return ret;
 }

-static int config_input(AVFilterLink *link)
+static int config_crop_area(AVFilterLink *link)
 {
     AVFilterContext *ctx = link->dst;
     CropContext *s = ctx->priv;
@@ -140,8 +141,6 @@ static int config_input(AVFilterLink *link)
     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;
     s->var_values[VAR_VSUB]  = 1<<pix_desc->log2_chroma_h;
-    s->var_values[VAR_X]     = NAN;
-    s->var_values[VAR_Y]     = NAN;
     s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = NAN;
     s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN;
     s->var_values[VAR_N]     = 0;
@@ -187,15 +186,6 @@ static int config_input(AVFilterLink *link)
         s->h &= ~((1 << s->vsub) - 1);
     }

-    av_expr_free(s->x_pexpr);
-    av_expr_free(s->y_pexpr);
-    s->x_pexpr = s->y_pexpr = NULL;
-    if ((ret = av_expr_parse(&s->x_pexpr, s->x_expr, var_names,
-                             NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
-        (ret = av_expr_parse(&s->y_pexpr, s->y_expr, var_names,
-                             NULL, NULL, NULL, NULL, 0, ctx)) < 0)
-        return AVERROR(EINVAL);
-
     if (s->keep_aspect) {
         AVRational dar = av_mul_q(link->sample_aspect_ratio,
                                   (AVRational){ link->w, link->h });
@@ -223,6 +213,8 @@ static int config_input(AVFilterLink *link)
         s->x &= ~((1 << s->hsub) - 1);
         s->y &= ~((1 << s->vsub) - 1);
     }
+    s->in_w = link->w;
+    s->in_h = link->h;
     return 0;

 fail_expr:
@@ -230,6 +222,24 @@ fail_expr:
     return ret;
 }

+static int config_input(AVFilterLink *link)
+{
+    CropContext *s = link->dst->priv;
+
+    s->var_values[VAR_X] = NAN;
+    s->var_values[VAR_Y] = NAN;
+    av_expr_free(s->x_pexpr);
+    av_expr_free(s->y_pexpr);
+    s->x_pexpr = s->y_pexpr = NULL;
+    if (av_expr_parse(&s->x_pexpr, s->x_expr, var_names,
+                      NULL, NULL, NULL, NULL, 0, link->dst) < 0 ||
+        av_expr_parse(&s->y_pexpr, s->y_expr, var_names,
+                      NULL, NULL, NULL, NULL, 0, link->dst) < 0)
+        return AVERROR(EINVAL);
+
+    return config_crop_area(link);
+}
+
 static int config_output(AVFilterLink *link)
 {
     CropContext *s = link->src->priv;
@@ -253,7 +263,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 i;
+    int i, ret;
+
+    if (frame->width != s->in_w || frame->height != s->in_h) {
+        link->w = frame->width;
+        link->h = frame->height;
+        if ((ret = config_crop_area(link)) < 0 ||
+            (ret = config_output(ctx->outputs[0])) < 0) {
+            s->in_w = -1;
+            av_frame_free(&frame);
+            return ret;
+        }
+    }

     s->var_values[VAR_N] = l->frame_count_out;
     s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
@@ -395,4 +416,5 @@ const FFFilter ff_vf_crop = {
     FILTER_OUTPUTS(avfilter_vf_crop_outputs),
     FILTER_QUERY_FUNC2(query_formats),
     .process_command = process_command,
+    .flags_internal = FF_FILTER_FLAG_DYNAMIC_FRAME_SIZE,
 };