blob: b7872db341929ca2bfee512553337bb9969b30eb
1 | /* |
2 | * Copyright (C) 2012 British Broadcasting Corporation, All Rights Reserved |
3 | * Author of de-interlace algorithm: Jim Easterbrook for BBC R&D |
4 | * Based on the process described by Martin Weston for BBC R&D |
5 | * Author of FFmpeg filter: Mark Himsley for BBC Broadcast Systems Development |
6 | * |
7 | * This file is part of FFmpeg. |
8 | * |
9 | * FFmpeg is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Lesser General Public |
11 | * License as published by the Free Software Foundation; either |
12 | * version 2.1 of the License, or (at your option) any later version. |
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 GNU |
17 | * Lesser General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU Lesser General Public |
20 | * License along with FFmpeg; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | */ |
23 | |
24 | #include "libavutil/common.h" |
25 | #include "libavutil/imgutils.h" |
26 | #include "libavutil/opt.h" |
27 | #include "libavutil/pixdesc.h" |
28 | #include "avfilter.h" |
29 | #include "formats.h" |
30 | #include "internal.h" |
31 | #include "video.h" |
32 | #include "w3fdif.h" |
33 | |
34 | typedef struct W3FDIFContext { |
35 | const AVClass *class; |
36 | int filter; ///< 0 is simple, 1 is more complex |
37 | int deint; ///< which frames to deinterlace |
38 | int linesize[4]; ///< bytes of pixel data per line for each plane |
39 | int planeheight[4]; ///< height of each plane |
40 | int field; ///< which field are we on, 0 or 1 |
41 | int eof; |
42 | int nb_planes; |
43 | AVFrame *prev, *cur, *next; ///< previous, current, next frames |
44 | int32_t **work_line; ///< lines we are calculating |
45 | int nb_threads; |
46 | int max; |
47 | |
48 | W3FDIFDSPContext dsp; |
49 | } W3FDIFContext; |
50 | |
51 | #define OFFSET(x) offsetof(W3FDIFContext, x) |
52 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
53 | #define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit } |
54 | |
55 | static const AVOption w3fdif_options[] = { |
56 | { "filter", "specify the filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "filter" }, |
57 | CONST("simple", NULL, 0, "filter"), |
58 | CONST("complex", NULL, 1, "filter"), |
59 | { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "deint" }, |
60 | CONST("all", "deinterlace all frames", 0, "deint"), |
61 | CONST("interlaced", "only deinterlace frames marked as interlaced", 1, "deint"), |
62 | { NULL } |
63 | }; |
64 | |
65 | AVFILTER_DEFINE_CLASS(w3fdif); |
66 | |
67 | static int query_formats(AVFilterContext *ctx) |
68 | { |
69 | static const enum AVPixelFormat pix_fmts[] = { |
70 | AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, |
71 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, |
72 | AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, |
73 | AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, |
74 | AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P, |
75 | AV_PIX_FMT_YUVJ411P, |
76 | AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, |
77 | AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, |
78 | AV_PIX_FMT_GRAY8, |
79 | AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, |
80 | AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, |
81 | AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, |
82 | AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, |
83 | AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, |
84 | AV_PIX_FMT_NONE |
85 | }; |
86 | |
87 | AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); |
88 | if (!fmts_list) |
89 | return AVERROR(ENOMEM); |
90 | return ff_set_common_formats(ctx, fmts_list); |
91 | } |
92 | |
93 | static void filter_simple_low(int32_t *work_line, |
94 | uint8_t *in_lines_cur[2], |
95 | const int16_t *coef, int linesize) |
96 | { |
97 | int i; |
98 | |
99 | for (i = 0; i < linesize; i++) { |
100 | *work_line = *in_lines_cur[0]++ * coef[0]; |
101 | *work_line++ += *in_lines_cur[1]++ * coef[1]; |
102 | } |
103 | } |
104 | |
105 | static void filter_complex_low(int32_t *work_line, |
106 | uint8_t *in_lines_cur[4], |
107 | const int16_t *coef, int linesize) |
108 | { |
109 | int i; |
110 | |
111 | for (i = 0; i < linesize; i++) { |
112 | *work_line = *in_lines_cur[0]++ * coef[0]; |
113 | *work_line += *in_lines_cur[1]++ * coef[1]; |
114 | *work_line += *in_lines_cur[2]++ * coef[2]; |
115 | *work_line++ += *in_lines_cur[3]++ * coef[3]; |
116 | } |
117 | } |
118 | |
119 | static void filter_simple_high(int32_t *work_line, |
120 | uint8_t *in_lines_cur[3], |
121 | uint8_t *in_lines_adj[3], |
122 | const int16_t *coef, int linesize) |
123 | { |
124 | int i; |
125 | |
126 | for (i = 0; i < linesize; i++) { |
127 | *work_line += *in_lines_cur[0]++ * coef[0]; |
128 | *work_line += *in_lines_adj[0]++ * coef[0]; |
129 | *work_line += *in_lines_cur[1]++ * coef[1]; |
130 | *work_line += *in_lines_adj[1]++ * coef[1]; |
131 | *work_line += *in_lines_cur[2]++ * coef[2]; |
132 | *work_line++ += *in_lines_adj[2]++ * coef[2]; |
133 | } |
134 | } |
135 | |
136 | static void filter_complex_high(int32_t *work_line, |
137 | uint8_t *in_lines_cur[5], |
138 | uint8_t *in_lines_adj[5], |
139 | const int16_t *coef, int linesize) |
140 | { |
141 | int i; |
142 | |
143 | for (i = 0; i < linesize; i++) { |
144 | *work_line += *in_lines_cur[0]++ * coef[0]; |
145 | *work_line += *in_lines_adj[0]++ * coef[0]; |
146 | *work_line += *in_lines_cur[1]++ * coef[1]; |
147 | *work_line += *in_lines_adj[1]++ * coef[1]; |
148 | *work_line += *in_lines_cur[2]++ * coef[2]; |
149 | *work_line += *in_lines_adj[2]++ * coef[2]; |
150 | *work_line += *in_lines_cur[3]++ * coef[3]; |
151 | *work_line += *in_lines_adj[3]++ * coef[3]; |
152 | *work_line += *in_lines_cur[4]++ * coef[4]; |
153 | *work_line++ += *in_lines_adj[4]++ * coef[4]; |
154 | } |
155 | } |
156 | |
157 | static void filter_scale(uint8_t *out_pixel, const int32_t *work_pixel, int linesize, int max) |
158 | { |
159 | int j; |
160 | |
161 | for (j = 0; j < linesize; j++, out_pixel++, work_pixel++) |
162 | *out_pixel = av_clip(*work_pixel, 0, 255 * 256 * 128) >> 15; |
163 | } |
164 | |
165 | static void filter16_simple_low(int32_t *work_line, |
166 | uint8_t *in_lines_cur8[2], |
167 | const int16_t *coef, int linesize) |
168 | { |
169 | uint16_t *in_lines_cur[2] = { (uint16_t *)in_lines_cur8[0], (uint16_t *)in_lines_cur8[1] }; |
170 | int i; |
171 | |
172 | linesize /= 2; |
173 | for (i = 0; i < linesize; i++) { |
174 | *work_line = *in_lines_cur[0]++ * coef[0]; |
175 | *work_line++ += *in_lines_cur[1]++ * coef[1]; |
176 | } |
177 | } |
178 | |
179 | static void filter16_complex_low(int32_t *work_line, |
180 | uint8_t *in_lines_cur8[4], |
181 | const int16_t *coef, int linesize) |
182 | { |
183 | uint16_t *in_lines_cur[4] = { (uint16_t *)in_lines_cur8[0], |
184 | (uint16_t *)in_lines_cur8[1], |
185 | (uint16_t *)in_lines_cur8[2], |
186 | (uint16_t *)in_lines_cur8[3] }; |
187 | int i; |
188 | |
189 | linesize /= 2; |
190 | for (i = 0; i < linesize; i++) { |
191 | *work_line = *in_lines_cur[0]++ * coef[0]; |
192 | *work_line += *in_lines_cur[1]++ * coef[1]; |
193 | *work_line += *in_lines_cur[2]++ * coef[2]; |
194 | *work_line++ += *in_lines_cur[3]++ * coef[3]; |
195 | } |
196 | } |
197 | |
198 | static void filter16_simple_high(int32_t *work_line, |
199 | uint8_t *in_lines_cur8[3], |
200 | uint8_t *in_lines_adj8[3], |
201 | const int16_t *coef, int linesize) |
202 | { |
203 | uint16_t *in_lines_cur[3] = { (uint16_t *)in_lines_cur8[0], |
204 | (uint16_t *)in_lines_cur8[1], |
205 | (uint16_t *)in_lines_cur8[2] }; |
206 | uint16_t *in_lines_adj[3] = { (uint16_t *)in_lines_adj8[0], |
207 | (uint16_t *)in_lines_adj8[1], |
208 | (uint16_t *)in_lines_adj8[2] }; |
209 | int i; |
210 | |
211 | linesize /= 2; |
212 | for (i = 0; i < linesize; i++) { |
213 | *work_line += *in_lines_cur[0]++ * coef[0]; |
214 | *work_line += *in_lines_adj[0]++ * coef[0]; |
215 | *work_line += *in_lines_cur[1]++ * coef[1]; |
216 | *work_line += *in_lines_adj[1]++ * coef[1]; |
217 | *work_line += *in_lines_cur[2]++ * coef[2]; |
218 | *work_line++ += *in_lines_adj[2]++ * coef[2]; |
219 | } |
220 | } |
221 | |
222 | static void filter16_complex_high(int32_t *work_line, |
223 | uint8_t *in_lines_cur8[5], |
224 | uint8_t *in_lines_adj8[5], |
225 | const int16_t *coef, int linesize) |
226 | { |
227 | uint16_t *in_lines_cur[5] = { (uint16_t *)in_lines_cur8[0], |
228 | (uint16_t *)in_lines_cur8[1], |
229 | (uint16_t *)in_lines_cur8[2], |
230 | (uint16_t *)in_lines_cur8[3], |
231 | (uint16_t *)in_lines_cur8[4] }; |
232 | uint16_t *in_lines_adj[5] = { (uint16_t *)in_lines_adj8[0], |
233 | (uint16_t *)in_lines_adj8[1], |
234 | (uint16_t *)in_lines_adj8[2], |
235 | (uint16_t *)in_lines_adj8[3], |
236 | (uint16_t *)in_lines_adj8[4] }; |
237 | int i; |
238 | |
239 | linesize /= 2; |
240 | for (i = 0; i < linesize; i++) { |
241 | *work_line += *in_lines_cur[0]++ * coef[0]; |
242 | *work_line += *in_lines_adj[0]++ * coef[0]; |
243 | *work_line += *in_lines_cur[1]++ * coef[1]; |
244 | *work_line += *in_lines_adj[1]++ * coef[1]; |
245 | *work_line += *in_lines_cur[2]++ * coef[2]; |
246 | *work_line += *in_lines_adj[2]++ * coef[2]; |
247 | *work_line += *in_lines_cur[3]++ * coef[3]; |
248 | *work_line += *in_lines_adj[3]++ * coef[3]; |
249 | *work_line += *in_lines_cur[4]++ * coef[4]; |
250 | *work_line++ += *in_lines_adj[4]++ * coef[4]; |
251 | } |
252 | } |
253 | |
254 | static void filter16_scale(uint8_t *out_pixel8, const int32_t *work_pixel, int linesize, int max) |
255 | { |
256 | uint16_t *out_pixel = (uint16_t *)out_pixel8; |
257 | int j; |
258 | |
259 | linesize /= 2; |
260 | for (j = 0; j < linesize; j++, out_pixel++, work_pixel++) |
261 | *out_pixel = av_clip(*work_pixel, 0, max) >> 15; |
262 | } |
263 | |
264 | static int config_input(AVFilterLink *inlink) |
265 | { |
266 | AVFilterContext *ctx = inlink->dst; |
267 | W3FDIFContext *s = ctx->priv; |
268 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
269 | int ret, i, depth; |
270 | |
271 | if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0) |
272 | return ret; |
273 | |
274 | s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); |
275 | s->planeheight[0] = s->planeheight[3] = inlink->h; |
276 | |
277 | s->nb_planes = av_pix_fmt_count_planes(inlink->format); |
278 | s->nb_threads = ff_filter_get_nb_threads(ctx); |
279 | s->work_line = av_calloc(s->nb_threads, sizeof(*s->work_line)); |
280 | if (!s->work_line) |
281 | return AVERROR(ENOMEM); |
282 | |
283 | for (i = 0; i < s->nb_threads; i++) { |
284 | s->work_line[i] = av_calloc(FFALIGN(s->linesize[0], 32), sizeof(*s->work_line[0])); |
285 | if (!s->work_line[i]) |
286 | return AVERROR(ENOMEM); |
287 | } |
288 | |
289 | depth = desc->comp[0].depth; |
290 | s->max = ((1 << depth) - 1) * 256 * 128; |
291 | if (depth <= 8) { |
292 | s->dsp.filter_simple_low = filter_simple_low; |
293 | s->dsp.filter_complex_low = filter_complex_low; |
294 | s->dsp.filter_simple_high = filter_simple_high; |
295 | s->dsp.filter_complex_high = filter_complex_high; |
296 | s->dsp.filter_scale = filter_scale; |
297 | } else { |
298 | s->dsp.filter_simple_low = filter16_simple_low; |
299 | s->dsp.filter_complex_low = filter16_complex_low; |
300 | s->dsp.filter_simple_high = filter16_simple_high; |
301 | s->dsp.filter_complex_high = filter16_complex_high; |
302 | s->dsp.filter_scale = filter16_scale; |
303 | } |
304 | |
305 | if (ARCH_X86) |
306 | ff_w3fdif_init_x86(&s->dsp, depth); |
307 | |
308 | return 0; |
309 | } |
310 | |
311 | static int config_output(AVFilterLink *outlink) |
312 | { |
313 | AVFilterLink *inlink = outlink->src->inputs[0]; |
314 | |
315 | outlink->time_base.num = inlink->time_base.num; |
316 | outlink->time_base.den = inlink->time_base.den * 2; |
317 | outlink->frame_rate.num = inlink->frame_rate.num * 2; |
318 | outlink->frame_rate.den = inlink->frame_rate.den; |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | /* |
324 | * Filter coefficients from PH-2071, scaled by 256 * 128. |
325 | * Each set of coefficients has a set for low-frequencies and high-frequencies. |
326 | * n_coef_lf[] and n_coef_hf[] are the number of coefs for simple and more-complex. |
327 | * It is important for later that n_coef_lf[] is even and n_coef_hf[] is odd. |
328 | * coef_lf[][] and coef_hf[][] are the coefficients for low-frequencies |
329 | * and high-frequencies for simple and more-complex mode. |
330 | */ |
331 | static const int8_t n_coef_lf[2] = { 2, 4 }; |
332 | static const int16_t coef_lf[2][4] = {{ 16384, 16384, 0, 0}, |
333 | { -852, 17236, 17236, -852}}; |
334 | static const int8_t n_coef_hf[2] = { 3, 5 }; |
335 | static const int16_t coef_hf[2][5] = {{ -2048, 4096, -2048, 0, 0}, |
336 | { 1016, -3801, 5570, -3801, 1016}}; |
337 | |
338 | typedef struct ThreadData { |
339 | AVFrame *out, *cur, *adj; |
340 | int plane; |
341 | } ThreadData; |
342 | |
343 | static int deinterlace_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
344 | { |
345 | W3FDIFContext *s = ctx->priv; |
346 | ThreadData *td = arg; |
347 | AVFrame *out = td->out; |
348 | AVFrame *cur = td->cur; |
349 | AVFrame *adj = td->adj; |
350 | const int plane = td->plane; |
351 | const int filter = s->filter; |
352 | uint8_t *in_line, *in_lines_cur[5], *in_lines_adj[5]; |
353 | uint8_t *out_line, *out_pixel; |
354 | int32_t *work_line, *work_pixel; |
355 | uint8_t *cur_data = cur->data[plane]; |
356 | uint8_t *adj_data = adj->data[plane]; |
357 | uint8_t *dst_data = out->data[plane]; |
358 | const int linesize = s->linesize[plane]; |
359 | const int height = s->planeheight[plane]; |
360 | const int cur_line_stride = cur->linesize[plane]; |
361 | const int adj_line_stride = adj->linesize[plane]; |
362 | const int dst_line_stride = out->linesize[plane]; |
363 | const int start = (height * jobnr) / nb_jobs; |
364 | const int end = (height * (jobnr+1)) / nb_jobs; |
365 | const int max = s->max; |
366 | int j, y_in, y_out; |
367 | |
368 | /* copy unchanged the lines of the field */ |
369 | y_out = start + (s->field == cur->top_field_first) - (start & 1); |
370 | |
371 | in_line = cur_data + (y_out * cur_line_stride); |
372 | out_line = dst_data + (y_out * dst_line_stride); |
373 | |
374 | while (y_out < end) { |
375 | memcpy(out_line, in_line, linesize); |
376 | y_out += 2; |
377 | in_line += cur_line_stride * 2; |
378 | out_line += dst_line_stride * 2; |
379 | } |
380 | |
381 | /* interpolate other lines of the field */ |
382 | y_out = start + (s->field != cur->top_field_first) - (start & 1); |
383 | |
384 | out_line = dst_data + (y_out * dst_line_stride); |
385 | |
386 | while (y_out < end) { |
387 | /* get low vertical frequencies from current field */ |
388 | for (j = 0; j < n_coef_lf[filter]; j++) { |
389 | y_in = (y_out + 1) + (j * 2) - n_coef_lf[filter]; |
390 | |
391 | while (y_in < 0) |
392 | y_in += 2; |
393 | while (y_in >= height) |
394 | y_in -= 2; |
395 | |
396 | in_lines_cur[j] = cur_data + (y_in * cur_line_stride); |
397 | } |
398 | |
399 | work_line = s->work_line[jobnr]; |
400 | switch (n_coef_lf[filter]) { |
401 | case 2: |
402 | s->dsp.filter_simple_low(work_line, in_lines_cur, |
403 | coef_lf[filter], linesize); |
404 | break; |
405 | case 4: |
406 | s->dsp.filter_complex_low(work_line, in_lines_cur, |
407 | coef_lf[filter], linesize); |
408 | } |
409 | |
410 | /* get high vertical frequencies from adjacent fields */ |
411 | for (j = 0; j < n_coef_hf[filter]; j++) { |
412 | y_in = (y_out + 1) + (j * 2) - n_coef_hf[filter]; |
413 | |
414 | while (y_in < 0) |
415 | y_in += 2; |
416 | while (y_in >= height) |
417 | y_in -= 2; |
418 | |
419 | in_lines_cur[j] = cur_data + (y_in * cur_line_stride); |
420 | in_lines_adj[j] = adj_data + (y_in * adj_line_stride); |
421 | } |
422 | |
423 | work_line = s->work_line[jobnr]; |
424 | switch (n_coef_hf[filter]) { |
425 | case 3: |
426 | s->dsp.filter_simple_high(work_line, in_lines_cur, in_lines_adj, |
427 | coef_hf[filter], linesize); |
428 | break; |
429 | case 5: |
430 | s->dsp.filter_complex_high(work_line, in_lines_cur, in_lines_adj, |
431 | coef_hf[filter], linesize); |
432 | } |
433 | |
434 | /* save scaled result to the output frame, scaling down by 256 * 128 */ |
435 | work_pixel = s->work_line[jobnr]; |
436 | out_pixel = out_line; |
437 | |
438 | s->dsp.filter_scale(out_pixel, work_pixel, linesize, max); |
439 | |
440 | /* move on to next line */ |
441 | y_out += 2; |
442 | out_line += dst_line_stride * 2; |
443 | } |
444 | |
445 | return 0; |
446 | } |
447 | |
448 | static int filter(AVFilterContext *ctx, int is_second) |
449 | { |
450 | W3FDIFContext *s = ctx->priv; |
451 | AVFilterLink *outlink = ctx->outputs[0]; |
452 | AVFrame *out, *adj; |
453 | ThreadData td; |
454 | int plane; |
455 | |
456 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
457 | if (!out) |
458 | return AVERROR(ENOMEM); |
459 | av_frame_copy_props(out, s->cur); |
460 | out->interlaced_frame = 0; |
461 | |
462 | if (!is_second) { |
463 | if (out->pts != AV_NOPTS_VALUE) |
464 | out->pts *= 2; |
465 | } else { |
466 | int64_t cur_pts = s->cur->pts; |
467 | int64_t next_pts = s->next->pts; |
468 | |
469 | if (next_pts != AV_NOPTS_VALUE && cur_pts != AV_NOPTS_VALUE) { |
470 | out->pts = cur_pts + next_pts; |
471 | } else { |
472 | out->pts = AV_NOPTS_VALUE; |
473 | } |
474 | } |
475 | |
476 | adj = s->field ? s->next : s->prev; |
477 | td.out = out; td.cur = s->cur; td.adj = adj; |
478 | for (plane = 0; plane < s->nb_planes; plane++) { |
479 | td.plane = plane; |
480 | ctx->internal->execute(ctx, deinterlace_slice, &td, NULL, FFMIN(s->planeheight[plane], s->nb_threads)); |
481 | } |
482 | |
483 | s->field = !s->field; |
484 | |
485 | return ff_filter_frame(outlink, out); |
486 | } |
487 | |
488 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
489 | { |
490 | AVFilterContext *ctx = inlink->dst; |
491 | W3FDIFContext *s = ctx->priv; |
492 | int ret; |
493 | |
494 | av_frame_free(&s->prev); |
495 | s->prev = s->cur; |
496 | s->cur = s->next; |
497 | s->next = frame; |
498 | |
499 | if (!s->cur) { |
500 | s->cur = av_frame_clone(s->next); |
501 | if (!s->cur) |
502 | return AVERROR(ENOMEM); |
503 | } |
504 | |
505 | if ((s->deint && !s->cur->interlaced_frame) || ctx->is_disabled) { |
506 | AVFrame *out = av_frame_clone(s->cur); |
507 | if (!out) |
508 | return AVERROR(ENOMEM); |
509 | |
510 | av_frame_free(&s->prev); |
511 | if (out->pts != AV_NOPTS_VALUE) |
512 | out->pts *= 2; |
513 | return ff_filter_frame(ctx->outputs[0], out); |
514 | } |
515 | |
516 | if (!s->prev) |
517 | return 0; |
518 | |
519 | ret = filter(ctx, 0); |
520 | if (ret < 0) |
521 | return ret; |
522 | |
523 | return filter(ctx, 1); |
524 | } |
525 | |
526 | static int request_frame(AVFilterLink *outlink) |
527 | { |
528 | AVFilterContext *ctx = outlink->src; |
529 | W3FDIFContext *s = ctx->priv; |
530 | int ret; |
531 | |
532 | if (s->eof) |
533 | return AVERROR_EOF; |
534 | |
535 | ret = ff_request_frame(ctx->inputs[0]); |
536 | |
537 | if (ret == AVERROR_EOF && s->cur) { |
538 | AVFrame *next = av_frame_clone(s->next); |
539 | if (!next) |
540 | return AVERROR(ENOMEM); |
541 | next->pts = s->next->pts * 2 - s->cur->pts; |
542 | filter_frame(ctx->inputs[0], next); |
543 | s->eof = 1; |
544 | } else if (ret < 0) { |
545 | return ret; |
546 | } |
547 | |
548 | return 0; |
549 | } |
550 | |
551 | static av_cold void uninit(AVFilterContext *ctx) |
552 | { |
553 | W3FDIFContext *s = ctx->priv; |
554 | int i; |
555 | |
556 | av_frame_free(&s->prev); |
557 | av_frame_free(&s->cur ); |
558 | av_frame_free(&s->next); |
559 | |
560 | for (i = 0; i < s->nb_threads; i++) |
561 | av_freep(&s->work_line[i]); |
562 | |
563 | av_freep(&s->work_line); |
564 | } |
565 | |
566 | static const AVFilterPad w3fdif_inputs[] = { |
567 | { |
568 | .name = "default", |
569 | .type = AVMEDIA_TYPE_VIDEO, |
570 | .filter_frame = filter_frame, |
571 | .config_props = config_input, |
572 | }, |
573 | { NULL } |
574 | }; |
575 | |
576 | static const AVFilterPad w3fdif_outputs[] = { |
577 | { |
578 | .name = "default", |
579 | .type = AVMEDIA_TYPE_VIDEO, |
580 | .config_props = config_output, |
581 | .request_frame = request_frame, |
582 | }, |
583 | { NULL } |
584 | }; |
585 | |
586 | AVFilter ff_vf_w3fdif = { |
587 | .name = "w3fdif", |
588 | .description = NULL_IF_CONFIG_SMALL("Apply Martin Weston three field deinterlace."), |
589 | .priv_size = sizeof(W3FDIFContext), |
590 | .priv_class = &w3fdif_class, |
591 | .uninit = uninit, |
592 | .query_formats = query_formats, |
593 | .inputs = w3fdif_inputs, |
594 | .outputs = w3fdif_outputs, |
595 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, |
596 | }; |
597 |