Browse code

lavfi: adding dejudder filter to remove judder produced by partially telecined material.

Signed-off-by: Nicholas Robbins <nickrobbins@yahoo.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Nicholas Robbins authored on 2014/02/15 06:15:44
Showing 5 changed files
... ...
@@ -27,6 +27,7 @@ version <next>
27 27
 - Mirillis FIC video decoder
28 28
 - Support DNx444
29 29
 - libx265 encoder
30
+- dejudder filter
30 31
 
31 32
 
32 33
 version 2.1:
... ...
@@ -3215,6 +3215,38 @@ Set whether or not chroma is considered in the metric calculations. Default is
3215 3215
 @code{1}.
3216 3216
 @end table
3217 3217
 
3218
+@section dejudder
3219
+
3220
+Remove judder produced by partially interlaced telecined content.
3221
+
3222
+Judder can be introduced, for instance, by @ref{pullup} filter. If the original
3223
+source was partially telecined content then the output of @code{pullup,dejudder}
3224
+will have a variable frame rate. May change the recorded frame rate of the
3225
+container. Aside from that change, this filter will not affect constant frame
3226
+rate video.
3227
+
3228
+The option available in this filter is:
3229
+@table @option
3230
+
3231
+@item cycle
3232
+Specify the length of the window over which the judder repeats.
3233
+
3234
+Accepts any interger greater than 1. Useful values are:
3235
+@table @samp
3236
+
3237
+@item 4
3238
+If the original was telecined from 24 to 30 fps (Film to NTSC).
3239
+
3240
+@item 5
3241
+If the original was telecined from 25 to 30 fps (PAL to NTSC).
3242
+
3243
+@item 20
3244
+If a mixture of the two.
3245
+@end table
3246
+
3247
+The default is @samp{4}.
3248
+@end table
3249
+
3218 3250
 @section delogo
3219 3251
 
3220 3252
 Suppress a TV station logo by a simple interpolation of the surrounding
... ...
@@ -6633,6 +6665,7 @@ On this example the input file being processed is compared with the
6633 6633
 reference file @file{ref_movie.mpg}. The PSNR of each individual frame
6634 6634
 is stored in @file{stats.log}.
6635 6635
 
6636
+@anchor{pullup}
6636 6637
 @section pullup
6637 6638
 
6638 6639
 Pulldown reversal (inverse telecine) filter, capable of handling mixed
... ...
@@ -127,6 +127,7 @@ OBJS-$(CONFIG_CROPDETECT_FILTER)             += vf_cropdetect.o
127 127
 OBJS-$(CONFIG_CURVES_FILTER)                 += vf_curves.o
128 128
 OBJS-$(CONFIG_DCTDNOIZ_FILTER)               += vf_dctdnoiz.o
129 129
 OBJS-$(CONFIG_DECIMATE_FILTER)               += vf_decimate.o
130
+OBJS-$(CONFIG_DEJUDDER_FILTER)               += vf_dejudder.o
130 131
 OBJS-$(CONFIG_DELOGO_FILTER)                 += vf_delogo.o
131 132
 OBJS-$(CONFIG_DESHAKE_FILTER)                += vf_deshake.o
132 133
 OBJS-$(CONFIG_DRAWBOX_FILTER)                += vf_drawbox.o
... ...
@@ -122,6 +122,7 @@ void avfilter_register_all(void)
122 122
     REGISTER_FILTER(CURVES,         curves,         vf);
123 123
     REGISTER_FILTER(DCTDNOIZ,       dctdnoiz,       vf);
124 124
     REGISTER_FILTER(DECIMATE,       decimate,       vf);
125
+    REGISTER_FILTER(DEJUDDER,       dejudder,       vf);
125 126
     REGISTER_FILTER(DELOGO,         delogo,         vf);
126 127
     REGISTER_FILTER(DESHAKE,        deshake,        vf);
127 128
     REGISTER_FILTER(DRAWBOX,        drawbox,        vf);
128 129
new file mode 100644
... ...
@@ -0,0 +1,187 @@
0
+/*
1
+ * Copyright (c) 2014 Nicholas Robbins
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
+/**
21
+ * @file
22
+ * remove judder in video stream
23
+ *
24
+ * Algorithm:
25
+ *    - If the old packets had PTS of old_pts[i]. Replace these with new
26
+ *      value based on the running average of the last n=cycle frames. So
27
+ *
28
+ *      new_pts[i] = Sum(k=i-n+1, i, old_pts[k])/n
29
+ *                        + (old_pts[i]-old_pts[i-n])*(n-1)/2n
30
+ *
31
+ *      For any repeating pattern of length n of judder this will produce
32
+ *      an even progression of PTS's.
33
+ *
34
+ *    - In order to avoid calculating this sum ever frame, a running tally
35
+ *      is maintained in ctx->new_pts. Each frame the new term at the start
36
+ *      of the sum is added, the one and the end is removed, and the offset
37
+ *      terms (second line in formula above) are recalculated.
38
+ *
39
+ *    - To aid in this a ringbuffer of the last n-2 PTS's is maintained in
40
+ *      ctx->ringbuff. With the indices of the first two and last two entries
41
+ *      stored in i1, i2, i3, & i4.
42
+ *
43
+ *    - To ensure that the new PTS's are integers, time_base is divided
44
+ *      by 2n. This removes the division in the new_pts calculation.
45
+ *
46
+ *    - frame_rate is also multiplied by 2n to allow the frames to fall
47
+ *      where they may in what may now be a VFR output. This produces more
48
+ *      even output then setting frame_rate=1/0 in practice.
49
+ */
50
+
51
+#include "libavutil/opt.h"
52
+#include "libavutil/mathematics.h"
53
+#include "avfilter.h"
54
+#include "internal.h"
55
+#include "video.h"
56
+
57
+typedef struct {
58
+    const AVClass *class;
59
+    int64_t *ringbuff;
60
+    int i1, i2, i3, i4;
61
+    int64_t new_pts;
62
+    int start_count;
63
+
64
+    /* options */
65
+    int cycle;
66
+} DejudderContext;
67
+
68
+#define OFFSET(x) offsetof(DejudderContext, x)
69
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
70
+
71
+static const AVOption dejudder_options[] = {
72
+    {"cycle", "set the length of the cycle to use for dejuddering",
73
+        OFFSET(cycle), AV_OPT_TYPE_INT, {.i64 = 4}, 2, 240, .flags = FLAGS},
74
+    {NULL}
75
+};
76
+
77
+AVFILTER_DEFINE_CLASS(dejudder);
78
+
79
+static int config_out_props(AVFilterLink *outlink)
80
+{
81
+    AVFilterContext *ctx = outlink->src;
82
+    DejudderContext *dj = ctx->priv;
83
+    AVFilterLink *inlink = outlink->src->inputs[0];
84
+
85
+    outlink->time_base = av_mul_q(inlink->time_base, av_make_q(1, 2 * dj->cycle));
86
+    outlink->frame_rate = av_mul_q(inlink->frame_rate, av_make_q(2 * dj->cycle, 1));
87
+
88
+    av_log(ctx, AV_LOG_VERBOSE, "cycle:%d\n", dj->cycle);
89
+
90
+    return 0;
91
+}
92
+
93
+static av_cold int dejudder_init(AVFilterContext *ctx)
94
+{
95
+    DejudderContext *dj = ctx->priv;
96
+
97
+    dj->ringbuff = av_mallocz(sizeof(*dj->ringbuff) * (dj->cycle+2));
98
+    if (!dj->ringbuff)
99
+        return AVERROR(ENOMEM);
100
+
101
+    dj->new_pts = 0;
102
+    dj->i1 = 0;
103
+    dj->i2 = 1;
104
+    dj->i3 = 2;
105
+    dj->i4 = 3;
106
+    dj->start_count = dj->cycle + 2;
107
+
108
+    return 0;
109
+}
110
+
111
+static av_cold void dejudder_uninit(AVFilterContext *ctx)
112
+{
113
+    DejudderContext *dj = ctx->priv;
114
+
115
+    av_freep(&(dj->ringbuff));
116
+}
117
+
118
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
119
+{
120
+    int k;
121
+    AVFilterContext *ctx  = inlink->dst;
122
+    AVFilterLink *outlink = ctx->outputs[0];
123
+    DejudderContext *dj   = ctx->priv;
124
+    int64_t *judbuff      = dj->ringbuff;
125
+    int64_t next_pts      = frame->pts;
126
+    int64_t offset;
127
+
128
+    if (next_pts == AV_NOPTS_VALUE)
129
+        return ff_filter_frame(outlink, frame);
130
+
131
+    if (dj->start_count) {
132
+        dj->start_count--;
133
+        dj->new_pts = next_pts * 2 * dj->cycle;
134
+    } else {
135
+        if (next_pts < judbuff[dj->i2]) {
136
+            offset = next_pts + judbuff[dj->i3] - judbuff[dj->i4] - judbuff[dj->i1];
137
+            for (k = 0; k < dj->cycle + 2; k++)
138
+                judbuff[k] += offset;
139
+        }
140
+        dj->new_pts += (dj->cycle - 1) * (judbuff[dj->i3] - judbuff[dj->i1])
141
+                    + (dj->cycle + 1) * (next_pts - judbuff[dj->i4]);
142
+    }
143
+
144
+    judbuff[dj->i2] = next_pts;
145
+    dj->i1 = dj->i2;
146
+    dj->i2 = dj->i3;
147
+    dj->i3 = dj->i4;
148
+    dj->i4 = (dj->i4 + 1) % (dj->cycle + 2);
149
+
150
+    frame->pts = dj->new_pts;
151
+
152
+    for (k = 0; k < dj->cycle + 2; k++)
153
+        av_log(ctx, AV_LOG_DEBUG, "%"PRId64"\t", judbuff[k]);
154
+    av_log(ctx, AV_LOG_DEBUG, "next=%"PRId64", new=%"PRId64"\n", next_pts, frame->pts);
155
+
156
+    return ff_filter_frame(outlink, frame);
157
+}
158
+
159
+static const AVFilterPad dejudder_inputs[] = {
160
+    {
161
+        .name         = "default",
162
+        .type         = AVMEDIA_TYPE_VIDEO,
163
+        .filter_frame = filter_frame,
164
+    },
165
+    { NULL }
166
+};
167
+
168
+static const AVFilterPad dejudder_outputs[] = {
169
+    {
170
+        .name = "default",
171
+        .type = AVMEDIA_TYPE_VIDEO,
172
+        .config_props = config_out_props,
173
+    },
174
+    { NULL }
175
+};
176
+
177
+AVFilter ff_vf_dejudder = {
178
+    .name        = "dejudder",
179
+    .description = NULL_IF_CONFIG_SMALL("Remove judder produced by pullup."),
180
+    .priv_size   = sizeof(DejudderContext),
181
+    .priv_class  = &dejudder_class,
182
+    .inputs      = dejudder_inputs,
183
+    .outputs     = dejudder_outputs,
184
+    .init        = dejudder_init,
185
+    .uninit      = dejudder_uninit,
186
+};