FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_crop.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2007 Bobby Bingham
3  *
4  * This file is part of FFmpeg.
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 /**
22  * @file
23  * video crop filter
24  */
25 
26 #include <stdio.h>
27 
28 #include "avfilter.h"
29 #include "formats.h"
30 #include "internal.h"
31 #include "video.h"
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"
39 
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
45  "a",
46  "sar",
47  "dar",
48  "hsub",
49  "vsub",
50  "x",
51  "y",
52  "n", ///< number of frame
53  "pos", ///< position in the file
54  "t", ///< timestamp expressed in seconds
55  NULL
56 };
57 
58 enum var_name {
74 };
75 
76 typedef struct {
77  const AVClass *class;
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
82 
83  AVRational out_sar; ///< output sample aspect ratio
84  int keep_aspect; ///< keep display aspect ratio when cropping
85 
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];
91 } CropContext;
92 
94 {
95  static const enum AVPixelFormat pix_fmts[] = {
119  };
120 
122 
123  return 0;
124 }
125 
126 static av_cold void uninit(AVFilterContext *ctx)
127 {
128  CropContext *s = ctx->priv;
129 
130  av_expr_free(s->x_pexpr);
131  s->x_pexpr = NULL;
132  av_expr_free(s->y_pexpr);
133  s->y_pexpr = NULL;
134 }
135 
136 static inline int normalize_double(int *n, double d)
137 {
138  int ret = 0;
139 
140  if (isnan(d)) {
141  ret = AVERROR(EINVAL);
142  } else if (d > INT_MAX || d < INT_MIN) {
143  *n = d > INT_MAX ? INT_MAX : INT_MIN;
144  ret = AVERROR(EINVAL);
145  } else
146  *n = round(d);
147 
148  return ret;
149 }
150 
151 static int config_input(AVFilterLink *link)
152 {
153  AVFilterContext *ctx = link->dst;
154  CropContext *s = ctx->priv;
155  const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(link->format);
156  int ret;
157  const char *expr;
158  double res;
159 
160  s->var_values[VAR_IN_W] = s->var_values[VAR_IW] = ctx->inputs[0]->w;
161  s->var_values[VAR_IN_H] = s->var_values[VAR_IH] = ctx->inputs[0]->h;
162  s->var_values[VAR_A] = (float) link->w / link->h;
165  s->var_values[VAR_HSUB] = 1<<pix_desc->log2_chroma_w;
166  s->var_values[VAR_VSUB] = 1<<pix_desc->log2_chroma_h;
167  s->var_values[VAR_X] = NAN;
168  s->var_values[VAR_Y] = NAN;
171  s->var_values[VAR_N] = 0;
172  s->var_values[VAR_T] = NAN;
173  s->var_values[VAR_POS] = NAN;
174 
176  s->hsub = pix_desc->log2_chroma_w;
177  s->vsub = pix_desc->log2_chroma_h;
178 
179  if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
180  var_names, s->var_values,
181  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
182  goto fail_expr;
184  if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr),
185  var_names, s->var_values,
186  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
187  goto fail_expr;
189  /* evaluate again ow as it may depend on oh */
190  if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr),
191  var_names, s->var_values,
192  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
193  goto fail_expr;
194 
196  if (normalize_double(&s->w, s->var_values[VAR_OUT_W]) < 0 ||
197  normalize_double(&s->h, s->var_values[VAR_OUT_H]) < 0) {
198  av_log(ctx, AV_LOG_ERROR,
199  "Too big value or invalid expression for out_w/ow or out_h/oh. "
200  "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n",
201  s->w_expr, s->h_expr);
202  return AVERROR(EINVAL);
203  }
204  s->w &= ~((1 << s->hsub) - 1);
205  s->h &= ~((1 << s->vsub) - 1);
206 
207  av_expr_free(s->x_pexpr);
208  av_expr_free(s->y_pexpr);
209  s->x_pexpr = s->y_pexpr = NULL;
210  if ((ret = av_expr_parse(&s->x_pexpr, s->x_expr, var_names,
211  NULL, NULL, NULL, NULL, 0, ctx)) < 0 ||
212  (ret = av_expr_parse(&s->y_pexpr, s->y_expr, var_names,
213  NULL, NULL, NULL, NULL, 0, ctx)) < 0)
214  return AVERROR(EINVAL);
215 
216  if (s->keep_aspect) {
218  (AVRational){ link->w, link->h });
219  av_reduce(&s->out_sar.num, &s->out_sar.den,
220  dar.num * s->h, dar.den * s->w, INT_MAX);
221  } else
222  s->out_sar = link->sample_aspect_ratio;
223 
224  av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d sar:%d/%d -> w:%d h:%d sar:%d/%d\n",
225  link->w, link->h, link->sample_aspect_ratio.num, link->sample_aspect_ratio.den,
226  s->w, s->h, s->out_sar.num, s->out_sar.den);
227 
228  if (s->w <= 0 || s->h <= 0 ||
229  s->w > link->w || s->h > link->h) {
230  av_log(ctx, AV_LOG_ERROR,
231  "Invalid too big or non positive size for width '%d' or height '%d'\n",
232  s->w, s->h);
233  return AVERROR(EINVAL);
234  }
235 
236  /* set default, required in the case the first computed value for x/y is NAN */
237  s->x = (link->w - s->w) / 2;
238  s->y = (link->h - s->h) / 2;
239  s->x &= ~((1 << s->hsub) - 1);
240  s->y &= ~((1 << s->vsub) - 1);
241  return 0;
242 
243 fail_expr:
244  av_log(NULL, AV_LOG_ERROR, "Error when evaluating the expression '%s'\n", expr);
245  return ret;
246 }
247 
248 static int config_output(AVFilterLink *link)
249 {
250  CropContext *s = link->src->priv;
251 
252  link->w = s->w;
253  link->h = s->h;
254  link->sample_aspect_ratio = s->out_sar;
255 
256  return 0;
257 }
258 
260 {
261  AVFilterContext *ctx = link->dst;
262  CropContext *s = ctx->priv;
263  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format);
264  int i;
265 
266  frame->width = s->w;
267  frame->height = s->h;
268 
269  s->var_values[VAR_N] = link->frame_count;
270  s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ?
271  NAN : frame->pts * av_q2d(link->time_base);
272  s->var_values[VAR_POS] = av_frame_get_pkt_pos(frame) == -1 ?
273  NAN : av_frame_get_pkt_pos(frame);
277 
280 
281  if (s->x < 0)
282  s->x = 0;
283  if (s->y < 0)
284  s->y = 0;
285  if ((unsigned)s->x + (unsigned)s->w > link->w)
286  s->x = link->w - s->w;
287  if ((unsigned)s->y + (unsigned)s->h > link->h)
288  s->y = link->h - s->h;
289  s->x &= ~((1 << s->hsub) - 1);
290  s->y &= ~((1 << s->vsub) - 1);
291 
292  av_dlog(ctx, "n:%d t:%f pos:%f x:%d y:%d x+w:%d y+h:%d\n",
293  (int)s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS],
294  s->x, s->y, s->x+s->w, s->y+s->h);
295 
296  frame->data[0] += s->y * frame->linesize[0];
297  frame->data[0] += s->x * s->max_step[0];
298 
299  if (!(desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)) {
300  for (i = 1; i < 3; i ++) {
301  if (frame->data[i]) {
302  frame->data[i] += (s->y >> s->vsub) * frame->linesize[i];
303  frame->data[i] += (s->x * s->max_step[i]) >> s->hsub;
304  }
305  }
306  }
307 
308  /* alpha plane */
309  if (frame->data[3]) {
310  frame->data[3] += s->y * frame->linesize[3];
311  frame->data[3] += s->x * s->max_step[3];
312  }
313 
314  return ff_filter_frame(link->dst->outputs[0], frame);
315 }
316 
317 #define OFFSET(x) offsetof(CropContext, x)
318 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
319 
320 static const AVOption crop_options[] = {
321  { "out_w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
322  { "w", "set the width crop area expression", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, CHAR_MIN, CHAR_MAX, FLAGS },
323  { "out_h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
324  { "h", "set the height crop area expression", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, CHAR_MIN, CHAR_MAX, FLAGS },
325  { "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 },
326  { "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 },
327  { "keep_aspect", "keep aspect ratio", OFFSET(keep_aspect), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
328  {NULL}
329 };
330 
332 
334  {
335  .name = "default",
336  .type = AVMEDIA_TYPE_VIDEO,
337  .filter_frame = filter_frame,
338  .get_video_buffer = ff_null_get_video_buffer,
339  .config_props = config_input,
340  },
341  { NULL }
342 };
343 
345  {
346  .name = "default",
347  .type = AVMEDIA_TYPE_VIDEO,
348  .config_props = config_output,
349  },
350  { NULL }
351 };
352 
354  .name = "crop",
355  .description = NULL_IF_CONFIG_SMALL("Crop the input video to width:height:x:y."),
356 
357  .priv_size = sizeof(CropContext),
358  .priv_class = &crop_class,
359 
361  .uninit = uninit,
362 
363  .inputs = avfilter_vf_crop_inputs,
364  .outputs = avfilter_vf_crop_outputs,
365 };