Browse code

telecine filter

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

Paul B Mahol authored on 2013/04/06 22:45:52
Showing 7 changed files
... ...
@@ -19,6 +19,7 @@ version <next>:
19 19
 - separatefields filter
20 20
 - libquvi demuxer
21 21
 - uniform options syntax across all filters
22
+- telecine filter
22 23
 
23 24
 
24 25
 version 1.2:
... ...
@@ -458,6 +458,45 @@ slope
458 458
 Determine how steep is the filter's shelf transition.
459 459
 @end table
460 460
 
461
+@section telecine
462
+
463
+Apply telecine process to the video.
464
+
465
+This filter accepts the following options:
466
+
467
+@table @option
468
+@item first_field
469
+@table @samp
470
+@item top, t
471
+top field first
472
+@item bottom, b
473
+bottom field first
474
+The default value is @code{top}.
475
+@end table
476
+
477
+@item pattern
478
+A string of numbers representing the pulldown pattern you wish to apply.
479
+The default value is @code{23}.
480
+@end table
481
+
482
+@example
483
+Some typical patterns:
484
+
485
+NTSC output (30i):
486
+27.5p: 32222
487
+24p: 23 (classic)
488
+24p: 2332 (preferred)
489
+20p: 33
490
+18p: 334
491
+16p: 3444
492
+
493
+PAL output (25i):
494
+27.5p: 12222
495
+24p: 222222222223 ("Euro pulldown")
496
+16.67p: 33
497
+16p: 33333334
498
+@end example
499
+
461 500
 @section treble
462 501
 
463 502
 Boost or cut treble (upper) frequencies of the audio using a two-pole
... ...
@@ -164,6 +164,7 @@ OBJS-$(CONFIG_STEREO3D_FILTER)               += vf_stereo3d.o
164 164
 OBJS-$(CONFIG_SUBTITLES_FILTER)              += vf_subtitles.o
165 165
 OBJS-$(CONFIG_SUPER2XSAI_FILTER)             += vf_super2xsai.o
166 166
 OBJS-$(CONFIG_SWAPUV_FILTER)                 += vf_swapuv.o
167
+OBJS-$(CONFIG_TELECINE_FILTER)               += vf_telecine.o
167 168
 OBJS-$(CONFIG_THUMBNAIL_FILTER)              += vf_thumbnail.o
168 169
 OBJS-$(CONFIG_TILE_FILTER)                   += vf_tile.o
169 170
 OBJS-$(CONFIG_TINTERLACE_FILTER)             += vf_tinterlace.o
... ...
@@ -159,6 +159,7 @@ void avfilter_register_all(void)
159 159
     REGISTER_FILTER(SUBTITLES,      subtitles,      vf);
160 160
     REGISTER_FILTER(SUPER2XSAI,     super2xsai,     vf);
161 161
     REGISTER_FILTER(SWAPUV,         swapuv,         vf);
162
+    REGISTER_FILTER(TELECINE,       telecine,       vf);
162 163
     REGISTER_FILTER(THUMBNAIL,      thumbnail,      vf);
163 164
     REGISTER_FILTER(TILE,           tile,           vf);
164 165
     REGISTER_FILTER(TINTERLACE,     tinterlace,     vf);
... ...
@@ -759,6 +759,7 @@ int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque
759 759
         !strcmp(filter->filter->name, "split"    ) ||
760 760
         !strcmp(filter->filter->name, "stereo3d" ) ||
761 761
         !strcmp(filter->filter->name, "subtitles") ||
762
+        !strcmp(filter->filter->name, "telecine" ) ||
762 763
         !strcmp(filter->filter->name, "testsrc"  ) ||
763 764
         !strcmp(filter->filter->name, "thumbnail") ||
764 765
         !strcmp(filter->filter->name, "tile") ||
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  3
32
-#define LIBAVFILTER_VERSION_MINOR  51
32
+#define LIBAVFILTER_VERSION_MINOR  52
33 33
 #define LIBAVFILTER_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
new file mode 100644
... ...
@@ -0,0 +1,283 @@
0
+/*
1
+ * Copyright (c) 2012 Rudolf Polzer
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
+/**
22
+ * @file telecine filter, heavily based from mpv-player:TOOLS/vf_dlopen/telecine.c by
23
+ * Rudolf Polzer.
24
+ */
25
+
26
+#include "libavutil/avstring.h"
27
+#include "libavutil/imgutils.h"
28
+#include "libavutil/opt.h"
29
+#include "libavutil/pixdesc.h"
30
+#include "avfilter.h"
31
+#include "formats.h"
32
+#include "internal.h"
33
+#include "video.h"
34
+
35
+typedef struct {
36
+    const AVClass *class;
37
+    int first_field;
38
+    char *pattern;
39
+    unsigned int pattern_pos;
40
+
41
+    AVRational pts;
42
+    double ts_unit;
43
+    int out_cnt;
44
+    int occupied;
45
+    int64_t frame_count;
46
+
47
+    int nb_planes;
48
+    int planeheight[4];
49
+    int stride[4];
50
+
51
+    AVFrame *frame[5];
52
+    AVFrame *temp;
53
+} TelecineContext;
54
+
55
+#define OFFSET(x) offsetof(TelecineContext, x)
56
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
57
+
58
+static const AVOption telecine_options[] = {
59
+    {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT,   {.i64=0}, 0, 1, FLAGS, "field"},
60
+        {"top",    "select top field first",                0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
61
+        {"t",      "select top field first",                0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"},
62
+        {"bottom", "select bottom field first",             0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
63
+        {"b",      "select bottom field first",             0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"},
64
+    {"pattern", "pattern that describe for how many fields a frame is to be displayed", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str="23"}, 0, 0, FLAGS},
65
+    {NULL}
66
+};
67
+
68
+AVFILTER_DEFINE_CLASS(telecine);
69
+
70
+static av_cold int init(AVFilterContext *ctx, const char *args)
71
+{
72
+    TelecineContext *tc = ctx->priv;
73
+    const char *p;
74
+    int max = 0;
75
+
76
+    if (!strlen(tc->pattern)) {
77
+        av_log(ctx, AV_LOG_ERROR, "No pattern provided.\n");
78
+        return AVERROR_INVALIDDATA;
79
+    }
80
+
81
+    for (p = tc->pattern; *p; p++) {
82
+        if (!av_isdigit(*p)) {
83
+            av_log(ctx, AV_LOG_ERROR, "Provided pattern includes non-numeric characters.\n");
84
+            return AVERROR_INVALIDDATA;
85
+        }
86
+
87
+        max = FFMAX(*p - '0', max);
88
+        tc->pts.num += 2;
89
+        tc->pts.den += *p - '0';
90
+    }
91
+
92
+    tc->out_cnt = (max + 1) / 2;
93
+    av_log(ctx, AV_LOG_INFO, "Telecine pattern %s yields up to %d frames per frame, pts advance factor: %d/%d\n",
94
+           tc->pattern, tc->out_cnt, tc->pts.num, tc->pts.den);
95
+
96
+    return 0;
97
+}
98
+
99
+static int query_formats(AVFilterContext *ctx)
100
+{
101
+    AVFilterFormats *pix_fmts = NULL;
102
+    int fmt;
103
+
104
+    for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) {
105
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
106
+        if (!(desc->flags & PIX_FMT_HWACCEL))
107
+            ff_add_format(&pix_fmts, fmt);
108
+    }
109
+
110
+    ff_set_common_formats(ctx, pix_fmts);
111
+    return 0;
112
+}
113
+
114
+static int config_input(AVFilterLink *inlink)
115
+{
116
+    TelecineContext *tc = inlink->dst->priv;
117
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
118
+    int i, ret;
119
+
120
+    tc->temp = ff_get_video_buffer(inlink, inlink->w, inlink->h);
121
+    if (!tc->temp)
122
+        return AVERROR(ENOMEM);
123
+    for (i = 0; i < tc->out_cnt; i++) {
124
+        tc->frame[i] = ff_get_video_buffer(inlink, inlink->w, inlink->h);
125
+        if (!tc->frame[i])
126
+            return AVERROR(ENOMEM);
127
+    }
128
+
129
+    if ((ret = av_image_fill_linesizes(tc->stride, inlink->format, inlink->w)) < 0)
130
+        return ret;
131
+
132
+    tc->planeheight[1] = tc->planeheight[2] = inlink->h >> desc->log2_chroma_h;
133
+    tc->planeheight[0] = tc->planeheight[3] = inlink->h;
134
+
135
+    tc->nb_planes = av_pix_fmt_count_planes(inlink->format);
136
+
137
+    return 0;
138
+}
139
+
140
+static int config_output(AVFilterLink *outlink)
141
+{
142
+    AVFilterContext *ctx = outlink->src;
143
+    TelecineContext *tc = ctx->priv;
144
+    const AVFilterLink *inlink = ctx->inputs[0];
145
+    AVRational fps = inlink->frame_rate;
146
+
147
+    if (!fps.num || !fps.den) {
148
+        av_log(ctx, AV_LOG_ERROR, "The input needs a constant frame rate; "
149
+               "current rate of %d/%d is invalid\n", fps.num, fps.den);
150
+        return AVERROR(EINVAL);
151
+    }
152
+    fps = av_mul_q(fps, av_inv_q(tc->pts));
153
+    av_log(ctx, AV_LOG_VERBOSE, "FPS: %d/%d -> %d/%d\n",
154
+           inlink->frame_rate.num, inlink->frame_rate.den, fps.num, fps.den);
155
+
156
+    outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
157
+    outlink->frame_rate = fps;
158
+    outlink->time_base = av_mul_q(inlink->time_base, tc->pts);
159
+
160
+    tc->ts_unit = av_q2d(av_inv_q(av_mul_q(fps, outlink->time_base)));
161
+
162
+    return 0;
163
+}
164
+
165
+static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref)
166
+{
167
+    AVFilterContext *ctx = inlink->dst;
168
+    AVFilterLink *outlink = ctx->outputs[0];
169
+    TelecineContext *tc = ctx->priv;
170
+    int i, len, ret = 0, nout = 0;
171
+
172
+    len = tc->pattern[tc->pattern_pos] - '0';
173
+
174
+    tc->pattern_pos++;
175
+    if (!tc->pattern[tc->pattern_pos])
176
+        tc->pattern_pos = 0;
177
+
178
+    if (!len) { // do not output any field from this frame
179
+        av_frame_free(&inpicref);
180
+        return 0;
181
+    }
182
+
183
+    if (tc->occupied) {
184
+        for (i = 0; i < tc->nb_planes; i++) {
185
+            // fill in the EARLIER field from the buffered pic
186
+            av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * tc->first_field,
187
+                                tc->frame[nout]->linesize[i] * 2,
188
+                                tc->temp->data[i] + tc->temp->linesize[i] * tc->first_field,
189
+                                tc->temp->linesize[i] * 2,
190
+                                tc->stride[i],
191
+                                (tc->planeheight[i] - tc->first_field + 1) / 2);
192
+            // fill in the LATER field from the new pic
193
+            av_image_copy_plane(tc->frame[nout]->data[i] + tc->frame[nout]->linesize[i] * !tc->first_field,
194
+                                tc->frame[nout]->linesize[i] * 2,
195
+                                inpicref->data[i] + inpicref->linesize[i] * !tc->first_field,
196
+                                inpicref->linesize[i] * 2,
197
+                                tc->stride[i],
198
+                                (tc->planeheight[i] - !tc->first_field + 1) / 2);
199
+        }
200
+        nout++;
201
+        len--;
202
+        tc->occupied = 0;
203
+    }
204
+
205
+    while (len >= 2) {
206
+        // output THIS image as-is
207
+        for (i = 0; i < tc->nb_planes; i++)
208
+            av_image_copy_plane(tc->frame[nout]->data[i], tc->frame[nout]->linesize[i],
209
+                                inpicref->data[i], inpicref->linesize[i],
210
+                                tc->stride[i],
211
+                                tc->planeheight[i]);
212
+        nout++;
213
+        len -= 2;
214
+    }
215
+
216
+    if (len >= 1) {
217
+        // copy THIS image to the buffer, we need it later
218
+        for (i = 0; i < tc->nb_planes; i++)
219
+            av_image_copy_plane(tc->temp->data[i], tc->temp->linesize[i],
220
+                                inpicref->data[i], inpicref->linesize[i],
221
+                                tc->stride[i],
222
+                                tc->planeheight[i]);
223
+        tc->occupied = 1;
224
+    }
225
+
226
+    for (i = 0; i < nout; i++) {
227
+        AVFrame *frame = av_frame_clone(tc->frame[i]);
228
+
229
+        if (!frame) {
230
+            av_frame_free(&inpicref);
231
+            return AVERROR(ENOMEM);
232
+        }
233
+
234
+        av_frame_copy_props(frame, inpicref);
235
+        frame->pts = tc->frame_count++ * tc->ts_unit;
236
+        ret = ff_filter_frame(outlink, frame);
237
+    }
238
+    av_frame_free(&inpicref);
239
+
240
+    return ret;
241
+}
242
+
243
+static av_cold void uninit(AVFilterContext *ctx)
244
+{
245
+    TelecineContext *tc = ctx->priv;
246
+    int i;
247
+
248
+    av_frame_free(&tc->temp);
249
+    for (i = 0; i < tc->out_cnt; i++)
250
+        av_frame_free(&tc->frame[i]);
251
+}
252
+
253
+static const AVFilterPad telecine_inputs[] = {
254
+    {
255
+        .name          = "default",
256
+        .type          = AVMEDIA_TYPE_VIDEO,
257
+        .filter_frame  = filter_frame,
258
+        .config_props  = config_input,
259
+    },
260
+    { NULL }
261
+};
262
+
263
+static const AVFilterPad telecine_outputs[] = {
264
+    {
265
+        .name          = "default",
266
+        .type          = AVMEDIA_TYPE_VIDEO,
267
+        .config_props  = config_output,
268
+    },
269
+    { NULL }
270
+};
271
+
272
+AVFilter avfilter_vf_telecine = {
273
+    .name          = "telecine",
274
+    .description   = NULL_IF_CONFIG_SMALL("Apply a telecine pattern."),
275
+    .priv_size     = sizeof(TelecineContext),
276
+    .priv_class    = &telecine_class,
277
+    .init          = init,
278
+    .uninit        = uninit,
279
+    .query_formats = query_formats,
280
+    .inputs        = telecine_inputs,
281
+    .outputs       = telecine_outputs,
282
+};