2 * Copyright (c) 2007 Bobby Bingham
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "libavutil/eval.h"
33 #include "libavutil/avstring.h"
34 #include "libavutil/internal.h"
35 #include "libavutil/libm.h"
36 #include "libavutil/imgutils.h"
37 #include "libavutil/mathematics.h"
38 #include "libavutil/opt.h"
40 static const char *const var_names
[] = {
41 "in_w", "iw", ///< width of the input video
42 "in_h", "ih", ///< height of the input video
43 "out_w", "ow", ///< width of the cropped video
44 "out_h", "oh", ///< height of the cropped video
52 "n", ///< number of frame
53 "pos", ///< position in the file
54 "t", ///< timestamp expressed in seconds
76 typedef struct CropContext
{
78 int x
; ///< x offset of the non-cropped area with respect to the input area
79 int y
; ///< y offset of the non-cropped area with respect to the input area
80 int w
; ///< width of the cropped area
81 int h
; ///< height of the cropped area
83 AVRational out_sar
; ///< output sample aspect ratio
84 int keep_aspect
; ///< keep display aspect ratio when cropping
86 int max_step
[4]; ///< max pixel step for each plane, expressed as a number of bytes
87 int hsub
, vsub
; ///< chroma subsampling
88 char *x_expr
, *y_expr
, *w_expr
, *h_expr
;
89 AVExpr
*x_pexpr
, *y_pexpr
; /* parsed expressions for x and y */
90 double var_values
[VAR_VARS_NB
];
93 static int query_formats(AVFilterContext
*ctx
)
95 AVFilterFormats
*formats
= NULL
;
98 for (fmt
= 0; av_pix_fmt_desc_get(fmt
); fmt
++) {
99 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(fmt
);
100 if (!(desc
->flags
& (AV_PIX_FMT_FLAG_HWACCEL
| AV_PIX_FMT_FLAG_BITSTREAM
)) &&
101 !((desc
->log2_chroma_w
|| desc
->log2_chroma_h
) && !(desc
->flags
& AV_PIX_FMT_FLAG_PLANAR
)))
102 ff_add_format(&formats
, fmt
);
105 ff_set_common_formats(ctx
, formats
);
109 static av_cold
void uninit(AVFilterContext
*ctx
)
111 CropContext
*s
= ctx
->priv
;
113 av_expr_free(s
->x_pexpr
);
115 av_expr_free(s
->y_pexpr
);
119 static inline int normalize_double(int *n
, double d
)
124 ret
= AVERROR(EINVAL
);
125 } else if (d
> INT_MAX
|| d
< INT_MIN
) {
126 *n
= d
> INT_MAX
? INT_MAX
: INT_MIN
;
127 ret
= AVERROR(EINVAL
);
134 static int config_input(AVFilterLink
*link
)
136 AVFilterContext
*ctx
= link
->dst
;
137 CropContext
*s
= ctx
->priv
;
138 const AVPixFmtDescriptor
*pix_desc
= av_pix_fmt_desc_get(link
->format
);
143 s
->var_values
[VAR_IN_W
] = s
->var_values
[VAR_IW
] = ctx
->inputs
[0]->w
;
144 s
->var_values
[VAR_IN_H
] = s
->var_values
[VAR_IH
] = ctx
->inputs
[0]->h
;
145 s
->var_values
[VAR_A
] = (float) link
->w
/ link
->h
;
146 s
->var_values
[VAR_SAR
] = link
->sample_aspect_ratio
.num
? av_q2d(link
->sample_aspect_ratio
) : 1;
147 s
->var_values
[VAR_DAR
] = s
->var_values
[VAR_A
] * s
->var_values
[VAR_SAR
];
148 s
->var_values
[VAR_HSUB
] = 1<<pix_desc
->log2_chroma_w
;
149 s
->var_values
[VAR_VSUB
] = 1<<pix_desc
->log2_chroma_h
;
150 s
->var_values
[VAR_X
] = NAN
;
151 s
->var_values
[VAR_Y
] = NAN
;
152 s
->var_values
[VAR_OUT_W
] = s
->var_values
[VAR_OW
] = NAN
;
153 s
->var_values
[VAR_OUT_H
] = s
->var_values
[VAR_OH
] = NAN
;
154 s
->var_values
[VAR_N
] = 0;
155 s
->var_values
[VAR_T
] = NAN
;
156 s
->var_values
[VAR_POS
] = NAN
;
158 av_image_fill_max_pixsteps(s
->max_step
, NULL
, pix_desc
);
159 s
->hsub
= pix_desc
->log2_chroma_w
;
160 s
->vsub
= pix_desc
->log2_chroma_h
;
162 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= s
->w_expr
),
163 var_names
, s
->var_values
,
164 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
166 s
->var_values
[VAR_OUT_W
] = s
->var_values
[VAR_OW
] = res
;
167 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= s
->h_expr
),
168 var_names
, s
->var_values
,
169 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
171 s
->var_values
[VAR_OUT_H
] = s
->var_values
[VAR_OH
] = res
;
172 /* evaluate again ow as it may depend on oh */
173 if ((ret
= av_expr_parse_and_eval(&res
, (expr
= s
->w_expr
),
174 var_names
, s
->var_values
,
175 NULL
, NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
178 s
->var_values
[VAR_OUT_W
] = s
->var_values
[VAR_OW
] = res
;
179 if (normalize_double(&s
->w
, s
->var_values
[VAR_OUT_W
]) < 0 ||
180 normalize_double(&s
->h
, s
->var_values
[VAR_OUT_H
]) < 0) {
181 av_log(ctx
, AV_LOG_ERROR
,
182 "Too big value or invalid expression for out_w/ow or out_h/oh. "
183 "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n",
184 s
->w_expr
, s
->h_expr
);
185 return AVERROR(EINVAL
);
187 s
->w
&= ~((1 << s
->hsub
) - 1);
188 s
->h
&= ~((1 << s
->vsub
) - 1);
190 av_expr_free(s
->x_pexpr
);
191 av_expr_free(s
->y_pexpr
);
192 s
->x_pexpr
= s
->y_pexpr
= NULL
;
193 if ((ret
= av_expr_parse(&s
->x_pexpr
, s
->x_expr
, var_names
,
194 NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0 ||
195 (ret
= av_expr_parse(&s
->y_pexpr
, s
->y_expr
, var_names
,
196 NULL
, NULL
, NULL
, NULL
, 0, ctx
)) < 0)
197 return AVERROR(EINVAL
);
199 if (s
->keep_aspect
) {
200 AVRational dar
= av_mul_q(link
->sample_aspect_ratio
,
201 (AVRational
){ link
->w
, link
->h
});
202 av_reduce(&s
->out_sar
.num
, &s
->out_sar
.den
,
203 dar
.num
* s
->h
, dar
.den
* s
->w
, INT_MAX
);
205 s
->out_sar
= link
->sample_aspect_ratio
;
207 av_log(ctx
, AV_LOG_VERBOSE
, "w:%d h:%d sar:%d/%d -> w:%d h:%d sar:%d/%d\n",
208 link
->w
, link
->h
, link
->sample_aspect_ratio
.num
, link
->sample_aspect_ratio
.den
,
209 s
->w
, s
->h
, s
->out_sar
.num
, s
->out_sar
.den
);
211 if (s
->w
<= 0 || s
->h
<= 0 ||
212 s
->w
> link
->w
|| s
->h
> link
->h
) {
213 av_log(ctx
, AV_LOG_ERROR
,
214 "Invalid too big or non positive size for width '%d' or height '%d'\n",
216 return AVERROR(EINVAL
);
219 /* set default, required in the case the first computed value for x/y is NAN */
220 s
->x
= (link
->w
- s
->w
) / 2;
221 s
->y
= (link
->h
- s
->h
) / 2;
222 s
->x
&= ~((1 << s
->hsub
) - 1);
223 s
->y
&= ~((1 << s
->vsub
) - 1);
227 av_log(NULL
, AV_LOG_ERROR
, "Error when evaluating the expression '%s'\n", expr
);
231 static int config_output(AVFilterLink
*link
)
233 CropContext
*s
= link
->src
->priv
;
237 link
->sample_aspect_ratio
= s
->out_sar
;
242 static int filter_frame(AVFilterLink
*link
, AVFrame
*frame
)
244 AVFilterContext
*ctx
= link
->dst
;
245 CropContext
*s
= ctx
->priv
;
246 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(link
->format
);
250 frame
->height
= s
->h
;
252 s
->var_values
[VAR_N
] = link
->frame_count
;
253 s
->var_values
[VAR_T
] = frame
->pts
== AV_NOPTS_VALUE
?
254 NAN
: frame
->pts
* av_q2d(link
->time_base
);
255 s
->var_values
[VAR_POS
] = av_frame_get_pkt_pos(frame
) == -1 ?
256 NAN
: av_frame_get_pkt_pos(frame
);
257 s
->var_values
[VAR_X
] = av_expr_eval(s
->x_pexpr
, s
->var_values
, NULL
);
258 s
->var_values
[VAR_Y
] = av_expr_eval(s
->y_pexpr
, s
->var_values
, NULL
);
259 s
->var_values
[VAR_X
] = av_expr_eval(s
->x_pexpr
, s
->var_values
, NULL
);
261 normalize_double(&s
->x
, s
->var_values
[VAR_X
]);
262 normalize_double(&s
->y
, s
->var_values
[VAR_Y
]);
268 if ((unsigned)s
->x
+ (unsigned)s
->w
> link
->w
)
269 s
->x
= link
->w
- s
->w
;
270 if ((unsigned)s
->y
+ (unsigned)s
->h
> link
->h
)
271 s
->y
= link
->h
- s
->h
;
272 s
->x
&= ~((1 << s
->hsub
) - 1);
273 s
->y
&= ~((1 << s
->vsub
) - 1);
275 av_dlog(ctx
, "n:%d t:%f pos:%f x:%d y:%d x+w:%d y+h:%d\n",
276 (int)s
->var_values
[VAR_N
], s
->var_values
[VAR_T
], s
->var_values
[VAR_POS
],
277 s
->x
, s
->y
, s
->x
+s
->w
, s
->y
+s
->h
);
279 frame
->data
[0] += s
->y
* frame
->linesize
[0];
280 frame
->data
[0] += s
->x
* s
->max_step
[0];
282 if (!(desc
->flags
& AV_PIX_FMT_FLAG_PAL
|| desc
->flags
& AV_PIX_FMT_FLAG_PSEUDOPAL
)) {
283 for (i
= 1; i
< 3; i
++) {
284 if (frame
->data
[i
]) {
285 frame
->data
[i
] += (s
->y
>> s
->vsub
) * frame
->linesize
[i
];
286 frame
->data
[i
] += (s
->x
* s
->max_step
[i
]) >> s
->hsub
;
292 if (frame
->data
[3]) {
293 frame
->data
[3] += s
->y
* frame
->linesize
[3];
294 frame
->data
[3] += s
->x
* s
->max_step
[3];
297 return ff_filter_frame(link
->dst
->outputs
[0], frame
);
300 #define OFFSET(x) offsetof(CropContext, x)
301 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
303 static const AVOption crop_options
[] = {
304 { "out_w", "set the width crop area expression", OFFSET(w_expr
), AV_OPT_TYPE_STRING
, {.str
= "iw"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
305 { "w", "set the width crop area expression", OFFSET(w_expr
), AV_OPT_TYPE_STRING
, {.str
= "iw"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
306 { "out_h", "set the height crop area expression", OFFSET(h_expr
), AV_OPT_TYPE_STRING
, {.str
= "ih"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
307 { "h", "set the height crop area expression", OFFSET(h_expr
), AV_OPT_TYPE_STRING
, {.str
= "ih"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
308 { "x", "set the x crop area expression", OFFSET(x_expr
), AV_OPT_TYPE_STRING
, {.str
= "(in_w-out_w)/2"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
309 { "y", "set the y crop area expression", OFFSET(y_expr
), AV_OPT_TYPE_STRING
, {.str
= "(in_h-out_h)/2"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
310 { "keep_aspect", "keep aspect ratio", OFFSET(keep_aspect
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 1, FLAGS
},
314 AVFILTER_DEFINE_CLASS(crop
);
316 static const AVFilterPad avfilter_vf_crop_inputs
[] = {
319 .type
= AVMEDIA_TYPE_VIDEO
,
320 .filter_frame
= filter_frame
,
321 .config_props
= config_input
,
326 static const AVFilterPad avfilter_vf_crop_outputs
[] = {
329 .type
= AVMEDIA_TYPE_VIDEO
,
330 .config_props
= config_output
,
335 AVFilter ff_vf_crop
= {
337 .description
= NULL_IF_CONFIG_SMALL("Crop the input video."),
338 .priv_size
= sizeof(CropContext
),
339 .priv_class
= &crop_class
,
340 .query_formats
= query_formats
,
342 .inputs
= avfilter_vf_crop_inputs
,
343 .outputs
= avfilter_vf_crop_outputs
,