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,
};