2 * Copyright (C) 2012 British Broadcasting Corporation, All Rights Reserved
3 * Author of de-interlace algorithm: Jim Easterbrook for BBC R&D
4 * Based on the process described by Martin Weston for BBC R&D
5 * Author of FFmpeg filter: Mark Himsley for BBC Broadcast Systems Development
7 * This file is part of FFmpeg.
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "libavutil/common.h"
25 #include "libavutil/imgutils.h"
26 #include "libavutil/opt.h"
27 #include "libavutil/pixdesc.h"
33 typedef struct W3FDIFContext
{
35 int filter
; ///< 0 is simple, 1 is more complex
36 int deint
; ///< which frames to deinterlace
37 int linesize
[4]; ///< bytes of pixel data per line for each plane
38 int planeheight
[4]; ///< height of each plane
39 int field
; ///< which field are we on, 0 or 1
42 AVFrame
*prev
, *cur
, *next
; ///< previous, current, next frames
43 int32_t *work_line
; ///< line we are calculating
46 #define OFFSET(x) offsetof(W3FDIFContext, x)
47 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
48 #define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit }
50 static const AVOption w3fdif_options
[] = {
51 { "filter", "specify the filter", OFFSET(filter
), AV_OPT_TYPE_INT
, {.i64
=1}, 0, 1, FLAGS
, "filter" },
52 CONST("simple", NULL
, 0, "filter"),
53 CONST("complex", NULL
, 1, "filter"),
54 { "deint", "specify which frames to deinterlace", OFFSET(deint
), AV_OPT_TYPE_INT
, {.i64
=0}, 0, 1, FLAGS
, "deint" },
55 CONST("all", "deinterlace all frames", 0, "deint"),
56 CONST("interlaced", "only deinterlace frames marked as interlaced", 1, "deint"),
60 AVFILTER_DEFINE_CLASS(w3fdif
);
62 static int query_formats(AVFilterContext
*ctx
)
64 static const enum AVPixelFormat pix_fmts
[] = {
65 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV411P
,
66 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV422P
,
67 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_YUV444P
,
68 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ440P
,
69 AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
71 AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUVA444P
,
72 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
77 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
82 static int config_input(AVFilterLink
*inlink
)
84 W3FDIFContext
*s
= inlink
->dst
->priv
;
85 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(inlink
->format
);
88 if ((ret
= av_image_fill_linesizes(s
->linesize
, inlink
->format
, inlink
->w
)) < 0)
91 s
->planeheight
[1] = s
->planeheight
[2] = FF_CEIL_RSHIFT(inlink
->h
, desc
->log2_chroma_h
);
92 s
->planeheight
[0] = s
->planeheight
[3] = inlink
->h
;
94 s
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
95 s
->work_line
= av_calloc(s
->linesize
[0], sizeof(*s
->work_line
));
97 return AVERROR(ENOMEM
);
102 static int config_output(AVFilterLink
*outlink
)
104 AVFilterLink
*inlink
= outlink
->src
->inputs
[0];
106 outlink
->time_base
.num
= inlink
->time_base
.num
;
107 outlink
->time_base
.den
= inlink
->time_base
.den
* 2;
108 outlink
->frame_rate
.num
= inlink
->frame_rate
.num
* 2;
109 outlink
->frame_rate
.den
= inlink
->frame_rate
.den
;
110 outlink
->flags
|= FF_LINK_FLAG_REQUEST_LOOP
;
116 * Filter coefficients from PH-2071, scaled by 256 * 256.
117 * Each set of coefficients has a set for low-frequencies and high-frequencies.
118 * n_coef_lf[] and n_coef_hf[] are the number of coefs for simple and more-complex.
119 * It is important for later that n_coef_lf[] is even and n_coef_hf[] is odd.
120 * coef_lf[][] and coef_hf[][] are the coefficients for low-frequencies
121 * and high-frequencies for simple and more-complex mode.
123 static const int8_t n_coef_lf
[2] = { 2, 4 };
124 static const int32_t coef_lf
[2][4] = {{ 32768, 32768, 0, 0},
125 { -1704, 34472, 34472, -1704}};
126 static const int8_t n_coef_hf
[2] = { 3, 5 };
127 static const int32_t coef_hf
[2][5] = {{ -4096, 8192, -4096, 0, 0},
128 { 2032, -7602, 11140, -7602, 2032}};
130 static void deinterlace_plane(AVFilterContext
*ctx
, AVFrame
*out
,
131 const AVFrame
*cur
, const AVFrame
*adj
,
132 const int filter
, const int plane
)
134 W3FDIFContext
*s
= ctx
->priv
;
135 uint8_t *in_line
, *in_lines_cur
[5], *in_lines_adj
[5];
136 uint8_t *out_line
, *out_pixel
;
137 int32_t *work_line
, *work_pixel
;
138 uint8_t *cur_data
= cur
->data
[plane
];
139 uint8_t *adj_data
= adj
->data
[plane
];
140 uint8_t *dst_data
= out
->data
[plane
];
141 const int linesize
= s
->linesize
[plane
];
142 const int height
= s
->planeheight
[plane
];
143 const int cur_line_stride
= cur
->linesize
[plane
];
144 const int adj_line_stride
= adj
->linesize
[plane
];
145 const int dst_line_stride
= out
->linesize
[plane
];
146 int i
, j
, y_in
, y_out
;
148 /* copy unchanged the lines of the field */
149 y_out
= s
->field
== cur
->top_field_first
;
151 in_line
= cur_data
+ (y_out
* cur_line_stride
);
152 out_line
= dst_data
+ (y_out
* dst_line_stride
);
154 while (y_out
< height
) {
155 memcpy(out_line
, in_line
, linesize
);
157 in_line
+= cur_line_stride
* 2;
158 out_line
+= dst_line_stride
* 2;
161 /* interpolate other lines of the field */
162 y_out
= s
->field
!= cur
->top_field_first
;
164 out_line
= dst_data
+ (y_out
* dst_line_stride
);
166 while (y_out
< height
) {
167 /* clear workspace */
168 memset(s
->work_line
, 0, sizeof(*s
->work_line
) * linesize
);
170 /* get low vertical frequencies from current field */
171 for (j
= 0; j
< n_coef_lf
[filter
]; j
++) {
172 y_in
= (y_out
+ 1) + (j
* 2) - n_coef_lf
[filter
];
176 while (y_in
>= height
)
179 in_lines_cur
[j
] = cur_data
+ (y_in
* cur_line_stride
);
182 work_line
= s
->work_line
;
183 switch (n_coef_lf
[filter
]) {
185 for (i
= 0; i
< linesize
; i
++) {
186 *work_line
+= *in_lines_cur
[0]++ * coef_lf
[filter
][0];
187 *work_line
++ += *in_lines_cur
[1]++ * coef_lf
[filter
][1];
191 for (i
= 0; i
< linesize
; i
++) {
192 *work_line
+= *in_lines_cur
[0]++ * coef_lf
[filter
][0];
193 *work_line
+= *in_lines_cur
[1]++ * coef_lf
[filter
][1];
194 *work_line
+= *in_lines_cur
[2]++ * coef_lf
[filter
][2];
195 *work_line
++ += *in_lines_cur
[3]++ * coef_lf
[filter
][3];
199 /* get high vertical frequencies from adjacent fields */
200 for (j
= 0; j
< n_coef_hf
[filter
]; j
++) {
201 y_in
= (y_out
+ 1) + (j
* 2) - n_coef_hf
[filter
];
205 while (y_in
>= height
)
208 in_lines_cur
[j
] = cur_data
+ (y_in
* cur_line_stride
);
209 in_lines_adj
[j
] = adj_data
+ (y_in
* adj_line_stride
);
212 work_line
= s
->work_line
;
213 switch (n_coef_hf
[filter
]) {
215 for (i
= 0; i
< linesize
; i
++) {
216 *work_line
+= *in_lines_cur
[0]++ * coef_hf
[filter
][0];
217 *work_line
+= *in_lines_adj
[0]++ * coef_hf
[filter
][0];
218 *work_line
+= *in_lines_cur
[1]++ * coef_hf
[filter
][1];
219 *work_line
+= *in_lines_adj
[1]++ * coef_hf
[filter
][1];
220 *work_line
+= *in_lines_cur
[2]++ * coef_hf
[filter
][2];
221 *work_line
++ += *in_lines_adj
[2]++ * coef_hf
[filter
][2];
225 for (i
= 0; i
< linesize
; i
++) {
226 *work_line
+= *in_lines_cur
[0]++ * coef_hf
[filter
][0];
227 *work_line
+= *in_lines_adj
[0]++ * coef_hf
[filter
][0];
228 *work_line
+= *in_lines_cur
[1]++ * coef_hf
[filter
][1];
229 *work_line
+= *in_lines_adj
[1]++ * coef_hf
[filter
][1];
230 *work_line
+= *in_lines_cur
[2]++ * coef_hf
[filter
][2];
231 *work_line
+= *in_lines_adj
[2]++ * coef_hf
[filter
][2];
232 *work_line
+= *in_lines_cur
[3]++ * coef_hf
[filter
][3];
233 *work_line
+= *in_lines_adj
[3]++ * coef_hf
[filter
][3];
234 *work_line
+= *in_lines_cur
[4]++ * coef_hf
[filter
][4];
235 *work_line
++ += *in_lines_adj
[4]++ * coef_hf
[filter
][4];
239 /* save scaled result to the output frame, scaling down by 256 * 256 */
240 work_pixel
= s
->work_line
;
241 out_pixel
= out_line
;
243 for (j
= 0; j
< linesize
; j
++, out_pixel
++, work_pixel
++)
244 *out_pixel
= av_clip(*work_pixel
, 0, 255 * 256 * 256) >> 16;
246 /* move on to next line */
248 out_line
+= dst_line_stride
* 2;
252 static int filter(AVFilterContext
*ctx
, int is_second
)
254 W3FDIFContext
*s
= ctx
->priv
;
255 AVFilterLink
*outlink
= ctx
->outputs
[0];
259 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
261 return AVERROR(ENOMEM
);
262 av_frame_copy_props(out
, s
->cur
);
263 out
->interlaced_frame
= 0;
266 if (out
->pts
!= AV_NOPTS_VALUE
)
269 int64_t cur_pts
= s
->cur
->pts
;
270 int64_t next_pts
= s
->next
->pts
;
272 if (next_pts
!= AV_NOPTS_VALUE
&& cur_pts
!= AV_NOPTS_VALUE
) {
273 out
->pts
= cur_pts
+ next_pts
;
275 out
->pts
= AV_NOPTS_VALUE
;
279 adj
= s
->field
? s
->next
: s
->prev
;
280 for (plane
= 0; plane
< s
->nb_planes
; plane
++)
281 deinterlace_plane(ctx
, out
, s
->cur
, adj
, s
->filter
, plane
);
283 s
->field
= !s
->field
;
285 return ff_filter_frame(outlink
, out
);
288 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
290 AVFilterContext
*ctx
= inlink
->dst
;
291 W3FDIFContext
*s
= ctx
->priv
;
294 av_frame_free(&s
->prev
);
300 s
->cur
= av_frame_clone(s
->next
);
302 return AVERROR(ENOMEM
);
305 if ((s
->deint
&& !s
->cur
->interlaced_frame
) || ctx
->is_disabled
) {
306 AVFrame
*out
= av_frame_clone(s
->cur
);
308 return AVERROR(ENOMEM
);
310 av_frame_free(&s
->prev
);
311 if (out
->pts
!= AV_NOPTS_VALUE
)
313 return ff_filter_frame(ctx
->outputs
[0], out
);
319 ret
= filter(ctx
, 0);
323 return filter(ctx
, 1);
326 static int request_frame(AVFilterLink
*outlink
)
328 AVFilterContext
*ctx
= outlink
->src
;
329 W3FDIFContext
*s
= ctx
->priv
;
337 ret
= ff_request_frame(ctx
->inputs
[0]);
339 if (ret
== AVERROR_EOF
&& s
->cur
) {
340 AVFrame
*next
= av_frame_clone(s
->next
);
342 return AVERROR(ENOMEM
);
343 next
->pts
= s
->next
->pts
* 2 - s
->cur
->pts
;
344 filter_frame(ctx
->inputs
[0], next
);
346 } else if (ret
< 0) {
354 static av_cold
void uninit(AVFilterContext
*ctx
)
356 W3FDIFContext
*s
= ctx
->priv
;
358 av_frame_free(&s
->prev
);
359 av_frame_free(&s
->cur
);
360 av_frame_free(&s
->next
);
361 av_freep(&s
->work_line
);
364 static const AVFilterPad w3fdif_inputs
[] = {
367 .type
= AVMEDIA_TYPE_VIDEO
,
368 .filter_frame
= filter_frame
,
369 .config_props
= config_input
,
374 static const AVFilterPad w3fdif_outputs
[] = {
377 .type
= AVMEDIA_TYPE_VIDEO
,
378 .config_props
= config_output
,
379 .request_frame
= request_frame
,
384 AVFilter ff_vf_w3fdif
= {
386 .description
= NULL_IF_CONFIG_SMALL("Apply Martin Weston three field deinterlace."),
387 .priv_size
= sizeof(W3FDIFContext
),
388 .priv_class
= &w3fdif_class
,
390 .query_formats
= query_formats
,
391 .inputs
= w3fdif_inputs
,
392 .outputs
= w3fdif_outputs
,
393 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
,