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 2774592 times.
✓ Branch 1 taken 4080960 times.
|
6855552 | if (src_ab > src_x) { |
158 |
2/2✓ Branch 0 taken 384549 times.
✓ Branch 1 taken 2390043 times.
|
2774592 | if (dstp[i] < srcp[i]) |
159 | 384549 | dstp[i] = srcp[i]; | |
160 |
2/2✓ Branch 0 taken 487484 times.
✓ Branch 1 taken 3593476 times.
|
4080960 | } else if (dstp[i] > srcp[i]) |
161 | 487484 | 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 | ff_draw_init2(&tinterlace->draw, outlink->format, outlink->colorspace, outlink->color_range, 0); | |
232 | 25 | ff_draw_color(&tinterlace->draw, &tinterlace->color, black); | |
233 | /* limited range */ | ||
234 |
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)) { |
235 | 21 | ret = av_image_alloc(tinterlace->black_data[0], tinterlace->black_linesize, | |
236 | 21 | outlink->w, outlink->h, outlink->format, 16); | |
237 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | if (ret < 0) |
238 | ✗ | return ret; | |
239 | 21 | ff_fill_rectangle(&tinterlace->draw, &tinterlace->color, tinterlace->black_data[0], | |
240 | 21 | tinterlace->black_linesize, 0, 0, outlink->w, outlink->h); | |
241 | } | ||
242 | /* full range */ | ||
243 | 25 | tinterlace->color.comp[0].u8[0] = 0; | |
244 | 25 | ret = av_image_alloc(tinterlace->black_data[1], tinterlace->black_linesize, | |
245 | 25 | outlink->w, outlink->h, outlink->format, 16); | |
246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (ret < 0) |
247 | ✗ | return ret; | |
248 | 25 | ff_fill_rectangle(&tinterlace->draw, &tinterlace->color, tinterlace->black_data[1], | |
249 | 25 | tinterlace->black_linesize, 0, 0, outlink->w, outlink->h); | |
250 | } | ||
251 |
2/2✓ Branch 0 taken 53 times.
✓ Branch 1 taken 60 times.
|
113 | if (tinterlace->flags & (TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF) |
252 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
|
53 | && !(tinterlace->mode == MODE_INTERLEAVE_TOP |
253 | ✗ | || tinterlace->mode == MODE_INTERLEAVE_BOTTOM)) { | |
254 | ✗ | av_log(ctx, AV_LOG_WARNING, "low_pass_filter flags ignored with mode %d\n", | |
255 | tinterlace->mode); | ||
256 | ✗ | tinterlace->flags &= ~(TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF); | |
257 | } | ||
258 | 113 | tinterlace->preout_time_base = inlink->time_base; | |
259 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | if (tinterlace->mode == MODE_INTERLACEX2) { |
260 | ✗ | tinterlace->preout_time_base.den *= 2; | |
261 | ✗ | ol->frame_rate = av_mul_q(il->frame_rate, (AVRational){2,1}); | |
262 | ✗ | outlink->time_base = av_mul_q(inlink->time_base , (AVRational){1,2}); | |
263 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | } else if (tinterlace->mode == MODE_MERGEX2) { |
264 | ✗ | ol->frame_rate = il->frame_rate; | |
265 | ✗ | outlink->time_base = inlink->time_base; | |
266 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 25 times.
|
113 | } else if (tinterlace->mode != MODE_PAD) { |
267 | 88 | ol->frame_rate = av_mul_q(il->frame_rate, (AVRational){1,2}); | |
268 | 88 | outlink->time_base = av_mul_q(inlink->time_base , (AVRational){2,1}); | |
269 | } | ||
270 | |||
271 |
2/2✓ Branch 0 taken 339 times.
✓ Branch 1 taken 113 times.
|
452 | for (i = 0; i<FF_ARRAY_ELEMS(standard_tbs); i++){ |
272 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 339 times.
|
339 | if (!av_cmp_q(standard_tbs[i], outlink->time_base)) |
273 | ✗ | break; | |
274 | } | ||
275 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | if (i == FF_ARRAY_ELEMS(standard_tbs) || |
276 | ✗ | (tinterlace->flags & TINTERLACE_FLAG_EXACT_TB)) | |
277 | 113 | outlink->time_base = tinterlace->preout_time_base; | |
278 | |||
279 | 113 | tinterlace->csp = av_pix_fmt_desc_get(outlink->format); | |
280 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 87 times.
|
113 | if (tinterlace->flags & TINTERLACE_FLAG_CVLPF) { |
281 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 15 times.
|
26 | if (tinterlace->csp->comp[0].depth > 8) |
282 | 11 | tinterlace->lowpass_line = lowpass_line_complex_c_16; | |
283 | else | ||
284 | 15 | tinterlace->lowpass_line = lowpass_line_complex_c; | |
285 | #if ARCH_X86 | ||
286 | 26 | ff_tinterlace_init_x86(tinterlace); | |
287 | #endif | ||
288 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 60 times.
|
87 | } else if (tinterlace->flags & TINTERLACE_FLAG_VLPF) { |
289 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 16 times.
|
27 | if (tinterlace->csp->comp[0].depth > 8) |
290 | 11 | tinterlace->lowpass_line = lowpass_line_c_16; | |
291 | else | ||
292 | 16 | tinterlace->lowpass_line = lowpass_line_c; | |
293 | #if ARCH_X86 | ||
294 | 27 | ff_tinterlace_init_x86(tinterlace); | |
295 | #endif | ||
296 | } | ||
297 | |||
298 | 113 | ret = ff_ccfifo_init(&tinterlace->cc_fifo, ol->frame_rate, ctx); | |
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | if (ret < 0) { |
300 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); | |
301 | ✗ | return ret; | |
302 | } | ||
303 | |||
304 | 113 | av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", tinterlace->mode, | |
305 |
2/2✓ Branch 0 taken 87 times.
✓ Branch 1 taken 26 times.
|
113 | (tinterlace->flags & TINTERLACE_FLAG_CVLPF) ? "complex" : |
306 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 60 times.
|
87 | (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "linear" : "off", |
307 | inlink->h, outlink->h); | ||
308 | |||
309 | 113 | return 0; | |
310 | } | ||
311 | |||
312 | #define FIELD_UPPER 0 | ||
313 | #define FIELD_LOWER 1 | ||
314 | #define FIELD_UPPER_AND_LOWER 2 | ||
315 | |||
316 | /** | ||
317 | * Copy picture field from src to dst. | ||
318 | * | ||
319 | * @param src_field copy from upper, lower field or both | ||
320 | * @param interleave leave a padding line between each copied line | ||
321 | * @param dst_field copy to upper or lower field, | ||
322 | * only meaningful when interleave is selected | ||
323 | * @param flags context flags | ||
324 | */ | ||
325 | static inline | ||
326 | 370 | void copy_picture_field(TInterlaceContext *tinterlace, | |
327 | uint8_t *dst[4], int dst_linesize[4], | ||
328 | const uint8_t *src[4], int src_linesize[4], | ||
329 | enum AVPixelFormat format, int w, int src_h, | ||
330 | int src_field, int interleave, int dst_field, | ||
331 | int flags) | ||
332 | { | ||
333 | 370 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format); | |
334 | 370 | int hsub = desc->log2_chroma_w; | |
335 | 370 | int plane, vsub = desc->log2_chroma_h; | |
336 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 270 times.
|
370 | int k = src_field == FIELD_UPPER_AND_LOWER ? 1 : 2; |
337 | int h; | ||
338 | |||
339 |
2/2✓ Branch 0 taken 1142 times.
✓ Branch 1 taken 370 times.
|
1512 | for (plane = 0; plane < desc->nb_components; plane++) { |
340 |
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; |
341 |
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; |
342 | 1142 | uint8_t *dstp = dst[plane]; | |
343 | 1142 | const uint8_t *srcp = src[plane]; | |
344 | 1142 | int srcp_linesize = src_linesize[plane] * k; | |
345 |
1/2✓ Branch 0 taken 1142 times.
✗ Branch 1 not taken.
|
1142 | int dstp_linesize = dst_linesize[plane] * (interleave ? 2 : 1); |
346 | 1142 | int clip_max = (1 << tinterlace->csp->comp[plane].depth) - 1; | |
347 | |||
348 | 1142 | lines = (lines + (src_field == FIELD_UPPER)) / k; | |
349 |
2/2✓ Branch 0 taken 413 times.
✓ Branch 1 taken 729 times.
|
1142 | if (src_field == FIELD_LOWER) |
350 | 413 | srcp += src_linesize[plane]; | |
351 |
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) |
352 | 571 | dstp += dst_linesize[plane]; | |
353 | // Low-pass filtering is required when creating an interlaced destination from | ||
354 | // a progressive source which contains high-frequency vertical detail. | ||
355 | // Filtering will reduce interlace 'twitter' and Moire patterning. | ||
356 |
2/2✓ Branch 0 taken 766 times.
✓ Branch 1 taken 376 times.
|
1142 | if (flags & (TINTERLACE_FLAG_VLPF | TINTERLACE_FLAG_CVLPF)) { |
357 | 766 | int x = !!(flags & TINTERLACE_FLAG_CVLPF); | |
358 |
2/2✓ Branch 0 taken 82080 times.
✓ Branch 1 taken 766 times.
|
82846 | for (h = lines; h > 0; h--) { |
359 | 82080 | ptrdiff_t pref = src_linesize[plane]; | |
360 | 82080 | ptrdiff_t mref = -pref; | |
361 |
2/2✓ Branch 0 taken 1074 times.
✓ Branch 1 taken 81006 times.
|
82080 | if (h >= (lines - x)) mref = 0; // there is no line above |
362 |
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 |
363 | |||
364 | 82080 | tinterlace->lowpass_line(dstp, cols, srcp, mref, pref, clip_max); | |
365 | 82080 | dstp += dstp_linesize; | |
366 | 82080 | srcp += srcp_linesize; | |
367 | } | ||
368 | } else { | ||
369 |
2/2✓ Branch 0 taken 144 times.
✓ Branch 1 taken 232 times.
|
376 | if (tinterlace->csp->comp[plane].depth > 8) |
370 | 144 | cols *= 2; | |
371 | 376 | av_image_copy_plane(dstp, dstp_linesize, srcp, srcp_linesize, cols, lines); | |
372 | } | ||
373 | } | ||
374 | 370 | } | |
375 | |||
376 | 370 | static int filter_frame(AVFilterLink *inlink, AVFrame *picref) | |
377 | { | ||
378 | 370 | FilterLink *inl = ff_filter_link(inlink); | |
379 | 370 | AVFilterContext *ctx = inlink->dst; | |
380 | 370 | AVFilterLink *outlink = ctx->outputs[0]; | |
381 | 370 | FilterLink *l = ff_filter_link(outlink); | |
382 | 370 | TInterlaceContext *tinterlace = ctx->priv; | |
383 | AVFrame *cur, *next, *out; | ||
384 | int field, tff, full, ret; | ||
385 | |||
386 | 370 | av_frame_free(&tinterlace->cur); | |
387 | 370 | tinterlace->cur = tinterlace->next; | |
388 | 370 | tinterlace->next = picref; | |
389 | |||
390 | 370 | ff_ccfifo_extract(&tinterlace->cc_fifo, picref); | |
391 | |||
392 | 370 | cur = tinterlace->cur; | |
393 | 370 | next = tinterlace->next; | |
394 | /* we need at least two frames */ | ||
395 |
2/2✓ Branch 0 taken 185 times.
✓ Branch 1 taken 185 times.
|
370 | if (!tinterlace->cur) |
396 | 185 | return 0; | |
397 | |||
398 |
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) { |
399 | 25 | case MODE_MERGEX2: /* move the odd frame into the upper field of the new image, even into | |
400 | * the lower field, generating a double-height video at same framerate */ | ||
401 | case MODE_MERGE: /* move the odd frame into the upper field of the new image, even into | ||
402 | * the lower field, generating a double-height video at half framerate */ | ||
403 | 25 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
404 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (!out) |
405 | ✗ | return AVERROR(ENOMEM); | |
406 | 25 | av_frame_copy_props(out, cur); | |
407 | 25 | out->height = outlink->h; | |
408 | #if FF_API_INTERLACED_FRAME | ||
409 | FF_DISABLE_DEPRECATION_WARNINGS | ||
410 | 25 | out->interlaced_frame = 1; | |
411 | 25 | out->top_field_first = 1; | |
412 | FF_ENABLE_DEPRECATION_WARNINGS | ||
413 | #endif | ||
414 | 25 | out->flags |= AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
415 | 25 | out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); | |
416 | |||
417 | /* write odd frame lines into the upper field of the new frame */ | ||
418 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
419 | 25 | (const uint8_t **)cur->data, cur->linesize, | |
420 | 25 | inlink->format, inlink->w, inlink->h, | |
421 |
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); |
422 | /* write even frame lines into the lower field of the new frame */ | ||
423 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
424 | 25 | (const uint8_t **)next->data, next->linesize, | |
425 | 25 | inlink->format, inlink->w, inlink->h, | |
426 |
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); |
427 |
1/2✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
|
25 | if (tinterlace->mode != MODE_MERGEX2) |
428 | 25 | av_frame_free(&tinterlace->next); | |
429 | 25 | break; | |
430 | |||
431 | ✗ | case MODE_DROP_ODD: /* only output even frames, odd frames are dropped; height unchanged, half framerate */ | |
432 | case MODE_DROP_EVEN: /* only output odd frames, even frames are dropped; height unchanged, half framerate */ | ||
433 | ✗ | out = av_frame_clone(tinterlace->mode == MODE_DROP_EVEN ? cur : next); | |
434 | ✗ | if (!out) | |
435 | ✗ | return AVERROR(ENOMEM); | |
436 | ✗ | av_frame_free(&tinterlace->next); | |
437 | ✗ | break; | |
438 | |||
439 | 25 | case MODE_PAD: /* expand each frame to double height, but pad alternate | |
440 | * lines with black; framerate unchanged */ | ||
441 | 25 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
442 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | if (!out) |
443 | ✗ | return AVERROR(ENOMEM); | |
444 | 25 | av_frame_copy_props(out, cur); | |
445 | 25 | out->height = outlink->h; | |
446 | 25 | out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); | |
447 | |||
448 | 25 | field = (1 + l->frame_count_in) & 1 ? FIELD_UPPER : FIELD_LOWER; | |
449 |
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); |
450 | /* copy upper and lower fields */ | ||
451 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
452 | 25 | (const uint8_t **)cur->data, cur->linesize, | |
453 | 25 | inlink->format, inlink->w, inlink->h, | |
454 | FIELD_UPPER_AND_LOWER, 1, field, tinterlace->flags); | ||
455 | /* pad with black the other field */ | ||
456 | 25 | copy_picture_field(tinterlace, out->data, out->linesize, | |
457 | 25 | (const uint8_t **)tinterlace->black_data[full], tinterlace->black_linesize, | |
458 | 25 | inlink->format, inlink->w, inlink->h, | |
459 | FIELD_UPPER_AND_LOWER, 1, !field, tinterlace->flags); | ||
460 | 25 | break; | |
461 | |||
462 | /* interleave upper/lower lines from odd frames with lower/upper lines from even frames, | ||
463 | * halving the frame rate and preserving image height */ | ||
464 | 135 | case MODE_INTERLEAVE_TOP: /* top field first */ | |
465 | case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ | ||
466 |
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)) { |
467 | ✗ | av_log(ctx, AV_LOG_WARNING, | |
468 | "video is already interlaced, adjusting framerate only\n"); | ||
469 | ✗ | out = av_frame_clone(cur); | |
470 | ✗ | if (!out) | |
471 | ✗ | return AVERROR(ENOMEM); | |
472 | ✗ | out->pts /= 2; // adjust pts to new framerate | |
473 | ✗ | ff_ccfifo_inject(&tinterlace->cc_fifo, out); | |
474 | ✗ | ret = ff_filter_frame(outlink, out); | |
475 | ✗ | return ret; | |
476 | } | ||
477 | 135 | tff = tinterlace->mode == MODE_INTERLEAVE_TOP; | |
478 | 135 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
479 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
|
135 | if (!out) |
480 | ✗ | return AVERROR(ENOMEM); | |
481 | 135 | av_frame_copy_props(out, cur); | |
482 | #if FF_API_INTERLACED_FRAME | ||
483 | FF_DISABLE_DEPRECATION_WARNINGS | ||
484 | 135 | out->interlaced_frame = 1; | |
485 | 135 | out->top_field_first = tff; | |
486 | FF_ENABLE_DEPRECATION_WARNINGS | ||
487 | #endif | ||
488 | 135 | out->flags |= AV_FRAME_FLAG_INTERLACED; | |
489 |
1/2✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
|
135 | if (tff) |
490 | 135 | out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
491 | else | ||
492 | ✗ | out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
493 | |||
494 | /* copy upper/lower field from cur */ | ||
495 | 135 | copy_picture_field(tinterlace, out->data, out->linesize, | |
496 | 135 | (const uint8_t **)cur->data, cur->linesize, | |
497 | 135 | inlink->format, inlink->w, inlink->h, | |
498 | tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, | ||
499 | tinterlace->flags); | ||
500 | /* copy lower/upper field from next */ | ||
501 | 135 | copy_picture_field(tinterlace, out->data, out->linesize, | |
502 | 135 | (const uint8_t **)next->data, next->linesize, | |
503 | 135 | inlink->format, inlink->w, inlink->h, | |
504 | tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, | ||
505 | tinterlace->flags); | ||
506 | 135 | av_frame_free(&tinterlace->next); | |
507 | 135 | break; | |
508 | ✗ | case MODE_INTERLACEX2: /* re-interlace preserving image height, double frame rate */ | |
509 | /* output current frame first */ | ||
510 | ✗ | out = av_frame_clone(cur); | |
511 | ✗ | if (!out) | |
512 | ✗ | return AVERROR(ENOMEM); | |
513 | #if FF_API_INTERLACED_FRAME | ||
514 | FF_DISABLE_DEPRECATION_WARNINGS | ||
515 | ✗ | out->interlaced_frame = 1; | |
516 | FF_ENABLE_DEPRECATION_WARNINGS | ||
517 | #endif | ||
518 | ✗ | out->flags |= AV_FRAME_FLAG_INTERLACED; | |
519 | ✗ | if (cur->pts != AV_NOPTS_VALUE) | |
520 | ✗ | out->pts = cur->pts*2; | |
521 | |||
522 | ✗ | out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); | |
523 | ✗ | ff_ccfifo_inject(&tinterlace->cc_fifo, out); | |
524 | ✗ | if ((ret = ff_filter_frame(outlink, out)) < 0) | |
525 | ✗ | return ret; | |
526 | |||
527 | /* output mix of current and next frame */ | ||
528 | ✗ | tff = !!(next->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); | |
529 | ✗ | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
530 | ✗ | if (!out) | |
531 | ✗ | return AVERROR(ENOMEM); | |
532 | ✗ | av_frame_copy_props(out, next); | |
533 | #if FF_API_INTERLACED_FRAME | ||
534 | FF_DISABLE_DEPRECATION_WARNINGS | ||
535 | ✗ | out->interlaced_frame = 1; | |
536 | ✗ | out->top_field_first = !tff; | |
537 | FF_ENABLE_DEPRECATION_WARNINGS | ||
538 | #endif | ||
539 | ✗ | out->flags |= AV_FRAME_FLAG_INTERLACED; | |
540 | ✗ | if (tff) | |
541 | ✗ | out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
542 | else | ||
543 | ✗ | out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; | |
544 | |||
545 | ✗ | if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE) | |
546 | ✗ | out->pts = cur->pts + next->pts; | |
547 | else | ||
548 | ✗ | out->pts = AV_NOPTS_VALUE; | |
549 | /* write current frame second field lines into the second field of the new frame */ | ||
550 | ✗ | copy_picture_field(tinterlace, out->data, out->linesize, | |
551 | ✗ | (const uint8_t **)cur->data, cur->linesize, | |
552 | ✗ | inlink->format, inlink->w, inlink->h, | |
553 | tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER, | ||
554 | tinterlace->flags); | ||
555 | /* write next frame first field lines into the first field of the new frame */ | ||
556 | ✗ | copy_picture_field(tinterlace, out->data, out->linesize, | |
557 | ✗ | (const uint8_t **)next->data, next->linesize, | |
558 | ✗ | inlink->format, inlink->w, inlink->h, | |
559 | tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER, | ||
560 | tinterlace->flags); | ||
561 | ✗ | break; | |
562 | ✗ | default: | |
563 | ✗ | av_assert0(0); | |
564 | } | ||
565 | |||
566 | 185 | out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); | |
567 | 185 | out->duration = av_rescale_q(1, av_inv_q(l->frame_rate), outlink->time_base); | |
568 | 185 | ff_ccfifo_inject(&tinterlace->cc_fifo, out); | |
569 | 185 | ret = ff_filter_frame(outlink, out); | |
570 | |||
571 | 185 | return ret; | |
572 | } | ||
573 | |||
574 | 6 | static int init_interlace(AVFilterContext *ctx) | |
575 | { | ||
576 | 6 | TInterlaceContext *tinterlace = ctx->priv; | |
577 | |||
578 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (tinterlace->mode <= MODE_BFF) |
579 | 6 | tinterlace->mode += MODE_INTERLEAVE_TOP; | |
580 | |||
581 | 6 | tinterlace->flags |= TINTERLACE_FLAG_BYPASS_IL; | |
582 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | if (tinterlace->lowpass == VLPF_LIN) |
583 | 4 | tinterlace->flags |= TINTERLACE_FLAG_VLPF; | |
584 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (tinterlace->lowpass == VLPF_CMP) |
585 | 2 | tinterlace->flags |= TINTERLACE_FLAG_CVLPF; | |
586 | |||
587 | 6 | return 0; | |
588 | } | ||
589 | |||
590 | static const AVFilterPad tinterlace_inputs[] = { | ||
591 | { | ||
592 | .name = "default", | ||
593 | .type = AVMEDIA_TYPE_VIDEO, | ||
594 | .filter_frame = filter_frame, | ||
595 | }, | ||
596 | }; | ||
597 | |||
598 | static const AVFilterPad tinterlace_outputs[] = { | ||
599 | { | ||
600 | .name = "default", | ||
601 | .type = AVMEDIA_TYPE_VIDEO, | ||
602 | .config_props = config_out_props, | ||
603 | }, | ||
604 | }; | ||
605 | |||
606 | const AVFilter ff_vf_tinterlace = { | ||
607 | .name = "tinterlace", | ||
608 | .description = NULL_IF_CONFIG_SMALL("Perform temporal field interlacing."), | ||
609 | .priv_size = sizeof(TInterlaceContext), | ||
610 | .uninit = uninit, | ||
611 | FILTER_INPUTS(tinterlace_inputs), | ||
612 | FILTER_OUTPUTS(tinterlace_outputs), | ||
613 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
614 | .priv_class = &tinterlace_class, | ||
615 | }; | ||
616 | |||
617 | |||
618 | const AVFilter ff_vf_interlace = { | ||
619 | .name = "interlace", | ||
620 | .description = NULL_IF_CONFIG_SMALL("Convert progressive video into interlaced."), | ||
621 | .priv_size = sizeof(TInterlaceContext), | ||
622 | .init = init_interlace, | ||
623 | .uninit = uninit, | ||
624 | FILTER_INPUTS(tinterlace_inputs), | ||
625 | FILTER_OUTPUTS(tinterlace_outputs), | ||
626 | FILTER_PIXFMTS_ARRAY(pix_fmts), | ||
627 | .priv_class = &interlace_class, | ||
628 | }; | ||
629 |