2 * Copyright (c) 2011 Stefano Sabatini
3 * Copyright (c) 2010 Baptiste Coudurier
4 * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com>
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with FFmpeg if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * temporal field interlace filter, ported from MPlayer/libmpcodecs
28 #include "libavutil/opt.h"
29 #include "libavutil/imgutils.h"
30 #include "libavutil/avassert.h"
40 MODE_INTERLEAVE_BOTTOM
,
47 enum TInterlaceMode mode
; ///< interlace mode selected
48 int flags
; ///< flags affecting interlacing algorithm
49 int frame
; ///< number of the output frame
50 int vsub
; ///< chroma vertical subsampling
53 uint8_t *black_data
[4]; ///< buffer used to fill padded lines
54 int black_linesize
[4];
57 #define OFFSET(x) offsetof(TInterlaceContext, x)
58 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
59 #define TINTERLACE_FLAG_VLPF 01
61 static const AVOption tinterlace_options
[] = {
62 {"mode", "select interlace mode", OFFSET(mode
), AV_OPT_TYPE_INT
, {.i64
=MODE_MERGE
}, 0, MODE_NB
-1, FLAGS
, "mode"},
63 {"merge", "merge fields", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_MERGE
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
64 {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_DROP_EVEN
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
65 {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_DROP_ODD
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
66 {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_PAD
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
67 {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_INTERLEAVE_TOP
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
68 {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_INTERLEAVE_BOTTOM
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
69 {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST
, {.i64
=MODE_INTERLACEX2
}, INT_MIN
, INT_MAX
, FLAGS
, "mode"},
71 {"flags", "set flags", OFFSET(flags
), AV_OPT_TYPE_FLAGS
, {.i64
= 0}, 0, INT_MAX
, 0, "flags" },
72 {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST
, {.i64
= TINTERLACE_FLAG_VLPF
}, INT_MIN
, INT_MAX
, FLAGS
, "flags" },
73 {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST
, {.i64
= TINTERLACE_FLAG_VLPF
}, INT_MIN
, INT_MAX
, FLAGS
, "flags" },
78 AVFILTER_DEFINE_CLASS(tinterlace
);
80 #define FULL_SCALE_YUVJ_FORMATS \
81 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P
83 static const enum AVPixelFormat full_scale_yuvj_pix_fmts
[] = {
84 FULL_SCALE_YUVJ_FORMATS
, AV_PIX_FMT_NONE
87 static int query_formats(AVFilterContext
*ctx
)
89 static const enum AVPixelFormat pix_fmts
[] = {
90 AV_PIX_FMT_YUV410P
, AV_PIX_FMT_YUV411P
,
91 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUV422P
,
92 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_YUV444P
,
93 AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUVA444P
,
94 AV_PIX_FMT_GRAY8
, FULL_SCALE_YUVJ_FORMATS
,
98 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
102 static av_cold
void uninit(AVFilterContext
*ctx
)
104 TInterlaceContext
*tinterlace
= ctx
->priv
;
106 av_frame_free(&tinterlace
->cur
);
107 av_frame_free(&tinterlace
->next
);
108 av_freep(&tinterlace
->black_data
[0]);
111 static int config_out_props(AVFilterLink
*outlink
)
113 AVFilterContext
*ctx
= outlink
->src
;
114 AVFilterLink
*inlink
= outlink
->src
->inputs
[0];
115 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(outlink
->format
);
116 TInterlaceContext
*tinterlace
= ctx
->priv
;
118 tinterlace
->vsub
= desc
->log2_chroma_h
;
119 outlink
->flags
|= FF_LINK_FLAG_REQUEST_LOOP
;
120 outlink
->w
= inlink
->w
;
121 outlink
->h
= tinterlace
->mode
== MODE_MERGE
|| tinterlace
->mode
== MODE_PAD
?
122 inlink
->h
*2 : inlink
->h
;
124 if (tinterlace
->mode
== MODE_PAD
) {
125 uint8_t black
[4] = { 16, 128, 128, 16 };
127 if (ff_fmt_is_in(outlink
->format
, full_scale_yuvj_pix_fmts
))
128 black
[0] = black
[3] = 0;
129 ret
= av_image_alloc(tinterlace
->black_data
, tinterlace
->black_linesize
,
130 outlink
->w
, outlink
->h
, outlink
->format
, 1);
134 /* fill black picture with black */
135 for (i
= 0; i
< 4 && tinterlace
->black_data
[i
]; i
++) {
136 int h
= i
== 1 || i
== 2 ? FF_CEIL_RSHIFT(outlink
->h
, desc
->log2_chroma_h
) : outlink
->h
;
137 memset(tinterlace
->black_data
[i
], black
[i
],
138 tinterlace
->black_linesize
[i
] * h
);
141 if ((tinterlace
->flags
& TINTERLACE_FLAG_VLPF
)
142 && !(tinterlace
->mode
== MODE_INTERLEAVE_TOP
143 || tinterlace
->mode
== MODE_INTERLEAVE_BOTTOM
)) {
144 av_log(ctx
, AV_LOG_WARNING
, "low_pass_filter flag ignored with mode %d\n",
146 tinterlace
->flags
&= ~TINTERLACE_FLAG_VLPF
;
148 if (tinterlace
->mode
== MODE_INTERLACEX2
) {
149 outlink
->time_base
.num
= inlink
->time_base
.num
;
150 outlink
->time_base
.den
= inlink
->time_base
.den
* 2;
151 outlink
->frame_rate
= av_mul_q(inlink
->frame_rate
, (AVRational
){2,1});
154 av_log(ctx
, AV_LOG_VERBOSE
, "mode:%d filter:%s h:%d -> h:%d\n",
155 tinterlace
->mode
, (tinterlace
->flags
& TINTERLACE_FLAG_VLPF
) ? "on" : "off",
156 inlink
->h
, outlink
->h
);
161 #define FIELD_UPPER 0
162 #define FIELD_LOWER 1
163 #define FIELD_UPPER_AND_LOWER 2
166 * Copy picture field from src to dst.
168 * @param src_field copy from upper, lower field or both
169 * @param interleave leave a padding line between each copied line
170 * @param dst_field copy to upper or lower field,
171 * only meaningful when interleave is selected
172 * @param flags context flags
175 void copy_picture_field(uint8_t *dst
[4], int dst_linesize
[4],
176 const uint8_t *src
[4], int src_linesize
[4],
177 enum AVPixelFormat format
, int w
, int src_h
,
178 int src_field
, int interleave
, int dst_field
,
181 const AVPixFmtDescriptor
*desc
= av_pix_fmt_desc_get(format
);
182 int plane
, vsub
= desc
->log2_chroma_h
;
183 int k
= src_field
== FIELD_UPPER_AND_LOWER
? 1 : 2;
186 for (plane
= 0; plane
< desc
->nb_components
; plane
++) {
187 int lines
= plane
== 1 || plane
== 2 ? FF_CEIL_RSHIFT(src_h
, vsub
) : src_h
;
188 int linesize
= av_image_get_linesize(format
, w
, plane
);
189 uint8_t *dstp
= dst
[plane
];
190 const uint8_t *srcp
= src
[plane
];
195 lines
= (lines
+ (src_field
== FIELD_UPPER
)) / k
;
196 if (src_field
== FIELD_LOWER
)
197 srcp
+= src_linesize
[plane
];
198 if (interleave
&& dst_field
== FIELD_LOWER
)
199 dstp
+= dst_linesize
[plane
];
200 if (flags
& TINTERLACE_FLAG_VLPF
) {
201 // Low-pass filtering is required when creating an interlaced destination from
202 // a progressive source which contains high-frequency vertical detail.
203 // Filtering will reduce interlace 'twitter' and Moire patterning.
204 int srcp_linesize
= src_linesize
[plane
] * k
;
205 int dstp_linesize
= dst_linesize
[plane
] * (interleave
? 2 : 1);
206 for (h
= lines
; h
> 0; h
--) {
207 const uint8_t *srcp_above
= srcp
- src_linesize
[plane
];
208 const uint8_t *srcp_below
= srcp
+ src_linesize
[plane
];
209 if (h
== lines
) srcp_above
= srcp
; // there is no line above
210 if (h
== 1) srcp_below
= srcp
; // there is no line below
211 for (i
= 0; i
< linesize
; i
++) {
212 // this calculation is an integer representation of
213 // '0.5 * current + 0.25 * above + 0.25 * below'
214 // '1 +' is for rounding. */
215 dstp
[i
] = (1 + srcp
[i
] + srcp
[i
] + srcp_above
[i
] + srcp_below
[i
]) >> 2;
217 dstp
+= dstp_linesize
;
218 srcp
+= srcp_linesize
;
221 av_image_copy_plane(dstp
, dst_linesize
[plane
] * (interleave
? 2 : 1),
222 srcp
, src_linesize
[plane
]*k
, linesize
, lines
);
227 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*picref
)
229 AVFilterContext
*ctx
= inlink
->dst
;
230 AVFilterLink
*outlink
= ctx
->outputs
[0];
231 TInterlaceContext
*tinterlace
= ctx
->priv
;
232 AVFrame
*cur
, *next
, *out
;
235 av_frame_free(&tinterlace
->cur
);
236 tinterlace
->cur
= tinterlace
->next
;
237 tinterlace
->next
= picref
;
239 cur
= tinterlace
->cur
;
240 next
= tinterlace
->next
;
241 /* we need at least two frames */
242 if (!tinterlace
->cur
)
245 switch (tinterlace
->mode
) {
246 case MODE_MERGE
: /* move the odd frame into the upper field of the new image, even into
247 * the lower field, generating a double-height video at half framerate */
248 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
250 return AVERROR(ENOMEM
);
251 av_frame_copy_props(out
, cur
);
252 out
->height
= outlink
->h
;
253 out
->interlaced_frame
= 1;
254 out
->top_field_first
= 1;
256 /* write odd frame lines into the upper field of the new frame */
257 copy_picture_field(out
->data
, out
->linesize
,
258 (const uint8_t **)cur
->data
, cur
->linesize
,
259 inlink
->format
, inlink
->w
, inlink
->h
,
260 FIELD_UPPER_AND_LOWER
, 1, FIELD_UPPER
, tinterlace
->flags
);
261 /* write even frame lines into the lower field of the new frame */
262 copy_picture_field(out
->data
, out
->linesize
,
263 (const uint8_t **)next
->data
, next
->linesize
,
264 inlink
->format
, inlink
->w
, inlink
->h
,
265 FIELD_UPPER_AND_LOWER
, 1, FIELD_LOWER
, tinterlace
->flags
);
266 av_frame_free(&tinterlace
->next
);
269 case MODE_DROP_ODD
: /* only output even frames, odd frames are dropped; height unchanged, half framerate */
270 case MODE_DROP_EVEN
: /* only output odd frames, even frames are dropped; height unchanged, half framerate */
271 out
= av_frame_clone(tinterlace
->mode
== MODE_DROP_EVEN
? cur
: next
);
273 return AVERROR(ENOMEM
);
274 av_frame_free(&tinterlace
->next
);
277 case MODE_PAD
: /* expand each frame to double height, but pad alternate
278 * lines with black; framerate unchanged */
279 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
281 return AVERROR(ENOMEM
);
282 av_frame_copy_props(out
, cur
);
283 out
->height
= outlink
->h
;
285 field
= (1 + tinterlace
->frame
) & 1 ? FIELD_UPPER
: FIELD_LOWER
;
286 /* copy upper and lower fields */
287 copy_picture_field(out
->data
, out
->linesize
,
288 (const uint8_t **)cur
->data
, cur
->linesize
,
289 inlink
->format
, inlink
->w
, inlink
->h
,
290 FIELD_UPPER_AND_LOWER
, 1, field
, tinterlace
->flags
);
291 /* pad with black the other field */
292 copy_picture_field(out
->data
, out
->linesize
,
293 (const uint8_t **)tinterlace
->black_data
, tinterlace
->black_linesize
,
294 inlink
->format
, inlink
->w
, inlink
->h
,
295 FIELD_UPPER_AND_LOWER
, 1, !field
, tinterlace
->flags
);
298 /* interleave upper/lower lines from odd frames with lower/upper lines from even frames,
299 * halving the frame rate and preserving image height */
300 case MODE_INTERLEAVE_TOP
: /* top field first */
301 case MODE_INTERLEAVE_BOTTOM
: /* bottom field first */
302 tff
= tinterlace
->mode
== MODE_INTERLEAVE_TOP
;
303 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
305 return AVERROR(ENOMEM
);
306 av_frame_copy_props(out
, cur
);
307 out
->interlaced_frame
= 1;
308 out
->top_field_first
= tff
;
310 /* copy upper/lower field from cur */
311 copy_picture_field(out
->data
, out
->linesize
,
312 (const uint8_t **)cur
->data
, cur
->linesize
,
313 inlink
->format
, inlink
->w
, inlink
->h
,
314 tff
? FIELD_UPPER
: FIELD_LOWER
, 1, tff
? FIELD_UPPER
: FIELD_LOWER
,
316 /* copy lower/upper field from next */
317 copy_picture_field(out
->data
, out
->linesize
,
318 (const uint8_t **)next
->data
, next
->linesize
,
319 inlink
->format
, inlink
->w
, inlink
->h
,
320 tff
? FIELD_LOWER
: FIELD_UPPER
, 1, tff
? FIELD_LOWER
: FIELD_UPPER
,
322 av_frame_free(&tinterlace
->next
);
324 case MODE_INTERLACEX2
: /* re-interlace preserving image height, double frame rate */
325 /* output current frame first */
326 out
= av_frame_clone(cur
);
328 return AVERROR(ENOMEM
);
329 out
->interlaced_frame
= 1;
330 if (cur
->pts
!= AV_NOPTS_VALUE
)
331 out
->pts
= cur
->pts
*2;
333 if ((ret
= ff_filter_frame(outlink
, out
)) < 0)
336 /* output mix of current and next frame */
337 tff
= next
->top_field_first
;
338 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
340 return AVERROR(ENOMEM
);
341 av_frame_copy_props(out
, next
);
342 out
->interlaced_frame
= 1;
344 if (next
->pts
!= AV_NOPTS_VALUE
&& cur
->pts
!= AV_NOPTS_VALUE
)
345 out
->pts
= cur
->pts
+ next
->pts
;
347 out
->pts
= AV_NOPTS_VALUE
;
348 /* write current frame second field lines into the second field of the new frame */
349 copy_picture_field(out
->data
, out
->linesize
,
350 (const uint8_t **)cur
->data
, cur
->linesize
,
351 inlink
->format
, inlink
->w
, inlink
->h
,
352 tff
? FIELD_LOWER
: FIELD_UPPER
, 1, tff
? FIELD_LOWER
: FIELD_UPPER
,
354 /* write next frame first field lines into the first field of the new frame */
355 copy_picture_field(out
->data
, out
->linesize
,
356 (const uint8_t **)next
->data
, next
->linesize
,
357 inlink
->format
, inlink
->w
, inlink
->h
,
358 tff
? FIELD_UPPER
: FIELD_LOWER
, 1, tff
? FIELD_UPPER
: FIELD_LOWER
,
365 ret
= ff_filter_frame(outlink
, out
);
371 static const AVFilterPad tinterlace_inputs
[] = {
374 .type
= AVMEDIA_TYPE_VIDEO
,
375 .filter_frame
= filter_frame
,
380 static const AVFilterPad tinterlace_outputs
[] = {
383 .type
= AVMEDIA_TYPE_VIDEO
,
384 .config_props
= config_out_props
,
389 AVFilter ff_vf_tinterlace
= {
390 .name
= "tinterlace",
391 .description
= NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."),
392 .priv_size
= sizeof(TInterlaceContext
),
394 .query_formats
= query_formats
,
395 .inputs
= tinterlace_inputs
,
396 .outputs
= tinterlace_outputs
,
397 .priv_class
= &tinterlace_class
,