blob: 80146a94805f0ab9e7f89b17787ca3b2e291caf9
1 | /* |
2 | * Copyright (c) 2011 Stefano Sabatini |
3 | * Copyright (c) 2010 Baptiste Coudurier |
4 | * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com> |
5 | * |
6 | * This file is part of FFmpeg. |
7 | * |
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. |
12 | * |
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. |
17 | * |
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. |
21 | */ |
22 | |
23 | /** |
24 | * @file |
25 | * temporal field interlace filter, ported from MPlayer/libmpcodecs |
26 | */ |
27 | |
28 | #include "libavutil/opt.h" |
29 | #include "libavutil/imgutils.h" |
30 | #include "libavutil/avassert.h" |
31 | #include "avfilter.h" |
32 | #include "internal.h" |
33 | #include "tinterlace.h" |
34 | |
35 | #define OFFSET(x) offsetof(TInterlaceContext, x) |
36 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
37 | #define TINTERLACE_FLAG_VLPF 01 |
38 | #define TINTERLACE_FLAG_EXACT_TB 2 |
39 | |
40 | static const AVOption tinterlace_options[] = { |
41 | {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, "mode"}, |
42 | {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
43 | {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
44 | {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
45 | {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
46 | {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
47 | {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
48 | {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
49 | {"mergex2", "merge fields keeping same frame rate", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGEX2}, INT_MIN, INT_MAX, FLAGS, "mode"}, |
50 | |
51 | {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, "flags" }, |
52 | {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, |
53 | {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, |
54 | {"exact_tb", "force a timebase which can represent timestamps exactly", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_EXACT_TB}, INT_MIN, INT_MAX, FLAGS, "flags" }, |
55 | |
56 | {NULL} |
57 | }; |
58 | |
59 | AVFILTER_DEFINE_CLASS(tinterlace); |
60 | |
61 | #define FULL_SCALE_YUVJ_FORMATS \ |
62 | AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P |
63 | |
64 | static const enum AVPixelFormat full_scale_yuvj_pix_fmts[] = { |
65 | FULL_SCALE_YUVJ_FORMATS, AV_PIX_FMT_NONE |
66 | }; |
67 | |
68 | static const AVRational standard_tbs[] = { |
69 | {1, 25}, |
70 | {1, 30}, |
71 | {1001, 30000}, |
72 | }; |
73 | |
74 | static int query_formats(AVFilterContext *ctx) |
75 | { |
76 | static const enum AVPixelFormat pix_fmts[] = { |
77 | AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, |
78 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, |
79 | AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, |
80 | AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, |
81 | AV_PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS, |
82 | AV_PIX_FMT_NONE |
83 | }; |
84 | |
85 | AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); |
86 | if (!fmts_list) |
87 | return AVERROR(ENOMEM); |
88 | return ff_set_common_formats(ctx, fmts_list); |
89 | } |
90 | |
91 | static void lowpass_line_c(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp, |
92 | const uint8_t *srcp_above, const uint8_t *srcp_below) |
93 | { |
94 | int i; |
95 | for (i = 0; i < width; i++) { |
96 | // this calculation is an integer representation of |
97 | // '0.5 * current + 0.25 * above + 0.25 * below' |
98 | // '1 +' is for rounding. |
99 | dstp[i] = (1 + srcp[i] + srcp[i] + srcp_above[i] + srcp_below[i]) >> 2; |
100 | } |
101 | } |
102 | |
103 | static av_cold void uninit(AVFilterContext *ctx) |
104 | { |
105 | TInterlaceContext *tinterlace = ctx->priv; |
106 | |
107 | av_frame_free(&tinterlace->cur ); |
108 | av_frame_free(&tinterlace->next); |
109 | av_freep(&tinterlace->black_data[0]); |
110 | } |
111 | |
112 | static int config_out_props(AVFilterLink *outlink) |
113 | { |
114 | AVFilterContext *ctx = outlink->src; |
115 | AVFilterLink *inlink = outlink->src->inputs[0]; |
116 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); |
117 | TInterlaceContext *tinterlace = ctx->priv; |
118 | int i; |
119 | |
120 | tinterlace->vsub = desc->log2_chroma_h; |
121 | outlink->w = inlink->w; |
122 | outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD || tinterlace->mode == MODE_MERGEX2? |
123 | inlink->h*2 : inlink->h; |
124 | if (tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD || tinterlace->mode == MODE_MERGEX2) |
125 | outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, |
126 | av_make_q(2, 1)); |
127 | |
128 | if (tinterlace->mode == MODE_PAD) { |
129 | uint8_t black[4] = { 16, 128, 128, 16 }; |
130 | int i, ret; |
131 | if (ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts)) |
132 | black[0] = black[3] = 0; |
133 | ret = av_image_alloc(tinterlace->black_data, tinterlace->black_linesize, |
134 | outlink->w, outlink->h, outlink->format, 16); |
135 | if (ret < 0) |
136 | return ret; |
137 | |
138 | /* fill black picture with black */ |
139 | for (i = 0; i < 4 && tinterlace->black_data[i]; i++) { |
140 | int h = i == 1 || i == 2 ? AV_CEIL_RSHIFT(outlink->h, desc->log2_chroma_h) : outlink->h; |
141 | memset(tinterlace->black_data[i], black[i], |
142 | tinterlace->black_linesize[i] * h); |
143 | } |
144 | } |
145 | if ((tinterlace->flags & TINTERLACE_FLAG_VLPF) |
146 | && !(tinterlace->mode == MODE_INTERLEAVE_TOP |
147 | || tinterlace->mode == MODE_INTERLEAVE_BOTTOM)) { |
148 | av_log(ctx, AV_LOG_WARNING, "low_pass_filter flag ignored with mode %d\n", |
149 | tinterlace->mode); |
150 | tinterlace->flags &= ~TINTERLACE_FLAG_VLPF; |
151 | } |
152 | tinterlace->preout_time_base = inlink->time_base; |
153 | if (tinterlace->mode == MODE_INTERLACEX2) { |
154 | tinterlace->preout_time_base.den *= 2; |
155 | outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){2,1}); |
156 | outlink->time_base = av_mul_q(inlink->time_base , (AVRational){1,2}); |
157 | } else if (tinterlace->mode == MODE_MERGEX2) { |
158 | outlink->frame_rate = inlink->frame_rate; |
159 | outlink->time_base = inlink->time_base; |
160 | } else if (tinterlace->mode != MODE_PAD) { |
161 | outlink->frame_rate = av_mul_q(inlink->frame_rate, (AVRational){1,2}); |
162 | outlink->time_base = av_mul_q(inlink->time_base , (AVRational){2,1}); |
163 | } |
164 | |
165 | for (i = 0; i<FF_ARRAY_ELEMS(standard_tbs); i++){ |
166 | if (!av_cmp_q(standard_tbs[i], outlink->time_base)) |
167 | break; |
168 | } |
169 | if (i == FF_ARRAY_ELEMS(standard_tbs) || |
170 | (tinterlace->flags & TINTERLACE_FLAG_EXACT_TB)) |
171 | outlink->time_base = tinterlace->preout_time_base; |
172 | |
173 | if (tinterlace->flags & TINTERLACE_FLAG_VLPF) { |
174 | tinterlace->lowpass_line = lowpass_line_c; |
175 | if (ARCH_X86) |
176 | ff_tinterlace_init_x86(tinterlace); |
177 | } |
178 | |
179 | av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", |
180 | tinterlace->mode, (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "on" : "off", |
181 | inlink->h, outlink->h); |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | #define FIELD_UPPER 0 |
187 | #define FIELD_LOWER 1 |
188 | #define FIELD_UPPER_AND_LOWER 2 |
189 | |
190 | /** |
191 | * Copy picture field from src to dst. |
192 | * |
193 | * @param src_field copy from upper, lower field or both |
194 | * @param interleave leave a padding line between each copied line |
195 | * @param dst_field copy to upper or lower field, |
196 | * only meaningful when interleave is selected |
197 | * @param flags context flags |
198 | */ |
199 | static inline |
200 | void copy_picture_field(TInterlaceContext *tinterlace, |
201 | uint8_t *dst[4], int dst_linesize[4], |
202 | const uint8_t *src[4], int src_linesize[4], |
203 | enum AVPixelFormat format, int w, int src_h, |
204 | int src_field, int interleave, int dst_field, |
205 | int flags) |
206 | { |
207 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); |
208 | int hsub = desc->log2_chroma_w; |
209 | int plane, vsub = desc->log2_chroma_h; |
210 | int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2; |
211 | int h; |
212 | |
213 | for (plane = 0; plane < desc->nb_components; plane++) { |
214 | int lines = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(src_h, vsub) : src_h; |
215 | int cols = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT( w, hsub) : w; |
216 | uint8_t *dstp = dst[plane]; |
217 | const uint8_t *srcp = src[plane]; |
218 | |
219 | lines = (lines + (src_field == FIELD_UPPER)) / k; |
220 | if (src_field == FIELD_LOWER) |
221 | srcp += src_linesize[plane]; |
222 | if (interleave && dst_field == FIELD_LOWER) |
223 | dstp += dst_linesize[plane]; |
224 | if (flags & TINTERLACE_FLAG_VLPF) { |
225 | // Low-pass filtering is required when creating an interlaced destination from |
226 | // a progressive source which contains high-frequency vertical detail. |
227 | // Filtering will reduce interlace 'twitter' and Moire patterning. |
228 | int srcp_linesize = src_linesize[plane] * k; |
229 | int dstp_linesize = dst_linesize[plane] * (interleave ? 2 : 1); |
230 | for (h = lines; h > 0; h--) { |
231 | const uint8_t *srcp_above = srcp - src_linesize[plane]; |
232 | const uint8_t *srcp_below = srcp + src_linesize[plane]; |
233 | if (h == lines) srcp_above = srcp; // there is no line above |
234 | if (h == 1) srcp_below = srcp; // there is no line below |
235 | |
236 | tinterlace->lowpass_line(dstp, cols, srcp, srcp_above, srcp_below); |
237 | dstp += dstp_linesize; |
238 | srcp += srcp_linesize; |
239 | } |
240 | } else { |
241 | av_image_copy_plane(dstp, dst_linesize[plane] * (interleave ? 2 : 1), |
242 | srcp, src_linesize[plane]*k, cols, lines); |
243 | } |
244 | } |
245 | } |
246 | |
247 | static int filter_frame(AVFilterLink *inlink, AVFrame *picref) |
248 | { |
249 | AVFilterContext *ctx = inlink->dst; |
250 | AVFilterLink *outlink = ctx->outputs[0]; |
251 | TInterlaceContext *tinterlace = ctx->priv; |
252 | AVFrame *cur, *next, *out; |
253 | int field, tff, ret; |
254 | |
255 | av_frame_free(&tinterlace->cur); |
256 | tinterlace->cur = tinterlace->next; |
257 | tinterlace->next = picref; |
258 | |
259 | cur = tinterlace->cur; |
260 | next = tinterlace->next; |
261 | /* we need at least two frames */ |
262 | if (!tinterlace->cur) |
263 | return 0; |
264 | |
265 | switch (tinterlace->mode) { |
266 | case MODE_MERGEX2: /* move the odd frame into the upper field of the new image, even into |
267 | * the lower field, generating a double-height video at same framerate */ |
268 | case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into |
269 | * the lower field, generating a double-height video at half framerate */ |
270 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
271 | if (!out) |
272 | return AVERROR(ENOMEM); |
273 | av_frame_copy_props(out, cur); |
274 | out->height = outlink->h; |
275 | out->interlaced_frame = 1; |
276 | out->top_field_first = 1; |
277 | out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); |
278 | |
279 | /* write odd frame lines into the upper field of the new frame */ |
280 | copy_picture_field(tinterlace, out->data, out->linesize, |
281 | (const uint8_t **)cur->data, cur->linesize, |
282 | inlink->format, inlink->w, inlink->h, |
283 | FIELD_UPPER_AND_LOWER, 1, tinterlace->mode == MODE_MERGEX2 ? inlink->frame_count_out & 1 ? FIELD_LOWER : FIELD_UPPER : FIELD_UPPER, tinterlace->flags); |
284 | /* write even frame lines into the lower field of the new frame */ |
285 | copy_picture_field(tinterlace, out->data, out->linesize, |
286 | (const uint8_t **)next->data, next->linesize, |
287 | inlink->format, inlink->w, inlink->h, |
288 | FIELD_UPPER_AND_LOWER, 1, tinterlace->mode == MODE_MERGEX2 ? inlink->frame_count_out & 1 ? FIELD_UPPER : FIELD_LOWER : FIELD_LOWER, tinterlace->flags); |
289 | if (tinterlace->mode != MODE_MERGEX2) |
290 | av_frame_free(&tinterlace->next); |
291 | break; |
292 | |
293 | case MODE_DROP_ODD: /* only output even frames, odd frames are dropped; height unchanged, half framerate */ |
294 | case MODE_DROP_EVEN: /* only output odd frames, even frames are dropped; height unchanged, half framerate */ |
295 | out = av_frame_clone(tinterlace->mode == MODE_DROP_EVEN ? cur : next); |
296 | if (!out) |
297 | return AVERROR(ENOMEM); |
298 | av_frame_free(&tinterlace->next); |
299 | break; |
300 | |
301 | case MODE_PAD: /* expand each frame to double height, but pad alternate |
302 | * lines with black; framerate unchanged */ |
303 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
304 | if (!out) |
305 | return AVERROR(ENOMEM); |
306 | av_frame_copy_props(out, cur); |
307 | out->height = outlink->h; |
308 | out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); |
309 | |
310 | field = (1 + tinterlace->frame) & 1 ? FIELD_UPPER : FIELD_LOWER; |
311 | /* copy upper and lower fields */ |
312 | copy_picture_field(tinterlace, out->data, out->linesize, |
313 | (const uint8_t **)cur->data, cur->linesize, |
314 | inlink->format, inlink->w, inlink->h, |
315 | FIELD_UPPER_AND_LOWER, 1, field, tinterlace->flags); |
316 | /* pad with black the other field */ |
317 | copy_picture_field(tinterlace, out->data, out->linesize, |
318 | (const uint8_t **)tinterlace->black_data, tinterlace->black_linesize, |
319 | inlink->format, inlink->w, inlink->h, |
320 | FIELD_UPPER_AND_LOWER, 1, !field, tinterlace->flags); |
321 | break; |
322 | |
323 | /* interleave upper/lower lines from odd frames with lower/upper lines from even frames, |
324 | * halving the frame rate and preserving image height */ |
325 | case MODE_INTERLEAVE_TOP: /* top field first */ |
326 | case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ |
327 | tff = tinterlace->mode == MODE_INTERLEAVE_TOP; |
328 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
329 | if (!out) |
330 | return AVERROR(ENOMEM); |
331 | av_frame_copy_props(out, cur); |
332 | out->interlaced_frame = 1; |
333 | out->top_field_first = tff; |
334 | |
335 | /* copy upper/lower field from cur */ |
336 | copy_picture_field(tinterlace, out->data, out->linesize, |
337 | (const uint8_t **)cur->data, cur->linesize, |
338 | inlink->format, inlink->w, inlink->h, |
339 | tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, |
340 | tinterlace->flags); |
341 | /* copy lower/upper field from next */ |
342 | copy_picture_field(tinterlace, out->data, out->linesize, |
343 | (const uint8_t **)next->data, next->linesize, |
344 | inlink->format, inlink->w, inlink->h, |
345 | tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, |
346 | tinterlace->flags); |
347 | av_frame_free(&tinterlace->next); |
348 | break; |
349 | case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */ |
350 | /* output current frame first */ |
351 | out = av_frame_clone(cur); |
352 | if (!out) |
353 | return AVERROR(ENOMEM); |
354 | out->interlaced_frame = 1; |
355 | if (cur->pts != AV_NOPTS_VALUE) |
356 | out->pts = cur->pts*2; |
357 | |
358 | out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); |
359 | if ((ret = ff_filter_frame(outlink, out)) < 0) |
360 | return ret; |
361 | |
362 | /* output mix of current and next frame */ |
363 | tff = next->top_field_first; |
364 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
365 | if (!out) |
366 | return AVERROR(ENOMEM); |
367 | av_frame_copy_props(out, next); |
368 | out->interlaced_frame = 1; |
369 | out->top_field_first = !tff; |
370 | |
371 | if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE) |
372 | out->pts = cur->pts + next->pts; |
373 | else |
374 | out->pts = AV_NOPTS_VALUE; |
375 | /* write current frame second field lines into the second field of the new frame */ |
376 | copy_picture_field(tinterlace, out->data, out->linesize, |
377 | (const uint8_t **)cur->data, cur->linesize, |
378 | inlink->format, inlink->w, inlink->h, |
379 | tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, |
380 | tinterlace->flags); |
381 | /* write next frame first field lines into the first field of the new frame */ |
382 | copy_picture_field(tinterlace, out->data, out->linesize, |
383 | (const uint8_t **)next->data, next->linesize, |
384 | inlink->format, inlink->w, inlink->h, |
385 | tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, |
386 | tinterlace->flags); |
387 | break; |
388 | default: |
389 | av_assert0(0); |
390 | } |
391 | |
392 | out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); |
393 | ret = ff_filter_frame(outlink, out); |
394 | tinterlace->frame++; |
395 | |
396 | return ret; |
397 | } |
398 | |
399 | static const AVFilterPad tinterlace_inputs[] = { |
400 | { |
401 | .name = "default", |
402 | .type = AVMEDIA_TYPE_VIDEO, |
403 | .filter_frame = filter_frame, |
404 | }, |
405 | { NULL } |
406 | }; |
407 | |
408 | static const AVFilterPad tinterlace_outputs[] = { |
409 | { |
410 | .name = "default", |
411 | .type = AVMEDIA_TYPE_VIDEO, |
412 | .config_props = config_out_props, |
413 | }, |
414 | { NULL } |
415 | }; |
416 | |
417 | AVFilter ff_vf_tinterlace = { |
418 | .name = "tinterlace", |
419 | .description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."), |
420 | .priv_size = sizeof(TInterlaceContext), |
421 | .uninit = uninit, |
422 | .query_formats = query_formats, |
423 | .inputs = tinterlace_inputs, |
424 | .outputs = tinterlace_outputs, |
425 | .priv_class = &tinterlace_class, |
426 | }; |
427 |