Browse code

Zoom & Pan filter

Signed-off-by: Paul B Mahol <onemda@gmail.com>

Paul B Mahol authored on 2013/09/17 00:31:15
Showing 7 changed files
... ...
@@ -27,6 +27,7 @@ version <next>:
27 27
 - display matrix export and rotation api
28 28
 - WebVTT encoder
29 29
 - showcqt multimedia filter
30
+- zoompan filter
30 31
 
31 32
 
32 33
 version 2.2:
... ...
@@ -2540,6 +2540,7 @@ pixfmts_super2xsai_test_deps="super2xsai_filter"
2540 2540
 tinterlace_merge_test_deps="tinterlace_filter"
2541 2541
 tinterlace_pad_test_deps="tinterlace_filter"
2542 2542
 zmq_filter_deps="libzmq"
2543
+zoompan_filter_deps="swscale"
2543 2544
 
2544 2545
 # examples
2545 2546
 avio_reading="avformat avcodec avutil"
... ...
@@ -8680,6 +8680,94 @@ Only deinterlace frames marked as interlaced.
8680 8680
 The default value is @code{all}.
8681 8681
 @end table
8682 8682
 
8683
+@section zoompan
8684
+
8685
+Apply Zoom & Pan effect.
8686
+
8687
+This filter accepts the following options:
8688
+
8689
+@table @option
8690
+@item zoom, z
8691
+Set the zoom expression. Default is 1.
8692
+
8693
+@item x
8694
+@item y
8695
+Set the x and y expression. Default is 0.
8696
+
8697
+@item d
8698
+Set the duration expression in number of frames.
8699
+This sets for how many number of frames effect will last for
8700
+single input image.
8701
+
8702
+@item s
8703
+Set the output image size, default is 'hd720'.
8704
+@end table
8705
+
8706
+Each expression can contain the following constants:
8707
+
8708
+@table @option
8709
+@item in_w, iw
8710
+Input width.
8711
+
8712
+@item in_h, ih
8713
+Input height.
8714
+
8715
+@item out_w, ow
8716
+Output width.
8717
+
8718
+@item out_h, oh
8719
+Output height.
8720
+
8721
+@item in
8722
+Input frame count.
8723
+
8724
+@item on
8725
+Output frame count.
8726
+
8727
+@item x
8728
+@item y
8729
+Last calculated 'x' and 'y' position from 'x' and 'y' expression
8730
+for current input frame.
8731
+
8732
+@item px
8733
+@item py
8734
+'x' and 'y' of last output frame of previous input frame or 0 when there was
8735
+not yet such frame (first input frame).
8736
+
8737
+@item zoom
8738
+Last calculated zoom from 'z' expression for current input frame.
8739
+
8740
+@item pzoom
8741
+Last calculated zoom of last output frame of previous input frame.
8742
+
8743
+@item duration
8744
+Number of output frames for current input frame. Calculated from 'd' expression
8745
+for each input frame.
8746
+
8747
+@item pduration
8748
+number of output frames created for previous input frame
8749
+
8750
+@item a
8751
+Rational number: input width / input height
8752
+
8753
+@item sar
8754
+sample aspect ratio
8755
+
8756
+@item dar
8757
+display aspect ratio
8758
+
8759
+@end table
8760
+
8761
+@subsection Examples
8762
+
8763
+@itemize
8764
+@item
8765
+Zoom-in up to 1.5 and pan at same time to some spot near center of picture:
8766
+@example
8767
+zoompan=z=min(zoom+0.0015\,1.5):d=700:x=if(gte(zoom\,1.5)\,x\,x+1/a):y=if(gte(zoom\,1.5)\,y\,y+1):s=640x360"
8768
+@end example
8769
+@end itemize
8770
+
8683 8771
 @c man end VIDEO FILTERS
8684 8772
 
8685 8773
 @chapter Video Sources
... ...
@@ -195,6 +195,7 @@ OBJS-$(CONFIG_VIGNETTE_FILTER)               += vf_vignette.o
195 195
 OBJS-$(CONFIG_W3FDIF_FILTER)                 += vf_w3fdif.o
196 196
 OBJS-$(CONFIG_YADIF_FILTER)                  += vf_yadif.o
197 197
 OBJS-$(CONFIG_ZMQ_FILTER)                    += f_zmq.o
198
+OBJS-$(CONFIG_ZOOMPAN_FILTER)                += vf_zoompan.o
198 199
 
199 200
 OBJS-$(CONFIG_CELLAUTO_FILTER)               += vsrc_cellauto.o
200 201
 OBJS-$(CONFIG_COLOR_FILTER)                  += vsrc_testsrc.o
... ...
@@ -212,6 +212,7 @@ void avfilter_register_all(void)
212 212
     REGISTER_FILTER(W3FDIF,         w3fdif,         vf);
213 213
     REGISTER_FILTER(YADIF,          yadif,          vf);
214 214
     REGISTER_FILTER(ZMQ,            zmq,            vf);
215
+    REGISTER_FILTER(ZOOMPAN,        zoompan,        vf);
215 216
 
216 217
     REGISTER_FILTER(CELLAUTO,       cellauto,       vsrc);
217 218
     REGISTER_FILTER(COLOR,          color,          vsrc);
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/version.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR   4
33
-#define LIBAVFILTER_VERSION_MINOR   6
33
+#define LIBAVFILTER_VERSION_MINOR   7
34 34
 #define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
37 37
new file mode 100644
... ...
@@ -0,0 +1,310 @@
0
+/*
1
+ * Copyright (c) 2013 Paul B Mahol
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with FFmpeg; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+#include "libavutil/eval.h"
21
+#include "libavutil/opt.h"
22
+#include "libavutil/pixdesc.h"
23
+#include "avfilter.h"
24
+#include "formats.h"
25
+#include "internal.h"
26
+#include "video.h"
27
+#include "libswscale/swscale.h"
28
+
29
+static const char *const var_names[] = {
30
+    "in_w",   "iw",
31
+    "in_h",   "ih",
32
+    "out_w",  "ow",
33
+    "out_h",  "oh",
34
+    "in",
35
+    "on",
36
+    "duration",
37
+    "pduration",
38
+    "time",
39
+    "frame",
40
+    "zoom",
41
+    "pzoom",
42
+    "x", "px",
43
+    "y", "py",
44
+    "a",
45
+    "sar",
46
+    "dar",
47
+    "hsub",
48
+    "vsub",
49
+    NULL
50
+};
51
+
52
+enum var_name {
53
+    VAR_IN_W,   VAR_IW,
54
+    VAR_IN_H,   VAR_IH,
55
+    VAR_OUT_W,  VAR_OW,
56
+    VAR_OUT_H,  VAR_OH,
57
+    VAR_IN,
58
+    VAR_ON,
59
+    VAR_DURATION,
60
+    VAR_PDURATION,
61
+    VAR_TIME,
62
+    VAR_FRAME,
63
+    VAR_ZOOM,
64
+    VAR_PZOOM,
65
+    VAR_X, VAR_PX,
66
+    VAR_Y, VAR_PY,
67
+    VAR_A,
68
+    VAR_SAR,
69
+    VAR_DAR,
70
+    VAR_HSUB,
71
+    VAR_VSUB,
72
+    VARS_NB
73
+};
74
+
75
+typedef struct ZPcontext {
76
+    const AVClass *class;
77
+    char *zoom_expr_str;
78
+    char *x_expr_str;
79
+    char *y_expr_str;
80
+    char *duration_expr_str;
81
+    int w, h;
82
+    double x, y;
83
+    double prev_zoom;
84
+    int prev_nb_frames;
85
+    struct SwsContext *sws;
86
+    int64_t frame_count;
87
+} ZPContext;
88
+
89
+#define OFFSET(x) offsetof(ZPContext, x)
90
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
91
+static const AVOption zoompan_options[] = {
92
+    { "zoom", "set the zoom expression", OFFSET(zoom_expr_str), AV_OPT_TYPE_STRING, {.str = "1" }, .flags = FLAGS },
93
+    { "z", "set the zoom expression", OFFSET(zoom_expr_str), AV_OPT_TYPE_STRING, {.str = "1" }, .flags = FLAGS },
94
+    { "x", "set the x expression", OFFSET(x_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, .flags = FLAGS },
95
+    { "y", "set the y expression", OFFSET(y_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, .flags = FLAGS },
96
+    { "d", "set the duration expression", OFFSET(duration_expr_str), AV_OPT_TYPE_STRING, {.str="90"}, .flags = FLAGS },
97
+    { "s", "set the output image size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, .flags = FLAGS },
98
+    { NULL }
99
+};
100
+
101
+AVFILTER_DEFINE_CLASS(zoompan);
102
+
103
+static av_cold int init(AVFilterContext *ctx)
104
+{
105
+    ZPContext *s = ctx->priv;
106
+
107
+    s->prev_zoom = 1;
108
+    return 0;
109
+}
110
+
111
+static int config_output(AVFilterLink *outlink)
112
+{
113
+    AVFilterContext *ctx = outlink->src;
114
+    ZPContext *s = ctx->priv;
115
+
116
+    outlink->w = s->w;
117
+    outlink->h = s->h;
118
+
119
+    return 0;
120
+}
121
+
122
+static int filter_frame(AVFilterLink *inlink, AVFrame *in)
123
+{
124
+    AVFilterContext *ctx = inlink->dst;
125
+    AVFilterLink *outlink = ctx->outputs[0];
126
+    ZPContext *s = ctx->priv;
127
+    double var_values[VARS_NB], nb_frames, zoom, dx, dy;
128
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(in->format);
129
+    AVFrame *out;
130
+    int i, k, x, y, w, h, ret = 0;
131
+
132
+    var_values[VAR_IN_W]  = var_values[VAR_IW] = in->width;
133
+    var_values[VAR_IN_H]  = var_values[VAR_IH] = in->height;
134
+    var_values[VAR_OUT_W] = var_values[VAR_OW] = s->w;
135
+    var_values[VAR_OUT_H] = var_values[VAR_OH] = s->h;
136
+    var_values[VAR_IN]    = inlink->frame_count + 1;
137
+    var_values[VAR_ON]    = outlink->frame_count + 1;
138
+    var_values[VAR_PX]    = s->x;
139
+    var_values[VAR_PY]    = s->y;
140
+    var_values[VAR_X]     = 0;
141
+    var_values[VAR_Y]     = 0;
142
+    var_values[VAR_PZOOM] = s->prev_zoom;
143
+    var_values[VAR_ZOOM]  = 1;
144
+    var_values[VAR_PDURATION] = s->prev_nb_frames;
145
+    var_values[VAR_A]     = (double) in->width / in->height;
146
+    var_values[VAR_SAR]   = inlink->sample_aspect_ratio.num ?
147
+        (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
148
+    var_values[VAR_DAR]   = var_values[VAR_A] * var_values[VAR_SAR];
149
+    var_values[VAR_HSUB]  = 1 << desc->log2_chroma_w;
150
+    var_values[VAR_VSUB]  = 1 << desc->log2_chroma_h;
151
+
152
+    if ((ret = av_expr_parse_and_eval(&nb_frames, s->duration_expr_str,
153
+                                      var_names, var_values,
154
+                                      NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
155
+        goto fail;
156
+
157
+    var_values[VAR_DURATION] = nb_frames;
158
+    for (i = 0; i < nb_frames; i++) {
159
+        int px[4];
160
+        int py[4];
161
+        int64_t pts = av_rescale_q(in->pts, inlink->time_base,
162
+                                   outlink->time_base) + s->frame_count;
163
+
164
+        var_values[VAR_TIME] = pts * av_q2d(outlink->time_base);
165
+        var_values[VAR_FRAME] = i;
166
+        var_values[VAR_ON] = outlink->frame_count + 1;
167
+        if ((ret = av_expr_parse_and_eval(&zoom, s->zoom_expr_str,
168
+                                          var_names, var_values,
169
+                                          NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
170
+            goto fail;
171
+
172
+        zoom = av_clipd(zoom, 1, 10);
173
+        var_values[VAR_ZOOM] = zoom;
174
+        w = in->width * (1.0 / zoom);
175
+        h = in->height * (1.0 / zoom);
176
+
177
+        if ((ret = av_expr_parse_and_eval(&dx, s->x_expr_str,
178
+                                          var_names, var_values,
179
+                                          NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
180
+            goto fail;
181
+        x = dx = av_clipd(dx, 0, FFMAX(in->width - w, 0));
182
+        var_values[VAR_X] = dx;
183
+        x &= ~((1 << desc->log2_chroma_w) - 1);
184
+
185
+        if ((ret = av_expr_parse_and_eval(&dy, s->y_expr_str,
186
+                                          var_names, var_values,
187
+                                          NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
188
+            goto fail;
189
+        y = dy = av_clipd(dy, 0, FFMAX(in->height - h, 0));
190
+        var_values[VAR_Y] = dy;
191
+        y &= ~((1 << desc->log2_chroma_h) - 1);
192
+
193
+        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
194
+        if (!out) {
195
+            ret = AVERROR(ENOMEM);
196
+            goto fail;
197
+        }
198
+
199
+        px[1] = px[2] = FF_CEIL_RSHIFT(x, desc->log2_chroma_w);
200
+        px[0] = px[3] = x;
201
+
202
+        py[1] = py[2] = FF_CEIL_RSHIFT(y, desc->log2_chroma_h);
203
+        py[0] = py[3] = y;
204
+
205
+        s->sws = sws_alloc_context();
206
+        if (!s->sws) {
207
+            ret = AVERROR(ENOMEM);
208
+            goto fail;
209
+        }
210
+
211
+        uint8_t *input[4];
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
+
252
+static int query_formats(AVFilterContext *ctx)
253
+{
254
+    static const enum AVPixelFormat pix_fmts[] = {
255
+        AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,
256
+        AV_PIX_FMT_YUV420P,  AV_PIX_FMT_YUV411P,
257
+        AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUV440P,
258
+        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P,
259
+        AV_PIX_FMT_YUVA420P,
260
+        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
261
+        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
262
+        AV_PIX_FMT_YUVJ411P,
263
+        AV_PIX_FMT_GRAY8,
264
+        AV_PIX_FMT_NONE
265
+    };
266
+
267
+
268
+    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
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
+
298
+AVFilter ff_vf_zoompan = {
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,
305
+    .query_formats = query_formats,
306
+    .inputs        = inputs,
307
+    .outputs       = outputs,
308
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
309
+};