Browse code

lavfi: port tinterlace filter from MPlayer

Port MPlayer tinterlace filter from MPlayer, with some ideas taken
from the FFmbc/libavfilter port, with the following main differences:

* added support for full-scale YUVJ formats
* added support for YUVA420P
* request_frame() on the filter is forced to return a frame
* some code factorization (related to the copy_picture_fields() function)
* fixed black padding values for mode 3

Stefano Sabatini authored on 2011/06/25 20:06:24
Showing 7 changed files
... ...
@@ -11,6 +11,7 @@ version next:
11 11
 - thumbnail video filter
12 12
 - XML output in ffprobe
13 13
 - asplit audio filter
14
+- tinterlace video filter
14 15
 
15 16
 
16 17
 version 0.9:
... ...
@@ -1652,6 +1652,7 @@ mptestsrc_filter_deps="gpl"
1652 1652
 negate_filter_deps="lut_filter"
1653 1653
 ocv_filter_deps="libopencv"
1654 1654
 scale_filter_deps="swscale"
1655
+tinterlace_filter_deps="gpl"
1655 1656
 yadif_filter_deps="gpl"
1656 1657
 
1657 1658
 # libraries
... ...
@@ -2433,6 +2433,40 @@ Complete example of a thumbnail creation with @command{ffmpeg}:
2433 2433
 ffmpeg -i in.avi -vf thumbnail,scale=300:200 -frames:v 1 out.png
2434 2434
 @end example
2435 2435
 
2436
+@section tinterlace
2437
+
2438
+Perform various types of temporal field interlacing.
2439
+
2440
+Frames are counted starting from 1, so the first input frame is
2441
+considered odd.
2442
+
2443
+This filter accepts a single parameter specifying the mode. Available
2444
+modes are:
2445
+
2446
+@table @samp
2447
+@item 0
2448
+Move odd frames into the upper field, even into the lower field,
2449
+generating a double height frame at half framerate.
2450
+
2451
+@item 1
2452
+Only output even frames, odd frames are dropped, generating a frame with
2453
+unchanged height at half framerate.
2454
+
2455
+@item 2
2456
+Only output odd frames, even frames are dropped, generating a frame with
2457
+unchanged height at half framerate.
2458
+
2459
+@item 3
2460
+Expand each frame to full height, but pad alternate lines with black,
2461
+generating a frame with double height at the same input framerate.
2462
+
2463
+@item 4
2464
+Interleave the upper field from odd frames with the lower field from
2465
+even frames, generating a frame with unchanged height at half framerate.
2466
+@end table
2467
+
2468
+Default mode is 0.
2469
+
2436 2470
 @section transpose
2437 2471
 
2438 2472
 Transpose rows with columns in the input video and optionally flip it.
... ...
@@ -81,6 +81,7 @@ OBJS-$(CONFIG_SHOWINFO_FILTER)               += vf_showinfo.o
81 81
 OBJS-$(CONFIG_SLICIFY_FILTER)                += vf_slicify.o
82 82
 OBJS-$(CONFIG_SPLIT_FILTER)                  += vf_split.o
83 83
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
84
+OBJS-$(CONFIG_TINTERLACE_FILTER)             += vf_tinterlace.o
84 85
 OBJS-$(CONFIG_TRANSPOSE_FILTER)              += vf_transpose.o
85 86
 OBJS-$(CONFIG_UNSHARP_FILTER)                += vf_unsharp.o
86 87
 OBJS-$(CONFIG_VFLIP_FILTER)                  += vf_vflip.o
... ...
@@ -91,6 +91,7 @@ void avfilter_register_all(void)
91 91
     REGISTER_FILTER (SLICIFY,     slicify,     vf);
92 92
     REGISTER_FILTER (SPLIT,       split,       vf);
93 93
     REGISTER_FILTER (THUMBNAIL,   thumbnail,   vf);
94
+    REGISTER_FILTER (TINTERLACE,  tinterlace,  vf);
94 95
     REGISTER_FILTER (TRANSPOSE,   transpose,   vf);
95 96
     REGISTER_FILTER (UNSHARP,     unsharp,     vf);
96 97
     REGISTER_FILTER (VFLIP,       vflip,       vf);
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavcodec/avcodec.h"
31 31
 
32 32
 #define LIBAVFILTER_VERSION_MAJOR  2
33
-#define LIBAVFILTER_VERSION_MINOR 55
34
-#define LIBAVFILTER_VERSION_MICRO 101
33
+#define LIBAVFILTER_VERSION_MINOR 56
34
+#define LIBAVFILTER_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
37 37
                                                LIBAVFILTER_VERSION_MINOR, \
38 38
new file mode 100644
... ...
@@ -0,0 +1,325 @@
0
+/*
1
+ * Copyright (c) 2011 Stefano Sabatini
2
+ * Copyright (c) 2010 Baptiste Coudurier
3
+ * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
4
+ *
5
+ * This file is part of FFmpeg.
6
+ *
7
+ * FFmpeg is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * FFmpeg is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License along
18
+ * with FFmpeg if not, write to the Free Software Foundation, Inc.,
19
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
+ */
21
+
22
+/**
23
+ * @file
24
+ * temporal field interlace filter, ported from MPlayer/libmpcodecs
25
+ */
26
+
27
+#include "libavutil/imgutils.h"
28
+#include "avfilter.h"
29
+#include "internal.h"
30
+
31
+typedef struct {
32
+    int mode;                   ///< interlace mode selected
33
+    int frame;                  ///< number of the output frame
34
+    int vsub;                   ///< chroma vertical subsampling
35
+    AVFilterBufferRef *cur;
36
+    AVFilterBufferRef *next;
37
+    uint8_t *black_data[4];     ///< buffer used to fill padded lines
38
+    int black_linesize[4];
39
+} TInterlaceContext;
40
+
41
+#define FULL_SCALE_YUVJ_FORMATS \
42
+    PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
43
+
44
+static enum PixelFormat full_scale_yuvj_pix_fmts[] = {
45
+    FULL_SCALE_YUVJ_FORMATS, PIX_FMT_NONE
46
+};
47
+
48
+static int query_formats(AVFilterContext *ctx)
49
+{
50
+    static const enum PixelFormat pix_fmts[] = {
51
+        PIX_FMT_YUV420P,  PIX_FMT_YUV422P,  PIX_FMT_YUV444P,
52
+        PIX_FMT_YUV444P,  PIX_FMT_YUV410P,  PIX_FMT_YUVA420P,
53
+        PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS,
54
+        PIX_FMT_NONE
55
+    };
56
+
57
+    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
58
+    return 0;
59
+}
60
+
61
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
62
+{
63
+    TInterlaceContext *tinterlace = ctx->priv;
64
+    int n;
65
+    tinterlace->mode = 0;
66
+
67
+    if (args) {
68
+        n = sscanf(args, "%d", &tinterlace->mode);
69
+
70
+        if (n != 1 || tinterlace->mode < 0 || tinterlace->mode > 4) {
71
+            av_log(ctx, AV_LOG_ERROR,
72
+                   "Invalid mode '%s', use an integer between 0 and 4\n", args);
73
+            return AVERROR(EINVAL);
74
+        }
75
+    }
76
+
77
+    return 0;
78
+}
79
+
80
+static av_cold void uninit(AVFilterContext *ctx)
81
+{
82
+    TInterlaceContext *tinterlace = ctx->priv;
83
+
84
+    if (tinterlace->cur ) avfilter_unref_buffer(tinterlace->cur );
85
+    if (tinterlace->next) avfilter_unref_buffer(tinterlace->next);
86
+
87
+    av_freep(&tinterlace->black_data[0]);
88
+}
89
+
90
+static int config_out_props(AVFilterLink *outlink)
91
+{
92
+    AVFilterContext *ctx = outlink->src;
93
+    AVFilterLink *inlink = outlink->src->inputs[0];
94
+    const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[outlink->format];
95
+    TInterlaceContext *tinterlace = ctx->priv;
96
+
97
+    tinterlace->vsub = desc->log2_chroma_h;
98
+    outlink->w = inlink->w;
99
+    outlink->h = tinterlace->mode == 0 || tinterlace->mode == 3 ?
100
+        inlink->h*2 : inlink->h;
101
+
102
+    if (tinterlace->mode == 3) {
103
+        uint8_t black[4] = { 16, 128, 128, 16 };
104
+        int i, ret;
105
+        if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts))
106
+            black[0] = black[3] = 0;
107
+        ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize,
108
+                             outlink->w, outlink->h, outlink->format, 1);
109
+        if (ret < 0)
110
+            return ret;
111
+
112
+        /* fill black picture with black */
113
+        for (i = 0; i < 4 && tinterlace->black_data[i]; i++) {
114
+            int h = i == 1 || i == 2 ? outlink->h >> desc->log2_chroma_h : outlink->h;
115
+            memset(tinterlace->black_data[i], black[i],
116
+                   tinterlace->black_linesize[i] * h);
117
+        }
118
+    }
119
+    av_log(ctx, AV_LOG_INFO, "mode:%d h:%d -> h:%d\n",
120
+           tinterlace->mode, inlink->h, outlink->h);
121
+
122
+    return 0;
123
+}
124
+
125
+#define FIELD_UPPER           0
126
+#define FIELD_LOWER           1
127
+#define FIELD_UPPER_AND_LOWER 2
128
+
129
+/**
130
+ * Copy picture field from src to dst.
131
+ *
132
+ * @param src_field copy from upper, lower field or both
133
+ * @param interleave leave a padding line between each copied field
134
+ * @param dst_field copy to upper or lower field,
135
+ *        only meaningful when interleave is selected
136
+ */
137
+static inline
138
+void copy_picture_field(uint8_t *dst[4], int dst_linesize[4],
139
+                        uint8_t *src[4], int src_linesize[4],
140
+                        enum PixelFormat format, int w, int src_h,
141
+                        int src_field, int interleave, int dst_field)
142
+{
143
+    const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[format];
144
+    int plane, vsub = desc->log2_chroma_h;
145
+    int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2;
146
+
147
+    for (plane = 0; plane < desc->nb_components; plane++) {
148
+        int lines = plane == 1 || plane == 2 ? src_h >> vsub : src_h;
149
+        int linesize = av_image_get_linesize(format, w, plane);
150
+        uint8_t *dstp = dst[plane];
151
+        uint8_t *srcp = src[plane];
152
+        lines /= k;
153
+        if (src_field == FIELD_LOWER)
154
+            srcp += src_linesize[plane];
155
+        if (interleave && dst_field == FIELD_LOWER)
156
+            dstp += dst_linesize[plane];
157
+        av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1),
158
+                            srcp, src_linesize[plane]*k, linesize, lines);
159
+    }
160
+}
161
+
162
+static void start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
163
+{
164
+    AVFilterContext *ctx = inlink->dst;
165
+    TInterlaceContext *tinterlace = ctx->priv;
166
+
167
+    if (tinterlace->cur)
168
+        avfilter_unref_buffer(tinterlace->cur);
169
+    tinterlace->cur  = tinterlace->next;
170
+    tinterlace->next = picref;
171
+}
172
+
173
+static void end_frame(AVFilterLink *inlink)
174
+{
175
+    AVFilterContext *ctx = inlink->dst;
176
+    AVFilterLink *outlink = ctx->outputs[0];
177
+    TInterlaceContext *tinterlace = ctx->priv;
178
+    AVFilterBufferRef *cur  = tinterlace->cur;
179
+    AVFilterBufferRef *next = tinterlace->next;
180
+    AVFilterBufferRef *out  = NULL;
181
+    int field;
182
+
183
+    /* we need at least two frames */
184
+    if (!tinterlace->cur)
185
+        return;
186
+
187
+    switch (tinterlace->mode) {
188
+    case 0: /* move the odd frame into the upper field of the new image, even into
189
+             * the lower field, generating a double-height video at half framerate */
190
+        out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
191
+        avfilter_copy_buffer_ref_props(out, cur);
192
+        out->video->h = outlink->h;
193
+        out->video->interlaced = 1;
194
+        out->video->top_field_first = 1;
195
+
196
+        /* write odd frame lines into the upper field of the new frame */
197
+        copy_picture_field(out->data, out->linesize,
198
+                           cur->data, cur->linesize,
199
+                           inlink->format, inlink->w, inlink->h,
200
+                           FIELD_UPPER_AND_LOWER, 1, FIELD_UPPER);
201
+        /* write even frame lines into the lower field of the new frame */
202
+        copy_picture_field(out->data, out->linesize,
203
+                           next->data, next->linesize,
204
+                           inlink->format, inlink->w, inlink->h,
205
+                           FIELD_UPPER_AND_LOWER, 1, FIELD_LOWER);
206
+        avfilter_unref_buffer(tinterlace->next);
207
+        tinterlace->next = NULL;
208
+        break;
209
+
210
+    case 1: /* only output even frames, odd  frames are dropped; height unchanged, half framerate */
211
+    case 2: /* only output odd  frames, even frames are dropped; height unchanged, half framerate */
212
+        out = avfilter_ref_buffer(tinterlace->mode == 2 ? cur : next, AV_PERM_READ);
213
+        avfilter_unref_buffer(tinterlace->next);
214
+        tinterlace->next = NULL;
215
+        break;
216
+
217
+    case 3: /* expand each frame to double height, but pad alternate
218
+             * lines with black; framerate unchanged */
219
+        out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
220
+        avfilter_copy_buffer_ref_props(out, cur);
221
+        out->video->h = outlink->h;
222
+
223
+        field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER;
224
+        /* copy upper and lower fields */
225
+        copy_picture_field(out->data, out->linesize,
226
+                           cur->data, cur->linesize,
227
+                           inlink->format, inlink->w, inlink->h,
228
+                           FIELD_UPPER_AND_LOWER, 1, field);
229
+        /* pad with black the other field */
230
+        copy_picture_field(out->data, out->linesize,
231
+                           tinterlace->black_data, tinterlace->black_linesize,
232
+                           inlink->format, inlink->w, inlink->h,
233
+                           FIELD_UPPER_AND_LOWER, 1, !field);
234
+        break;
235
+
236
+    case 4: /* interleave upper lines from odd frames with lower lines from even frames,
237
+             * halving the frame rate and preserving image height */
238
+        out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
239
+        avfilter_copy_buffer_ref_props(out, cur);
240
+        out->video->interlaced = 1;
241
+        out->video->top_field_first = 1;
242
+
243
+        /* copy upper field from cur */
244
+        copy_picture_field(out->data, out->linesize,
245
+                           cur->data, cur->linesize,
246
+                           inlink->format, inlink->w, inlink->h,
247
+                           FIELD_UPPER, 1, FIELD_UPPER);
248
+        /* copy lower fields from next */
249
+        copy_picture_field(out->data, out->linesize,
250
+                           next->data, next->linesize,
251
+                           inlink->format, inlink->w, inlink->h,
252
+                           FIELD_LOWER, 1, FIELD_LOWER);
253
+        avfilter_unref_buffer(tinterlace->next);
254
+        tinterlace->next = NULL;
255
+        break;
256
+    }
257
+
258
+    avfilter_start_frame(outlink, out);
259
+    avfilter_draw_slice(outlink, 0, outlink->h, 1);
260
+    avfilter_end_frame(outlink);
261
+
262
+    tinterlace->frame++;
263
+}
264
+
265
+static int poll_frame(AVFilterLink *outlink)
266
+{
267
+    TInterlaceContext *tinterlace = outlink->src->priv;
268
+    AVFilterLink *inlink = outlink->src->inputs[0];
269
+    int ret, val;
270
+
271
+    val = avfilter_poll_frame(inlink);
272
+
273
+    if (val == 1 && !tinterlace->next) {
274
+        if ((ret = avfilter_request_frame(inlink)) < 0)
275
+            return ret;
276
+        val = avfilter_poll_frame(inlink);
277
+    }
278
+    assert(tinterlace->next);
279
+
280
+    return val;
281
+}
282
+
283
+static int request_frame(AVFilterLink *outlink)
284
+{
285
+    TInterlaceContext *tinterlace = outlink->src->priv;
286
+    AVFilterLink *inlink = outlink->src->inputs[0];
287
+
288
+    do {
289
+        int ret;
290
+
291
+        if ((ret = avfilter_request_frame(inlink)) < 0)
292
+            return ret;
293
+    } while (!tinterlace->cur);
294
+
295
+    return 0;
296
+}
297
+
298
+static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { }
299
+
300
+AVFilter avfilter_vf_tinterlace = {
301
+    .name          = "tinterlace",
302
+    .description   = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
303
+    .priv_size     = sizeof(TInterlaceContext),
304
+    .init          = init,
305
+    .uninit        = uninit,
306
+    .query_formats = query_formats,
307
+
308
+    .inputs = (const AVFilterPad[]) {
309
+        { .name          = "default",
310
+          .type          = AVMEDIA_TYPE_VIDEO,
311
+          .start_frame   = start_frame,
312
+          .draw_slice    = null_draw_slice,
313
+          .end_frame     = end_frame, },
314
+        { .name = NULL}
315
+    },
316
+    .outputs = (const AVFilterPad[]) {
317
+        { .name          = "default",
318
+          .type          = AVMEDIA_TYPE_VIDEO,
319
+          .config_props  = config_out_props,
320
+          .poll_frame    = poll_frame,
321
+          .request_frame = request_frame },
322
+        { .name = NULL}
323
+    },
324
+};