FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_zoompan.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Paul B Mahol
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 #include "libavutil/eval.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
24 #include "avfilter.h"
25 #include "formats.h"
26 #include "internal.h"
27 #include "video.h"
28 #include "libswscale/swscale.h"
29 
30 static const char *const var_names[] = {
31  "in_w", "iw",
32  "in_h", "ih",
33  "out_w", "ow",
34  "out_h", "oh",
35  "in",
36  "on",
37  "duration",
38  "pduration",
39  "time",
40  "frame",
41  "zoom",
42  "pzoom",
43  "x", "px",
44  "y", "py",
45  "a",
46  "sar",
47  "dar",
48  "hsub",
49  "vsub",
50  NULL
51 };
52 
53 enum var_name {
74 };
75 
76 typedef struct ZPcontext {
77  const AVClass *class;
79  char *x_expr_str;
80  char *y_expr_str;
82  int w, h;
83  double x, y;
84  double prev_zoom;
86  struct SwsContext *sws;
87  int64_t frame_count;
88 } ZPContext;
89 
90 #define OFFSET(x) offsetof(ZPContext, x)
91 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
92 static const AVOption zoompan_options[] = {
93  { "zoom", "set the zoom expression", OFFSET(zoom_expr_str), AV_OPT_TYPE_STRING, {.str = "1" }, .flags = FLAGS },
94  { "z", "set the zoom expression", OFFSET(zoom_expr_str), AV_OPT_TYPE_STRING, {.str = "1" }, .flags = FLAGS },
95  { "x", "set the x expression", OFFSET(x_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, .flags = FLAGS },
96  { "y", "set the y expression", OFFSET(y_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, .flags = FLAGS },
97  { "d", "set the duration expression", OFFSET(duration_expr_str), AV_OPT_TYPE_STRING, {.str="90"}, .flags = FLAGS },
98  { "s", "set the output image size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, .flags = FLAGS },
99  { NULL }
100 };
101 
102 AVFILTER_DEFINE_CLASS(zoompan);
103 
104 static av_cold int init(AVFilterContext *ctx)
105 {
106  ZPContext *s = ctx->priv;
107 
108  s->prev_zoom = 1;
109  return 0;
110 }
111 
112 static int config_output(AVFilterLink *outlink)
113 {
114  AVFilterContext *ctx = outlink->src;
115  ZPContext *s = ctx->priv;
116 
117  outlink->w = s->w;
118  outlink->h = s->h;
119 
120  return 0;
121 }
122 
123 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
124 {
125  AVFilterContext *ctx = inlink->dst;
126  AVFilterLink *outlink = ctx->outputs[0];
127  ZPContext *s = ctx->priv;
128  double var_values[VARS_NB], nb_frames, zoom, dx, dy;
129  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(in->format);
130  AVFrame *out;
131  int i, k, x, y, w, h, ret = 0;
132 
133  var_values[VAR_IN_W] = var_values[VAR_IW] = in->width;
134  var_values[VAR_IN_H] = var_values[VAR_IH] = in->height;
135  var_values[VAR_OUT_W] = var_values[VAR_OW] = s->w;
136  var_values[VAR_OUT_H] = var_values[VAR_OH] = s->h;
137  var_values[VAR_IN] = inlink->frame_count + 1;
138  var_values[VAR_ON] = outlink->frame_count + 1;
139  var_values[VAR_PX] = s->x;
140  var_values[VAR_PY] = s->y;
141  var_values[VAR_X] = 0;
142  var_values[VAR_Y] = 0;
143  var_values[VAR_PZOOM] = s->prev_zoom;
144  var_values[VAR_ZOOM] = 1;
145  var_values[VAR_PDURATION] = s->prev_nb_frames;
146  var_values[VAR_A] = (double) in->width / in->height;
147  var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ?
148  (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
149  var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR];
150  var_values[VAR_HSUB] = 1 << desc->log2_chroma_w;
151  var_values[VAR_VSUB] = 1 << desc->log2_chroma_h;
152 
153  if ((ret = av_expr_parse_and_eval(&nb_frames, s->duration_expr_str,
154  var_names, var_values,
155  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
156  goto fail;
157 
158  var_values[VAR_DURATION] = nb_frames;
159  for (i = 0; i < nb_frames; i++) {
160  int px[4];
161  int py[4];
162  uint8_t *input[4];
163  int64_t pts = av_rescale_q(in->pts, inlink->time_base,
164  outlink->time_base) + s->frame_count;
165 
166  var_values[VAR_TIME] = pts * av_q2d(outlink->time_base);
167  var_values[VAR_FRAME] = i;
168  var_values[VAR_ON] = outlink->frame_count + 1;
169  if ((ret = av_expr_parse_and_eval(&zoom, s->zoom_expr_str,
170  var_names, var_values,
171  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
172  goto fail;
173 
174  zoom = av_clipd(zoom, 1, 10);
175  var_values[VAR_ZOOM] = zoom;
176  w = in->width * (1.0 / zoom);
177  h = in->height * (1.0 / zoom);
178 
179  if ((ret = av_expr_parse_and_eval(&dx, s->x_expr_str,
180  var_names, var_values,
181  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
182  goto fail;
183  x = dx = av_clipd(dx, 0, FFMAX(in->width - w, 0));
184  var_values[VAR_X] = dx;
185  x &= ~((1 << desc->log2_chroma_w) - 1);
186 
187  if ((ret = av_expr_parse_and_eval(&dy, s->y_expr_str,
188  var_names, var_values,
189  NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
190  goto fail;
191  y = dy = av_clipd(dy, 0, FFMAX(in->height - h, 0));
192  var_values[VAR_Y] = dy;
193  y &= ~((1 << desc->log2_chroma_h) - 1);
194 
195  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
196  if (!out) {
197  ret = AVERROR(ENOMEM);
198  goto fail;
199  }
200 
201  px[1] = px[2] = FF_CEIL_RSHIFT(x, desc->log2_chroma_w);
202  px[0] = px[3] = x;
203 
204  py[1] = py[2] = FF_CEIL_RSHIFT(y, desc->log2_chroma_h);
205  py[0] = py[3] = y;
206 
207  s->sws = sws_alloc_context();
208  if (!s->sws) {
209  ret = AVERROR(ENOMEM);
210  goto fail;
211  }
212 
213  for (k = 0; in->data[k]; k++)
214  input[k] = in->data[k] + py[k] * in->linesize[k] + px[k];
215 
216  av_opt_set_int(s->sws, "srcw", w, 0);
217  av_opt_set_int(s->sws, "srch", h, 0);
218  av_opt_set_int(s->sws, "src_format", in->format, 0);
219  av_opt_set_int(s->sws, "dstw", outlink->w, 0);
220  av_opt_set_int(s->sws, "dsth", outlink->h, 0);
221  av_opt_set_int(s->sws, "dst_format", outlink->format, 0);
222  av_opt_set_int(s->sws, "sws_flags", SWS_BICUBIC, 0);
223 
224  if ((ret = sws_init_context(s->sws, NULL, NULL)) < 0)
225  goto fail;
226 
227  sws_scale(s->sws, (const uint8_t *const *)&input, in->linesize, 0, h, out->data, out->linesize);
228 
229  out->pts = pts;
230  s->frame_count++;
231 
232  ret = ff_filter_frame(outlink, out);
233  if (ret < 0)
234  break;
235 
236  sws_freeContext(s->sws);
237  s->sws = NULL;
238  }
239 
240  s->x = dx;
241  s->y = dy;
242  s->prev_zoom = zoom;
243  s->prev_nb_frames = nb_frames;
244 
245 fail:
246  sws_freeContext(s->sws);
247  s->sws = NULL;
248  av_frame_free(&in);
249  return ret;
250 }
251 
253 {
254  static const enum AVPixelFormat pix_fmts[] = {
265  };
266 
267 
269  return 0;
270 }
271 
272 static av_cold void uninit(AVFilterContext *ctx)
273 {
274  ZPContext *s = ctx->priv;
275 
276  sws_freeContext(s->sws);
277  s->sws = NULL;
278 }
279 
280 static const AVFilterPad inputs[] = {
281  {
282  .name = "default",
283  .type = AVMEDIA_TYPE_VIDEO,
284  .filter_frame = filter_frame,
285  },
286  { NULL }
287 };
288 
289 static const AVFilterPad outputs[] = {
290  {
291  .name = "default",
292  .type = AVMEDIA_TYPE_VIDEO,
293  .config_props = config_output,
294  },
295  { NULL }
296 };
297 
299  .name = "zoompan",
300  .description = NULL_IF_CONFIG_SMALL("Apply Zoom & Pan effect."),
301  .priv_size = sizeof(ZPContext),
302  .priv_class = &zoompan_class,
303  .init = init,
304  .uninit = uninit,
306  .inputs = inputs,
307  .outputs = outputs,
309 };