Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2017 Thomas Mundt <tmundt75@gmail.com> | ||
3 | * Copyright (c) 2011 Stefano Sabatini | ||
4 | * Copyright (c) 2010 Baptiste Coudurier | ||
5 | * Copyright (c) 2003 Michael Zucchi <notzed@ximian.com> | ||
6 | * | ||
7 | * This file is part of FFmpeg. | ||
8 | * | ||
9 | * FFmpeg is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
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 | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with FFmpeg if not, write to the Free Software Foundation, Inc., | ||
21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
22 | */ | ||
23 | |||
24 | /** | ||
25 | * @file | ||
26 | * temporal field interlace filter, ported from MPlayer/libmpcodecs | ||
27 | */ | ||
28 | |||
29 | #include "libavutil/mem.h" | ||
30 | #include "libavutil/opt.h" | ||
31 | #include "libavutil/imgutils.h" | ||
32 | #include "libavutil/avassert.h" | ||
33 | #include "avfilter.h" | ||
34 | #include "filters.h" | ||
35 | #include "tinterlace.h" | ||
36 | #include "video.h" | ||
37 | |||
38 | #define OFFSET(x) offsetof(TInterlaceContext, x) | ||
39 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
40 | |||
41 | static const AVOption tinterlace_options[] = { | ||
42 | {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, .unit = "mode"}, | ||
43 | {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
44 | {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
45 | {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
46 | {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
47 | {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
48 | {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
49 | {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
50 | {"mergex2", "merge fields keeping same frame rate", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGEX2}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
51 | |||
52 | {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, .unit = "flags" }, | ||
53 | {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, | ||
54 | {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, | ||
55 | {"complex_filter", "enable complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, | ||
56 | {"cvlpf", "enable complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, | ||
57 | {"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, .unit = "flags" }, | ||
58 | {"bypass_il", "bypass already interlaced frames", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_BYPASS_IL}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, | ||
59 | |||
60 | {NULL} | ||
61 | }; | ||
62 | |||
63 | AVFILTER_DEFINE_CLASS(tinterlace); | ||
64 | |||
65 | static const AVOption interlace_options[] = { | ||
66 | { "scan", "scanning mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_TFF}, 0, 1, FLAGS, .unit = "mode"}, | ||
67 | { "tff", "top field first", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_TFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
68 | { "bff", "bottom field first", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, | ||
69 | { "lowpass", "set vertical low-pass filter", OFFSET(lowpass), AV_OPT_TYPE_INT, {.i64 = VLPF_LIN}, 0, 2, FLAGS, .unit = "lowpass" }, | ||
70 | { "off", "disable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_OFF}, INT_MIN, INT_MAX, FLAGS, .unit = "lowpass" }, | ||
71 | { "linear", "linear vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_LIN}, INT_MIN, INT_MAX, FLAGS, .unit = "lowpass" }, | ||
72 | { "complex", "complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_CMP}, INT_MIN, INT_MAX, FLAGS, .unit = "lowpass" }, | ||
73 | |||
74 | { NULL } | ||
75 | }; | ||
76 | |||
77 | AVFILTER_DEFINE_CLASS(interlace); | ||
78 | |||
79 | #define FULL_SCALE_YUVJ_FORMATS \ | ||
80 | AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P | ||
81 | |||
82 | static const enum AVPixelFormat full_scale_yuvj_pix_fmts[] = { | ||
83 | FULL_SCALE_YUVJ_FORMATS, AV_PIX_FMT_NONE | ||
84 | }; | ||
85 | |||
86 | static const AVRational standard_tbs[] = { | ||
87 | {1, 25}, | ||
88 | {1, 30}, | ||
89 | {1001, 30000}, | ||
90 | }; | ||
91 | |||
92 | static const enum AVPixelFormat pix_fmts[] = { | ||
93 | AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, | ||
94 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, | ||
95 | AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, | ||
96 | AV_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUV422P10LE, | ||
97 | AV_PIX_FMT_YUV440P10LE, AV_PIX_FMT_YUV444P10LE, | ||
98 | AV_PIX_FMT_YUV420P12LE, AV_PIX_FMT_YUV422P12LE, | ||
99 | AV_PIX_FMT_YUV440P12LE, AV_PIX_FMT_YUV444P12LE, | ||
100 | AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, | ||
101 | AV_PIX_FMT_YUVA420P10LE, AV_PIX_FMT_YUVA422P10LE, AV_PIX_FMT_YUVA444P10LE, | ||
102 | AV_PIX_FMT_GRAY8, FULL_SCALE_YUVJ_FORMATS, | ||
103 | AV_PIX_FMT_NONE | ||
104 | }; | ||
105 | |||
106 | 39312 | static void lowpass_line_c(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp, | |
107 | ptrdiff_t mref, ptrdiff_t pref, int clip_max) | ||
108 | { | ||
109 | 39312 | const uint8_t *srcp_above = srcp + mref; | |
110 | 39312 | const uint8_t *srcp_below = srcp + pref; | |
111 | int i; | ||
112 |
2/2✓ Branch 0 taken 10657152 times.
✓ Branch 1 taken 39312 times.
|
10696464 | for (i = 0; i < width; i++) { |
113 | // this calculation is an integer representation of | ||
114 | // '0.5 * current + 0.25 * above + 0.25 * below' | ||
115 | // '1 +' is for rounding. | ||
116 | 10657152 | dstp[i] = (1 + srcp[i] + srcp[i] + srcp_above[i] + srcp_below[i]) >> 2; | |
117 | } | ||
118 | 39312 | } | |
119 | |||
120 | 8928 | static void lowpass_line_c_16(uint8_t *dst8, ptrdiff_t width, const uint8_t *src8, | |
121 | ptrdiff_t mref, ptrdiff_t pref, int clip_max) | ||
122 | { | ||
123 | 8928 | uint16_t *dstp = (uint16_t *)dst8; | |
124 | 8928 | const uint16_t *srcp = (const uint16_t *)src8; | |
125 | 8928 | const uint16_t *srcp_above = srcp + mref / 2; | |
126 | 8928 | const uint16_t *srcp_below = srcp + pref / 2; | |
127 | int i, src_x; | ||
128 |
2/2✓ Branch 0 taken 2686464 times.
✓ Branch 1 taken 8928 times.
|
2695392 | for (i = 0; i < width; i++) { |
129 | // this calculation is an integer representation of | ||
130 | // '0.5 * current + 0.25 * above + 0.25 * below' | ||
131 | // '1 +' is for rounding. | ||
132 | 2686464 | src_x = av_le2ne16(srcp[i]) << 1; | |
133 | 2686464 | dstp[i] = av_le2ne16((1 + src_x + av_le2ne16(srcp_above[i]) | |
134 | + av_le2ne16(srcp_below[i])) >> 2); | ||
135 | } | ||
136 | 8928 | } | |
137 | |||
138 | 24912 | static void lowpass_line_complex_c(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp, | |
139 | ptrdiff_t mref, ptrdiff_t pref, int clip_max) | ||
140 | { | ||
141 | 24912 | const uint8_t *srcp_above = srcp + mref; | |
142 | 24912 | const uint8_t *srcp_below = srcp + pref; | |
143 | 24912 | const uint8_t *srcp_above2 = srcp + mref * 2; | |
144 | 24912 | const uint8_t *srcp_below2 = srcp + pref * 2; | |
145 | int i, src_x, src_ab; | ||
146 |
2/2✓ Branch 0 taken 6855552 times.
✓ Branch 1 taken 24912 times.
|
6880464 | for (i = 0; i < width; i++) { |
147 | // this calculation is an integer representation of | ||
148 | // '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2' | ||
149 | // '4 +' is for rounding. | ||
150 | 6855552 | src_x = srcp[i] << 1; | |
151 | 6855552 | src_ab = srcp_above[i] + srcp_below[i]; | |
152 | 6855552 | dstp[i] = av_clip_uint8((4 + ((srcp[i] + src_x + src_ab) << 1) | |
153 | 6855552 | - srcp_above2[i] - srcp_below2[i]) >> 3); | |
154 | // Prevent over-sharpening: | ||
155 | // dst must not exceed src when the average of above and below | ||
156 | // is less than src. And the other way around. | ||
157 |
2/2✓ Branch 0 taken 2774517 times.
✓ Branch 1 taken 4081035 times.
|
6855552 | if (src_ab > src_x) { |
158 |
2/2✓ Branch 0 taken 384538 times.
✓ Branch 1 taken 2389979 times.
|
2774517 | if (dstp[i] < srcp[i]) |
159 | 384538 | dstp[i] = srcp[i]; | |
160 |
2/2✓ Branch 0 taken 487517 times.
✓ Branch 1 taken 3593518 times.
|
4081035 | } else if (dstp[i] > srcp[i]) |
161 | 487517 | dstp[i] = srcp[i]; | |
162 | } | ||
163 | 24912 | } | |
164 | |||
165 | 8928 | static void lowpass_line_complex_c_16(uint8_t *dst8, ptrdiff_t width, const uint8_t *src8, | |
166 | ptrdiff_t mref, ptrdiff_t pref, int clip_max) | ||
167 | { | ||
168 | 8928 | uint16_t *dstp = (uint16_t *)dst8; | |
169 | 8928 | const uint16_t *srcp = (const uint16_t *)src8; | |
170 | 8928 | const uint16_t *srcp_above = srcp + mref / 2; | |
171 | 8928 | const uint16_t *srcp_below = srcp + pref / 2; | |
172 | 8928 | const uint16_t *srcp_above2 = srcp + mref; | |
173 | 8928 | const uint16_t *srcp_below2 = srcp + pref; | |
174 | int i, dst_le, src_le, src_x, src_ab; | ||
175 |
2/2✓ Branch 0 taken 2686464 times.
✓ Branch 1 taken 8928 times.
|
2695392 | for (i = 0; i < width; i++) { |
176 | // this calculation is an integer representation of | ||
177 | // '0.75 * current + 0.25 * above + 0.25 * below - 0.125 * above2 - 0.125 * below2' | ||
178 | // '4 +' is for rounding. | ||
179 | 2686464 | src_le = av_le2ne16(srcp[i]); | |
180 | 2686464 | src_x = src_le << 1; | |
181 | 2686464 | src_ab = av_le2ne16(srcp_above[i]) + av_le2ne16(srcp_below[i]); | |
182 | 2686464 | dst_le = av_clip((4 + ((src_le + src_x + src_ab) << 1) | |
183 | 2686464 | - av_le2ne16(srcp_above2[i]) | |
184 | 2686464 | - av_le2ne16(srcp_below2[i])) >> 3, 0, clip_max); | |
185 | // Prevent over-sharpening: | ||
186 | // dst must not exceed src when the average of above and below | ||
187 | // is less than src. And the other way around. | ||
188 |
2/2✓ Branch 0 taken 1072111 times.
✓ Branch 1 taken 1614353 times.
|
2686464 | if (src_ab > src_x) { |
189 |
2/2✓ Branch 0 taken 270609 times.
✓ Branch 1 taken 801502 times.
|
1072111 | if (dst_le < src_le) |
190 | 270609 | dstp[i] = av_le2ne16(src_le); | |
191 | else | ||
192 | 801502 | dstp[i] = av_le2ne16(dst_le); | |
193 |
2/2✓ Branch 0 taken 306777 times.
✓ Branch 1 taken 1307576 times.
|
1614353 | } else if (dst_le > src_le) { |
194 | 306777 | dstp[i] = av_le2ne16(src_le); | |
195 | } else | ||
196 | 1307576 | dstp[i] = av_le2ne16(dst_le); | |
197 | } | ||
198 | 8928 | } | |
199 | |||
200 | 230 | static av_cold void uninit(AVFilterContext *ctx) | |
201 | { | ||
202 | 230 | TInterlaceContext *tinterlace = ctx->priv; | |
203 | |||
204 | 230 | av_frame_free(&tinterlace->cur ); | |
205 | 230 | av_frame_free(&tinterlace->next); | |
206 | 230 | av_freep(&tinterlace->black_data[0][0]); | |
207 | 230 | av_freep(&tinterlace->black_data[1][0]); | |
208 | 230 | ff_ccfifo_uninit(&tinterlace->cc_fifo); | |
209 | 230 | } | |
210 | |||
211 | 113 | static int config_out_props(AVFilterLink *outlink) | |
212 | { | ||
213 | 113 | AVFilterContext *ctx = outlink->src; | |
214 | 113 | AVFilterLink *inlink = outlink->src->inputs[0]; | |
215 | 113 | FilterLink *il = ff_filter_link(inlink); | |
216 | 113 | FilterLink *ol = ff_filter_link(outlink); | |
217 | 113 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); | |
218 | 113 | TInterlaceContext *tinterlace = ctx->priv; | |
219 | int ret, i; | ||
220 | |||
221 | 113 | tinterlace->vsub = desc->log2_chroma_h; | |
222 | 113 | outlink->w = inlink->w; | |
223 |
3/4✓ Branch 0 taken 63 times.
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 63 times.
|
88 | outlink->h = tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD || tinterlace->mode == MODE_MERGEX2? |
224 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 25 times.
|
201 | inlink->h*2 : inlink->h; |
225 |
5/6✓ Branch 0 taken 88 times.
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 63 times.
✓ Branch 3 taken 25 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 63 times.
|
113 | if (tinterlace->mode == MODE_MERGE || tinterlace->mode == MODE_PAD || tinterlace->mode == MODE_MERGEX2) |
226 | 50 | outlink->sample_aspect_ratio = av_mul_q(inlink->sample_aspect_ratio, | |
227 | av_make_q(2, 1)); | ||
228 | |||
229 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 88 times.
|
113 | if (tinterlace->mode == MODE_PAD) { |
230 | 25 | uint8_t black[4] = { 0, 0, 0, 16 }; | |
231 | 25 | ret = ff_draw_init2(&tinterlace->draw, outlink->format, outlink->colorspace, outlink->color_range, 0); | |
232 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (ret < 0) { |
233 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failed to initialize FFDrawContext\n"); | |
234 | ✗ | return ret; | |
235 | } | ||
236 | 25 | ff_draw_color(&tinterlace->draw, &tinterlace->color, black); | |
237 | /* limited range */ | ||
238 |
2/2✓ Branch 1 taken 21 times.
✓ Branch 2 taken 4 times.
|
25 | if (!ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts)) { |
239 | 21 | ret = av_image_alloc(tinterlace->black_data[0], tinterlace->black_linesize, | |
240 | 21 | outlink->w, outlink->h, outlink->format, 16); | |
241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | if (ret < 0) |
242 | ✗ | return ret; | |
243 | 21 | ff_fill_rectangle(&tinterlace->draw, &tinterlace->color, tinterlace->black_data[0], | |
244 | 21 | tinterlace->black_linesize, 0, 0, outlink->w, outlink->h); | |
245 | } | ||
246 | /* full range */ | ||
247 | 25 | tinterlace->color.comp[0].u8[0] = 0; | |
248 | 25 | ret = av_image_alloc(tinterlace->black_data[1], tinterlace->black_linesize, | |
249 | 25 | outlink->w, outlink->h, outlink->format, 16); | |
250 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (ret < 0) |
251 | ✗ | return ret; | |
252 | 25 | ff_fill_rectangle(&tinterlace->draw, &tinterlace->color, tinterlace->black_data[1], | |
253 | 25 | tinterlace->black_linesize, 0, 0, outlink->w, outlink->h); | |
254 | } | ||
255 |
2/2✓ Branch 0 taken 53 times.
✓ Branch 1 taken 60 times.
|
113 | if (tinterlace->flags & (TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF) |
256 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
|
53 | && !(tinterlace->mode == MODE_INTERLEAVE_TOP |
257 | ✗ | || tinterlace->mode == MODE_INTERLEAVE_BOTTOM)) { | |
258 | ✗ | av_log(ctx, AV_LOG_WARNING, "low_pass_filter flags ignored with mode %d\n", | |
259 | tinterlace->mode); | ||
260 | ✗ | tinterlace->flags &= ~(TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF); | |
261 | } | ||
262 | 113 | tinterlace->preout_time_base = inlink->time_base; | |
263 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | if (tinterlace->mode == MODE_INTERLACEX2) { |
264 | ✗ | tinterlace->preout_time_base.den *= 2; | |
265 | ✗ | ol->frame_rate = av_mul_q(il->frame_rate, (AVRational){2,1}); | |
266 | ✗ | outlink->time_base = av_mul_q(inlink->time_base , (AVRational){1,2}); | |
267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | } else if (tinterlace->mode == MODE_MERGEX2) { |
268 | ✗ | ol->frame_rate = il->frame_rate; | |
269 | ✗ | outlink->time_base = inlink->time_base; | |
270 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 25 times.
|
113 | } else if (tinterlace->mode != MODE_PAD) { |
271 | 88 | ol->frame_rate = av_mul_q(il->frame_rate, (AVRational){1,2}); | |
272 | 88 | outlink->time_base = av_mul_q(inlink->time_base , (AVRational){2,1}); | |
273 | } | ||
274 | |||
275 |
2/2✓ Branch 0 taken 339 times.
✓ Branch 1 taken 113 times.
|
452 | for (i = 0; i<FF_ARRAY_ELEMS(standard_tbs); i++){ |
276 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 339 times.
|
339 | if (!av_cmp_q(standard_tbs[i], outlink->time_base)) |
277 | ✗ | break; | |
278 | } | ||
279 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | if (i == FF_ARRAY_ELEMS(standard_tbs) || |
280 | ✗ | (tinterlace->flags & TINTERLACE_FLAG_EXACT_TB)) | |
281 | 113 | outlink->time_base = tinterlace->preout_time_base; | |
282 | |||
283 | 113 | tinterlace->csp = av_pix_fmt_desc_get(outlink->format); | |
284 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 87 times.
|
113 | if (tinterlace->flags & TINTERLACE_FLAG_CVLPF) { |
285 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
|
26 | if (tinterlace->csp->comp[0].depth > 8) |
286 | 11 | tinterlace->lowpass_line = lowpass_line_complex_c_16; | |
287 | else | ||
288 | 15 | tinterlace->lowpass_line = lowpass_line_complex_c; | |
289 | #if ARCH_X86 | ||
290 | 26 | ff_tinterlace_init_x86(tinterlace); | |
291 | #endif | ||
292 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 60 times.
|
87 | } else if (tinterlace->flags & TINTERLACE_FLAG_VLPF) { |
293 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 16 times.
|
27 | if (tinterlace->csp->comp[0].depth > 8) |
294 | 11 | tinterlace->lowpass_line = lowpass_line_c_16; | |
295 | else | ||
296 | 16 | tinterlace->lowpass_line = lowpass_line_c; | |
297 | #if ARCH_X86 | ||
298 | 27 | ff_tinterlace_init_x86(tinterlace); | |
299 | #endif | ||
300 | } | ||
301 | |||
302 | 113 | ret = ff_ccfifo_init(&tinterlace->cc_fifo, ol->frame_rate, ctx); | |
303 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | if (ret < 0) { |
304 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); | |
305 | ✗ | return ret; | |
306 | } | ||
307 | |||
308 | 113 | av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", tinterlace->mode, | |
309 |
2/2✓ Branch 0 taken 87 times.
✓ Branch 1 taken 26 times.
|
113 | (tinterlace->flags & TINTERLACE_FLAG_CVLPF) ? "complex" : |
310 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 60 times.
|
87 | (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "linear" : "off", |
311 | inlink->h, outlink->h); | ||
312 | |||
313 | 113 | return 0; | |
314 | } | ||
315 | |||
316 | #define FIELD_UPPER 0 | ||
317 | #define FIELD_LOWER 1 | ||
318 | #define FIELD_UPPER_AND_LOWER 2 | ||
319 | |||
320 | /** | ||
321 | * Copy picture field from src to dst. | ||
322 | * | ||
323 | * @param src_field copy from upper, lower field or both | ||
324 | * @param interleave leave a padding line between each copied line | ||
325 | * @param dst_field copy to upper or lower field, | ||
326 | * only meaningful when interleave is selected | ||
327 | * @param flags context flags | ||
328 | */ | ||
329 | static inline | ||
330 | 370 | void copy_picture_field(TInterlaceContext *tinterlace, | |
331 | uint8_t *dst[4], int dst_linesize[4], | ||
332 | const uint8_t *src[4], int src_linesize[4], | ||
333 | enum AVPixelFormat format, int w, int src_h, | ||
334 | int src_field, int interleave, int dst_field, | ||
335 | int flags) | ||
336 | { | ||
337 | 370 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); | |
338 | 370 | int hsub = desc->log2_chroma_w; | |
339 | 370 | int plane, vsub = desc->log2_chroma_h; | |
340 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 270 times.
|
370 | int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2; |
341 | int h; | ||
342 | |||
343 |
2/2✓ Branch 0 taken 1142 times.
✓ Branch 1 taken 370 times.
|
1512 | for (plane = 0; plane < desc->nb_components; plane++) { |
344 |
4/4✓ Branch 0 taken 780 times.
✓ Branch 1 taken 362 times.
✓ Branch 2 taken 362 times.
✓ Branch 3 taken 418 times.
|
1142 | int lines = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT(src_h, vsub) : src_h; |
345 |
4/4✓ Branch 0 taken 780 times.
✓ Branch 1 taken 362 times.
✓ Branch 2 taken 362 times.
✓ Branch 3 taken 418 times.
|
1142 | int cols = plane == 1 || plane == 2 ? AV_CEIL_RSHIFT( w, hsub) : w; |
346 | 1142 | uint8_t *dstp = dst[plane]; | |
347 | 1142 | const uint8_t *srcp = src[plane]; | |
348 | 1142 | int srcp_linesize = src_linesize[plane] * k; | |
349 |
1/2✓ Branch 0 taken 1142 times.
✗ Branch 1 not taken.
|
1142 | int dstp_linesize = dst_linesize[plane] * (interleave ? 2 : 1); |
350 | 1142 | int clip_max = (1 << tinterlace->csp->comp[plane].depth) - 1; | |
351 | |||
352 | 1142 | lines = (lines + (src_field == FIELD_UPPER)) / k; | |
353 |
2/2✓ Branch 0 taken 413 times.
✓ Branch 1 taken 729 times.
|
1142 | if (src_field == FIELD_LOWER) |
354 | 413 | srcp += src_linesize[plane]; | |
355 |
3/4✓ Branch 0 taken 1142 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 571 times.
✓ Branch 3 taken 571 times.
|
1142 | if (interleave && dst_field == FIELD_LOWER) |
356 | 571 | dstp += dst_linesize[plane]; | |
357 | // Low-pass filtering is required when creating an interlaced destination from | ||
358 | // a progressive source which contains high-frequency vertical detail. | ||
359 | // Filtering will reduce interlace 'twitter' and Moire patterning. | ||
360 |
2/2✓ Branch 0 taken 766 times.
✓ Branch 1 taken 376 times.
|
1142 | if (flags & (TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF)) { |
361 | 766 | int x = !!(flags & TINTERLACE_FLAG_CVLPF); | |
362 |
2/2✓ Branch 0 taken 82080 times.
✓ Branch 1 taken 766 times.
|
82846 | for (h = lines; h > 0; h--) { |
363 | 82080 | ptrdiff_t pref = src_linesize[plane]; | |
364 | 82080 | ptrdiff_t mref = -pref; | |
365 |
2/2✓ Branch 0 taken 1074 times.
✓ Branch 1 taken 81006 times.
|
82080 | if (h >= (lines - x)) mref = 0; // there is no line above |
366 |
2/2✓ Branch 0 taken 1074 times.
✓ Branch 1 taken 79932 times.
|
81006 | else if (h <= (1 + x)) pref = 0; // there is no line below |
367 | |||
368 | 82080 | tinterlace->lowpass_line(dstp, cols, srcp, mref, pref, clip_max); | |
369 | 82080 | dstp += dstp_linesize; | |
370 | 82080 | srcp += srcp_linesize; | |
371 | } | ||
372 | } else { | ||
373 |
2/2✓ Branch 0 taken 144 times.
✓ Branch 1 taken 232 times.
|
376 | if (tinterlace->csp->comp[plane].depth > 8) |
374 | 144 | cols *= 2; | |
375 | 376 | av_image_copy_plane(dstp, dstp_linesize, srcp, srcp_linesize, cols, lines); | |
376 | } | ||
377 | } | ||
378 | 370 | } | |
379 | |||
380 | 370 | static int filter_frame(AVFilterLink *inlink, AVFrame *picref) | |
381 | { | ||
382 | 370 | FilterLink *inl = ff_filter_link(inlink); | |
383 | 370 | AVFilterContext *ctx = inlink->dst; | |
384 | 370 | AVFilterLink *outlink = ctx->outputs[0]; | |
385 | 370 | FilterLink *l = ff_filter_link(outlink); | |
386 | 370 | TInterlaceContext *tinterlace = ctx->priv; | |
387 | AVFrame *cur, *next, *out; | ||
388 | int field, tff, full, ret; | ||
389 | |||
390 | 370 | av_frame_free(&tinterlace->cur); | |
391 | 370 | tinterlace->cur = tinterlace->next; | |
392 | 370 | tinterlace->next = picref; | |
393 | |||
394 | 370 | ff_ccfifo_extract(&tinterlace->cc_fifo, picref); | |
395 | |||
396 | 370 | cur = tinterlace->cur; | |
397 | 370 | next = tinterlace->next; | |
398 | /* we need at least two frames */ | ||
399 |
2/2✓ Branch 0 taken 185 times.
✓ Branch 1 taken 185 times.
|
370 | if (!tinterlace->cur) |
400 | 185 | return 0; | |
401 | |||
402 |
3/6✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 135 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
185 | switch (tinterlace->mode) { |
403 | 25 | case MODE_MERGEX2: /* move the odd frame into the upper field of the new image, even into | |
404 | * the lower field, generating a double-height video at same framerate */ | ||
405 | case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into | ||
406 | * the lower field, generating a double-height video at half framerate */ | ||
407 | 25 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
408 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (!out) |
409 | ✗ | return AVERROR(ENOMEM); | |
410 | 25 | av_frame_copy_props(out, cur); | |
411 | 25 | out->height = outlink->h; | |
412 | 25 | out->flags |= AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
413 | 25 | out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); | |
414 | |||
415 | /* write odd frame lines into the upper field of the new frame */ | ||
416 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
417 | 25 | (const uint8_t **)cur->data, cur->linesize, | |
418 | 25 | inlink->format, inlink->w, inlink->h, | |
419 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | FIELD_UPPER_AND_LOWER, 1, tinterlace->mode == MODE_MERGEX2 ? (1 + inl->frame_count_out) & 1 ? FIELD_LOWER : FIELD_UPPER : FIELD_UPPER, tinterlace->flags); |
420 | /* write even frame lines into the lower field of the new frame */ | ||
421 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
422 | 25 | (const uint8_t **)next->data, next->linesize, | |
423 | 25 | inlink->format, inlink->w, inlink->h, | |
424 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
25 | FIELD_UPPER_AND_LOWER, 1, tinterlace->mode == MODE_MERGEX2 ? (1 + inl->frame_count_out) & 1 ? FIELD_UPPER : FIELD_LOWER : FIELD_LOWER, tinterlace->flags); |
425 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | if (tinterlace->mode != MODE_MERGEX2) |
426 | 25 | av_frame_free(&tinterlace->next); | |
427 | 25 | break; | |
428 | |||
429 | ✗ | case MODE_DROP_ODD: /* only output even frames, odd frames are dropped; height unchanged, half framerate */ | |
430 | case MODE_DROP_EVEN: /* only output odd frames, even frames are dropped; height unchanged, half framerate */ | ||
431 | ✗ | out = av_frame_clone(tinterlace->mode == MODE_DROP_EVEN ? cur : next); | |
432 | ✗ | if (!out) | |
433 | ✗ | return AVERROR(ENOMEM); | |
434 | ✗ | av_frame_free(&tinterlace->next); | |
435 | ✗ | break; | |
436 | |||
437 | 25 | case MODE_PAD: /* expand each frame to double height, but pad alternate | |
438 | * lines with black; framerate unchanged */ | ||
439 | 25 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
440 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (!out) |
441 | ✗ | return AVERROR(ENOMEM); | |
442 | 25 | av_frame_copy_props(out, cur); | |
443 | 25 | out->height = outlink->h; | |
444 | 25 | out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); | |
445 | |||
446 | 25 | field = (1 + l->frame_count_in) & 1 ? FIELD_UPPER : FIELD_LOWER; | |
447 |
3/4✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
|
25 | full = out->color_range == AVCOL_RANGE_JPEG || ff_fmt_is_in(out->format, full_scale_yuvj_pix_fmts); |
448 | /* copy upper and lower fields */ | ||
449 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
450 | 25 | (const uint8_t **)cur->data, cur->linesize, | |
451 | 25 | inlink->format, inlink->w, inlink->h, | |
452 | FIELD_UPPER_AND_LOWER, 1, field, tinterlace->flags); | ||
453 | /* pad with black the other field */ | ||
454 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
455 | 25 | (const uint8_t **)tinterlace->black_data[full], tinterlace->black_linesize, | |
456 | 25 | inlink->format, inlink->w, inlink->h, | |
457 | FIELD_UPPER_AND_LOWER, 1, !field, tinterlace->flags); | ||
458 | 25 | break; | |
459 | |||
460 | /* interleave upper/lower lines from odd frames with lower/upper lines from even frames, | ||
461 | * halving the frame rate and preserving image height */ | ||
462 | 135 | case MODE_INTERLEAVE_TOP: /* top field first */ | |
463 | case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ | ||
464 |
3/4✓ Branch 0 taken 75 times.
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 75 times.
|
135 | if ((tinterlace->flags & TINTERLACE_FLAG_BYPASS_IL) && (cur->flags & AV_FRAME_FLAG_INTERLACED)) { |
465 | ✗ | av_log(ctx, AV_LOG_WARNING, | |
466 | "video is already interlaced, adjusting framerate only\n"); | ||
467 | ✗ | out = av_frame_clone(cur); | |
468 | ✗ | if (!out) | |
469 | ✗ | return AVERROR(ENOMEM); | |
470 | ✗ | out->pts /= 2; // adjust pts to new framerate | |
471 | ✗ | ff_ccfifo_inject(&tinterlace->cc_fifo, out); | |
472 | ✗ | ret = ff_filter_frame(outlink, out); | |
473 | ✗ | return ret; | |
474 | } | ||
475 | 135 | tff = tinterlace->mode == MODE_INTERLEAVE_TOP; | |
476 | 135 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
477 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
|
135 | if (!out) |
478 | ✗ | return AVERROR(ENOMEM); | |
479 | 135 | av_frame_copy_props(out, cur); | |
480 | 135 | out->flags |= AV_FRAME_FLAG_INTERLACED; | |
481 |
1/2✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
|
135 | if (tff) |
482 | 135 | out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
483 | else | ||
484 | ✗ | out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
485 | |||
486 | /* copy upper/lower field from cur */ | ||
487 | 135 | copy_picture_field(tinterlace, out->data, out->linesize, | |
488 | 135 | (const uint8_t **)cur->data, cur->linesize, | |
489 | 135 | inlink->format, inlink->w, inlink->h, | |
490 | tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, | ||
491 | tinterlace->flags); | ||
492 | /* copy lower/upper field from next */ | ||
493 | 135 | copy_picture_field(tinterlace, out->data, out->linesize, | |
494 | 135 | (const uint8_t **)next->data, next->linesize, | |
495 | 135 | inlink->format, inlink->w, inlink->h, | |
496 | tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, | ||
497 | tinterlace->flags); | ||
498 | 135 | av_frame_free(&tinterlace->next); | |
499 | 135 | break; | |
500 | ✗ | case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */ | |
501 | /* output current frame first */ | ||
502 | ✗ | out = av_frame_clone(cur); | |
503 | ✗ | if (!out) | |
504 | ✗ | return AVERROR(ENOMEM); | |
505 | ✗ | out->flags |= AV_FRAME_FLAG_INTERLACED; | |
506 | ✗ | if (cur->pts != AV_NOPTS_VALUE) | |
507 | ✗ | out->pts = cur->pts*2; | |
508 | |||
509 | ✗ | out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); | |
510 | ✗ | ff_ccfifo_inject(&tinterlace->cc_fifo, out); | |
511 | ✗ | if ((ret = ff_filter_frame(outlink, out)) < 0) | |
512 | ✗ | return ret; | |
513 | |||
514 | /* output mix of current and next frame */ | ||
515 | ✗ | tff = !!(next->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); | |
516 | ✗ | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
517 | ✗ | if (!out) | |
518 | ✗ | return AVERROR(ENOMEM); | |
519 | ✗ | av_frame_copy_props(out, next); | |
520 | ✗ | out->flags |= AV_FRAME_FLAG_INTERLACED; | |
521 | ✗ | if (tff) | |
522 | ✗ | out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
523 | else | ||
524 | ✗ | out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
525 | |||
526 | ✗ | if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE) | |
527 | ✗ | out->pts = cur->pts + next->pts; | |
528 | else | ||
529 | ✗ | out->pts = AV_NOPTS_VALUE; | |
530 | /* write current frame second field lines into the second field of the new frame */ | ||
531 | ✗ | copy_picture_field(tinterlace, out->data, out->linesize, | |
532 | ✗ | (const uint8_t **)cur->data, cur->linesize, | |
533 | ✗ | inlink->format, inlink->w, inlink->h, | |
534 | tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, | ||
535 | tinterlace->flags); | ||
536 | /* write next frame first field lines into the first field of the new frame */ | ||
537 | ✗ | copy_picture_field(tinterlace, out->data, out->linesize, | |
538 | ✗ | (const uint8_t **)next->data, next->linesize, | |
539 | ✗ | inlink->format, inlink->w, inlink->h, | |
540 | tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, | ||
541 | tinterlace->flags); | ||
542 | ✗ | break; | |
543 | ✗ | default: | |
544 | ✗ | av_assert0(0); | |
545 | } | ||
546 | |||
547 | 185 | out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); | |
548 | 185 | out->duration = av_rescale_q(1, av_inv_q(l->frame_rate), outlink->time_base); | |
549 | 185 | ff_ccfifo_inject(&tinterlace->cc_fifo, out); | |
550 | 185 | ret = ff_filter_frame(outlink, out); | |
551 | |||
552 | 185 | return ret; | |
553 | } | ||
554 | |||
555 | 6 | static int init_interlace(AVFilterContext *ctx) | |
556 | { | ||
557 | 6 | TInterlaceContext *tinterlace = ctx->priv; | |
558 | |||
559 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (tinterlace->mode <= MODE_BFF) |
560 | 6 | tinterlace->mode += MODE_INTERLEAVE_TOP; | |
561 | |||
562 | 6 | tinterlace->flags |= TINTERLACE_FLAG_BYPASS_IL; | |
563 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | if (tinterlace->lowpass == VLPF_LIN) |
564 | 4 | tinterlace->flags |= TINTERLACE_FLAG_VLPF; | |
565 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (tinterlace->lowpass == VLPF_CMP) |
566 | 2 | tinterlace->flags |= TINTERLACE_FLAG_CVLPF; | |
567 | |||
568 | 6 | return 0; | |
569 | } | ||
570 | |||
571 | static const AVFilterPad tinterlace_inputs[] = { | ||
572 | { | ||
573 | .name = "default", | ||
574 | .type = AVMEDIA_TYPE_VIDEO, | ||
575 | .filter_frame = filter_frame, | ||
576 | }, | ||
577 | }; | ||
578 | |||
579 | static const AVFilterPad tinterlace_outputs[] = { | ||
580 | { | ||
581 | .name = "default", | ||
582 | .type = AVMEDIA_TYPE_VIDEO, | ||
583 | .config_props = config_out_props, | ||
584 | }, | ||
585 | }; | ||
586 | |||
587 | const FFFilter ff_vf_tinterlace = { | ||
588 | .p.name = "tinterlace", | ||
589 | .p.description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."), | ||
590 | .p.priv_class = &tinterlace_class, | ||
591 | .priv_size = sizeof(TInterlaceContext), | ||
592 | .uninit = uninit, | ||
593 | FILTER_INPUTS(tinterlace_inputs), | ||
594 | FILTER_OUTPUTS(tinterlace_outputs), | ||
595 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
596 | }; | ||
597 | |||
598 | |||
599 | const FFFilter ff_vf_interlace = { | ||
600 | .p.name = "interlace", | ||
601 | .p.description = NULL_IF_CONFIG_SMALL("Convert progressive video into interlaced."), | ||
602 | .p.priv_class = &interlace_class, | ||
603 | .priv_size = sizeof(TInterlaceContext), | ||
604 | .init = init_interlace, | ||
605 | .uninit = uninit, | ||
606 | FILTER_INPUTS(tinterlace_inputs), | ||
607 | FILTER_OUTPUTS(tinterlace_outputs), | ||
608 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
609 | }; | ||
610 |