FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_fieldmatch.c
Date: 2026-10-29 23:19:09
Exec Total Coverage
Lines: 416 560 74.3%
Functions: 20 21 95.2%
Branches: 384 634 60.6%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2012 Fredrik Mellbin
3 * Copyright (c) 2013 Clément Bœsch
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * Fieldmatching filter, ported from VFM filter (VapourSynth) by Clément.
25 * Fredrik Mellbin is the author of the VIVTC/VFM filter, which is itself a
26 * light clone of the TIVTC/TFM (AviSynth) filter written by Kevin Stone
27 * (tritical), the original author.
28 *
29 * @see http://bengal.missouri.edu/~kes25c/
30 * @see http://www.vapoursynth.com/about/
31 */
32
33 #include <inttypes.h>
34
35 #include "libavutil/avassert.h"
36 #include "libavutil/imgutils.h"
37 #include "libavutil/intreadwrite.h"
38 #include "libavutil/mem.h"
39 #include "libavutil/opt.h"
40 #include "libavutil/timestamp.h"
41 #include "avfilter.h"
42 #include "filters.h"
43 #include "formats.h"
44 #include "video.h"
45
46 #define INPUT_MAIN 0
47 #define INPUT_CLEANSRC 1
48
49 enum fieldmatch_parity {
50 FM_PARITY_AUTO = -1,
51 FM_PARITY_BOTTOM = 0,
52 FM_PARITY_TOP = 1,
53 };
54
55 enum matching_mode {
56 MODE_PC,
57 MODE_PC_N,
58 MODE_PC_U,
59 MODE_PC_N_UB,
60 MODE_PCN,
61 MODE_PCN_UB,
62 NB_MODE
63 };
64
65 enum comb_matching_mode {
66 COMBMATCH_NONE,
67 COMBMATCH_SC,
68 COMBMATCH_FULL,
69 NB_COMBMATCH
70 };
71
72 enum comb_dbg {
73 COMBDBG_NONE,
74 COMBDBG_PCN,
75 COMBDBG_PCNUB,
76 NB_COMBDBG
77 };
78
79 typedef struct FieldMatchContext {
80 const AVClass *class;
81
82 AVFrame *prv, *src, *nxt; ///< main sliding window of 3 frames
83 AVFrame *prv2, *src2, *nxt2; ///< sliding window of the optional second stream
84 int got_frame[2]; ///< frame request flag for each input stream
85 int hsub[2], vsub[2]; ///< chroma subsampling values
86 int bpc; ///< bytes per component
87 uint32_t eof; ///< bitmask for end of stream
88 int64_t lastscdiff;
89 int64_t lastn;
90
91 /* options */
92 int order;
93 int ppsrc;
94 int mode; ///< matching_mode
95 int field;
96 int mchroma;
97 int y0, y1;
98 int64_t scthresh;
99 double scthresh_flt;
100 int combmatch; ///< comb_matching_mode
101 int combdbg;
102 int cthresh;
103 int chroma;
104 int blockx, blocky;
105 int combpel;
106
107 /* misc buffers */
108 uint8_t *map_data[4];
109 int map_linesize[4];
110 uint8_t *cmask_data[4];
111 int cmask_linesize[4];
112 int *c_array;
113 int tpitchy, tpitchuv;
114 uint8_t *tbuffer;
115 } FieldMatchContext;
116
117 #define OFFSET(x) offsetof(FieldMatchContext, x)
118 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
119
120 static const AVOption fieldmatch_options[] = {
121 { "order", "specify the assumed field order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=FM_PARITY_AUTO}, -1, 1, FLAGS, .unit = "order" },
122 { "auto", "auto detect parity", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_AUTO}, INT_MIN, INT_MAX, FLAGS, .unit = "order" },
123 { "bff", "assume bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "order" },
124 { "tff", "assume top field first", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "order" },
125 { "mode", "set the matching mode or strategy to use", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_PC_N}, MODE_PC, NB_MODE-1, FLAGS, .unit = "mode" },
126 { "pc", "2-way match (p/c)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
127 { "pc_n", "2-way match + 3rd match on combed (p/c + u)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_N}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
128 { "pc_u", "2-way match + 3rd match (same order) on combed (p/c + u)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_U}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
129 { "pc_n_ub", "2-way match + 3rd match on combed + 4th/5th matches if still combed (p/c + u + u/b)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_N_UB}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
130 { "pcn", "3-way match (p/c/n)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PCN}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
131 { "pcn_ub", "3-way match + 4th/5th matches on combed (p/c/n + u/b)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PCN_UB}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" },
132 { "ppsrc", "mark main input as a pre-processed input and activate clean source input stream", OFFSET(ppsrc), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
133 { "field", "set the field to match from", OFFSET(field), AV_OPT_TYPE_INT, {.i64=FM_PARITY_AUTO}, -1, 1, FLAGS, .unit = "field" },
134 { "auto", "automatic (same value as 'order')", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_AUTO}, INT_MIN, INT_MAX, FLAGS, .unit = "field" },
135 { "bottom", "bottom field", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "field" },
136 { "top", "top field", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "field" },
137 { "mchroma", "set whether or not chroma is included during the match comparisons", OFFSET(mchroma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
138 { "y0", "define an exclusion band which excludes the lines between y0 and y1 from the field matching decision", OFFSET(y0), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
139 { "y1", "define an exclusion band which excludes the lines between y0 and y1 from the field matching decision", OFFSET(y1), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
140 { "scthresh", "set scene change detection threshold", OFFSET(scthresh_flt), AV_OPT_TYPE_DOUBLE, {.dbl=12}, 0, 100, FLAGS },
141 { "combmatch", "set combmatching mode", OFFSET(combmatch), AV_OPT_TYPE_INT, {.i64=COMBMATCH_SC}, COMBMATCH_NONE, NB_COMBMATCH-1, FLAGS, .unit = "combmatching" },
142 { "none", "disable combmatching", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "combmatching" },
143 { "sc", "enable combmatching only on scene change", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_SC}, INT_MIN, INT_MAX, FLAGS, .unit = "combmatching" },
144 { "full", "enable combmatching all the time", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_FULL}, INT_MIN, INT_MAX, FLAGS, .unit = "combmatching" },
145 { "combdbg", "enable comb debug", OFFSET(combdbg), AV_OPT_TYPE_INT, {.i64=COMBDBG_NONE}, COMBDBG_NONE, NB_COMBDBG-1, FLAGS, .unit = "dbglvl" },
146 { "none", "no forced calculation", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "dbglvl" },
147 { "pcn", "calculate p/c/n", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_PCN}, INT_MIN, INT_MAX, FLAGS, .unit = "dbglvl" },
148 { "pcnub", "calculate p/c/n/u/b", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_PCNUB}, INT_MIN, INT_MAX, FLAGS, .unit = "dbglvl" },
149 { "cthresh", "set the area combing threshold used for combed frame detection", OFFSET(cthresh), AV_OPT_TYPE_INT, {.i64= 9}, -1, 0xff, FLAGS },
150 { "chroma", "set whether or not chroma is considered in the combed frame decision", OFFSET(chroma), AV_OPT_TYPE_BOOL,{.i64= 0}, 0, 1, FLAGS },
151 { "blockx", "set the x-axis size of the window used during combed frame detection", OFFSET(blockx), AV_OPT_TYPE_INT, {.i64=16}, 4, 1<<9, FLAGS },
152 { "blocky", "set the y-axis size of the window used during combed frame detection", OFFSET(blocky), AV_OPT_TYPE_INT, {.i64=16}, 4, 1<<9, FLAGS },
153 { "combpel", "set the number of combed pixels inside any of the blocky by blockx size blocks on the frame for the frame to be detected as combed", OFFSET(combpel), AV_OPT_TYPE_INT, {.i64=80}, 0, INT_MAX, FLAGS },
154 { NULL }
155 };
156
157 AVFILTER_DEFINE_CLASS(fieldmatch);
158
159 1564 static int get_width(const FieldMatchContext *fm, const AVFrame *f, int plane, int input)
160 {
161
2/2
✓ Branch 0 taken 896 times.
✓ Branch 1 taken 668 times.
1564 return plane ? AV_CEIL_RSHIFT(f->width, fm->hsub[input]) : f->width;
162 }
163
164 1564 static int get_height(const FieldMatchContext *fm, const AVFrame *f, int plane, int input)
165 {
166
2/2
✓ Branch 0 taken 896 times.
✓ Branch 1 taken 668 times.
1564 return plane ? AV_CEIL_RSHIFT(f->height, fm->vsub[input]) : f->height;
167 }
168
169 135 static int64_t luma_abs_diff(const AVFrame *f1, const AVFrame *f2)
170 {
171 int x, y;
172 135 const uint8_t *srcp1 = f1->data[0];
173 135 const uint8_t *srcp2 = f2->data[0];
174 135 const int src1_linesize = f1->linesize[0];
175 135 const int src2_linesize = f2->linesize[0];
176 135 const int width = f1->width;
177 135 const int height = f1->height;
178 135 int64_t acc = 0;
179
180
2/2
✓ Branch 0 taken 38880 times.
✓ Branch 1 taken 135 times.
39015 for (y = 0; y < height; y++) {
181
2/2
✓ Branch 0 taken 13685760 times.
✓ Branch 1 taken 38880 times.
13724640 for (x = 0; x < width; x++)
182 13685760 acc += abs(srcp1[x] - srcp2[x]);
183 38880 srcp1 += src1_linesize;
184 38880 srcp2 += src2_linesize;
185 }
186 135 return acc;
187 }
188
189 610 static void fill_buf(uint8_t *data, int w, int h, int linesize, uint8_t v)
190 {
191 int y;
192
193
2/2
✓ Branch 0 taken 156960 times.
✓ Branch 1 taken 610 times.
157570 for (y = 0; y < h; y++) {
194 156960 memset(data, v, w);
195 156960 data += linesize;
196 }
197 610 }
198
199 ✗ static av_always_inline void mark_combed_chroma(const FieldMatchContext *fm, const AVFrame *src,
200 int hsub, int vsub)
201 {
202 ✗ const uint8_t *cmkpU = fm->cmask_data[1];
203 ✗ const uint8_t *cmkpV = fm->cmask_data[2];
204 ✗ const int width = AV_CEIL_RSHIFT(src->width, hsub);
205 ✗ const int height = AV_CEIL_RSHIFT(src->height, vsub);
206 ✗ const ptrdiff_t cmk_linesize = fm->cmask_linesize[0];
207 ✗ const ptrdiff_t cmk_linesizeUV = fm->cmask_linesize[2];
208
209 ✗ for (int y = 1; y < height - 1; y++) {
210 ✗ uint8_t *cmkp_row = fm->cmask_data[0] + ((y << vsub) - (vsub ? y & 1 : 0)) * cmk_linesize;
211 ✗ cmkpV += cmk_linesizeUV;
212 ✗ cmkpU += cmk_linesizeUV;
213 ✗ for (int x = 1; x < width - 1; x++) {
214 #define HAS_FF_AROUND(p, lz) (p[(x)-1 - (lz)] == 0xff || p[(x) - (lz)] == 0xff || p[(x)+1 - (lz)] == 0xff || \
215 p[(x)-1 ] == 0xff || p[(x)+1 ] == 0xff || \
216 p[(x)-1 + (lz)] == 0xff || p[(x) + (lz)] == 0xff || p[(x)+1 + (lz)] == 0xff)
217 ✗ if ((cmkpV[x] == 0xff && HAS_FF_AROUND(cmkpV, cmk_linesizeUV)) ||
218 ✗ (cmkpU[x] == 0xff && HAS_FF_AROUND(cmkpU, cmk_linesizeUV))) {
219 ✗ uint8_t *cmkp = cmkp_row + (x << hsub);
220 ✗ for (int i = 0; i < (1 << vsub) + !!vsub; i++, cmkp += cmk_linesize) {
221 ✗ if (hsub == 2)
222 ✗ AV_WN32A(cmkp, 0xffffffff);
223 ✗ else if (hsub)
224 ✗ AV_WN16A(cmkp, 0xffff);
225 else
226 ✗ *cmkp = 0xff;
227 }
228 }
229 }
230 }
231 ✗ }
232
233 220 static int calc_combed_score(const FieldMatchContext *fm, const AVFrame *src)
234 {
235 220 int x, y, plane, max_v = 0;
236 220 const int cthresh = fm->cthresh;
237 220 const int cthresh6 = cthresh * 6;
238
239
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 440 times.
✓ Branch 2 taken 220 times.
✓ Branch 3 taken 220 times.
440 for (plane = 0; plane < (fm->chroma ? 3 : 1); plane++) {
240 220 const uint8_t *srcp = src->data[plane];
241 220 const int src_linesize = src->linesize[plane];
242 220 const int width = get_width (fm, src, plane, INPUT_MAIN);
243 220 const int height = get_height(fm, src, plane, INPUT_MAIN);
244 220 uint8_t *cmkp = fm->cmask_data[plane];
245 220 const int cmk_linesize = fm->cmask_linesize[plane];
246
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 220 times.
220 if (cthresh < 0) {
248 ✗ fill_buf(cmkp, width, height, cmk_linesize, 0xff);
249 ✗ continue;
250 }
251 220 fill_buf(cmkp, width, height, cmk_linesize, 0);
252
253 /* [1 -3 4 -3 1] vertical filter */
254 #define FILTER(xm2, xm1, xp1, xp2) \
255 abs( 4 * srcp[x] \
256 -3 * (srcp[x + (xm1)*src_linesize] + srcp[x + (xp1)*src_linesize]) \
257 + (srcp[x + (xm2)*src_linesize] + srcp[x + (xp2)*src_linesize])) > cthresh6
258
259 /* first line */
260
2/2
✓ Branch 0 taken 77440 times.
✓ Branch 1 taken 220 times.
77660 for (x = 0; x < width; x++) {
261 77440 const int s1 = abs(srcp[x] - srcp[x + src_linesize]);
262
4/4
✓ Branch 0 taken 41535 times.
✓ Branch 1 taken 35905 times.
✓ Branch 2 taken 40180 times.
✓ Branch 3 taken 1355 times.
77440 if (s1 > cthresh && FILTER(2, 1, 1, 2))
263 40180 cmkp[x] = 0xff;
264 }
265 220 srcp += src_linesize;
266 220 cmkp += cmk_linesize;
267
268 /* second line */
269
2/2
✓ Branch 0 taken 77440 times.
✓ Branch 1 taken 220 times.
77660 for (x = 0; x < width; x++) {
270 77440 const int s1 = abs(srcp[x] - srcp[x - src_linesize]);
271 77440 const int s2 = abs(srcp[x] - srcp[x + src_linesize]);
272
6/6
✓ Branch 0 taken 41535 times.
✓ Branch 1 taken 35905 times.
✓ Branch 2 taken 37459 times.
✓ Branch 3 taken 4076 times.
✓ Branch 4 taken 37099 times.
✓ Branch 5 taken 360 times.
77440 if (s1 > cthresh && s2 > cthresh && FILTER(2, -1, 1, 2))
273 37099 cmkp[x] = 0xff;
274 }
275 220 srcp += src_linesize;
276 220 cmkp += cmk_linesize;
277
278 /* all lines minus first two and last two */
279
2/2
✓ Branch 0 taken 62480 times.
✓ Branch 1 taken 220 times.
62700 for (y = 2; y < height-2; y++) {
280
2/2
✓ Branch 0 taken 21992960 times.
✓ Branch 1 taken 62480 times.
22055440 for (x = 0; x < width; x++) {
281 21992960 const int s1 = abs(srcp[x] - srcp[x - src_linesize]);
282 21992960 const int s2 = abs(srcp[x] - srcp[x + src_linesize]);
283
6/6
✓ Branch 0 taken 12008290 times.
✓ Branch 1 taken 9984670 times.
✓ Branch 2 taken 10342405 times.
✓ Branch 3 taken 1665885 times.
✓ Branch 4 taken 9768879 times.
✓ Branch 5 taken 573526 times.
21992960 if (s1 > cthresh && s2 > cthresh && FILTER(-2, -1, 1, 2))
284 9768879 cmkp[x] = 0xff;
285 }
286 62480 srcp += src_linesize;
287 62480 cmkp += cmk_linesize;
288 }
289
290 /* before-last line */
291
2/2
✓ Branch 0 taken 77440 times.
✓ Branch 1 taken 220 times.
77660 for (x = 0; x < width; x++) {
292 77440 const int s1 = abs(srcp[x] - srcp[x - src_linesize]);
293 77440 const int s2 = abs(srcp[x] - srcp[x + src_linesize]);
294
6/6
✓ Branch 0 taken 41719 times.
✓ Branch 1 taken 35721 times.
✓ Branch 2 taken 33941 times.
✓ Branch 3 taken 7778 times.
✓ Branch 4 taken 30781 times.
✓ Branch 5 taken 3160 times.
77440 if (s1 > cthresh && s2 > cthresh && FILTER(-2, -1, 1, -2))
295 30781 cmkp[x] = 0xff;
296 }
297 220 srcp += src_linesize;
298 220 cmkp += cmk_linesize;
299
300 /* last line */
301
2/2
✓ Branch 0 taken 77440 times.
✓ Branch 1 taken 220 times.
77660 for (x = 0; x < width; x++) {
302 77440 const int s1 = abs(srcp[x] - srcp[x - src_linesize]);
303
4/4
✓ Branch 0 taken 40462 times.
✓ Branch 1 taken 36978 times.
✓ Branch 2 taken 35380 times.
✓ Branch 3 taken 5082 times.
77440 if (s1 > cthresh && FILTER(-2, -1, -1, -2))
304 35380 cmkp[x] = 0xff;
305 }
306 }
307
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 220 times.
220 if (fm->chroma) {
309 ✗ const int hsub = fm->hsub[INPUT_MAIN];
310 ✗ const int vsub = fm->vsub[INPUT_MAIN];
311 ✗ if (hsub == 0 && vsub == 0) mark_combed_chroma(fm, src, 0, 0);
312 ✗ else if (hsub == 1 && vsub == 0) mark_combed_chroma(fm, src, 1, 0);
313 ✗ else if (hsub == 1 && vsub == 1) mark_combed_chroma(fm, src, 1, 1);
314 ✗ else if (hsub == 2 && vsub == 0) mark_combed_chroma(fm, src, 2, 0);
315 ✗ else mark_combed_chroma(fm, src, hsub, vsub);
316 }
317
318 {
319 220 const int blockx = fm->blockx;
320 220 const int blocky = fm->blocky;
321 220 const int xhalf = blockx/2;
322 220 const int yhalf = blocky/2;
323 220 const int cmk_linesize = fm->cmask_linesize[0];
324 220 const uint8_t *cmkp = fm->cmask_data[0] + cmk_linesize;
325 220 const int width = src->width;
326 220 const int height = src->height;
327 220 const int xblocks = ((width+xhalf)/blockx) + 1;
328 220 const int xblocks4 = xblocks<<2;
329 220 const int yblocks = ((height+yhalf)/blocky) + 1;
330 220 int *c_array = fm->c_array;
331 220 const int arraysize = (xblocks*yblocks)<<2;
332 220 int heighta = (height/(blocky/2))*(blocky/2);
333 220 const int widtha = (width /(blockx/2))*(blockx/2);
334
1/2
✓ Branch 0 taken 220 times.
✗ Branch 1 not taken.
220 if (heighta == height)
335 220 heighta = height - yhalf;
336 220 memset(c_array, 0, arraysize * sizeof(*c_array));
337
338 #define C_ARRAY_ADD(v) do { \
339 const int box1 = (x / blockx) * 4; \
340 const int box2 = ((x + xhalf) / blockx) * 4; \
341 c_array[temp1 + box1 ] += v; \
342 c_array[temp1 + box2 + 1] += v; \
343 c_array[temp2 + box1 + 2] += v; \
344 c_array[temp2 + box2 + 3] += v; \
345 } while (0)
346
347 #define VERTICAL_HALF(y_start, y_end) do { \
348 for (y = y_start; y < y_end; y++) { \
349 const int temp1 = (y / blocky) * xblocks4; \
350 const int temp2 = ((y + yhalf) / blocky) * xblocks4; \
351 for (x = 0; x < width; x++) \
352 if (cmkp[x - cmk_linesize] == 0xff && \
353 cmkp[x ] == 0xff && \
354 cmkp[x + cmk_linesize] == 0xff) \
355 C_ARRAY_ADD(1); \
356 cmkp += cmk_linesize; \
357 } \
358 } while (0)
359
360
10/10
✓ Branch 0 taken 261066 times.
✓ Branch 1 taken 281014 times.
✓ Branch 2 taken 252355 times.
✓ Branch 3 taken 8711 times.
✓ Branch 4 taken 247367 times.
✓ Branch 5 taken 4988 times.
✓ Branch 6 taken 542080 times.
✓ Branch 7 taken 1540 times.
✓ Branch 8 taken 1540 times.
✓ Branch 9 taken 220 times.
543840 VERTICAL_HALF(1, yhalf);
361
362
2/2
✓ Branch 0 taken 7480 times.
✓ Branch 1 taken 220 times.
7700 for (y = yhalf; y < heighta; y += yhalf) {
363 7480 const int temp1 = (y / blocky) * xblocks4;
364 7480 const int temp2 = ((y + yhalf) / blocky) * xblocks4;
365
366
2/2
✓ Branch 0 taken 329120 times.
✓ Branch 1 taken 7480 times.
336600 for (x = 0; x < widtha; x += xhalf) {
367 329120 const uint8_t *cmkp_tmp = cmkp + x;
368 329120 int u, v, sum = 0;
369
2/2
✓ Branch 0 taken 2632960 times.
✓ Branch 1 taken 329120 times.
2962080 for (u = 0; u < yhalf; u++) {
370
2/2
✓ Branch 0 taken 21063680 times.
✓ Branch 1 taken 2632960 times.
23696640 for (v = 0; v < xhalf; v++)
371
2/2
✓ Branch 0 taken 9365808 times.
✓ Branch 1 taken 11697872 times.
21063680 if (cmkp_tmp[v - cmk_linesize] == 0xff &&
372
2/2
✓ Branch 0 taken 8453820 times.
✓ Branch 1 taken 911988 times.
9365808 cmkp_tmp[v ] == 0xff &&
373
2/2
✓ Branch 0 taken 7819534 times.
✓ Branch 1 taken 634286 times.
8453820 cmkp_tmp[v + cmk_linesize] == 0xff)
374 7819534 sum++;
375 2632960 cmkp_tmp += cmk_linesize;
376 }
377
2/2
✓ Branch 0 taken 200837 times.
✓ Branch 1 taken 128283 times.
329120 if (sum)
378 200837 C_ARRAY_ADD(sum);
379 }
380
381
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7480 times.
7480 for (x = widtha; x < width; x++) {
382 ✗ const uint8_t *cmkp_tmp = cmkp + x;
383 ✗ int u, sum = 0;
384 ✗ for (u = 0; u < yhalf; u++) {
385 ✗ if (cmkp_tmp[-cmk_linesize] == 0xff &&
386 ✗ cmkp_tmp[ 0] == 0xff &&
387 ✗ cmkp_tmp[ cmk_linesize] == 0xff)
388 ✗ sum++;
389 ✗ cmkp_tmp += cmk_linesize;
390 }
391 ✗ if (sum)
392 ✗ C_ARRAY_ADD(sum);
393 }
394
395 7480 cmkp += cmk_linesize * yhalf;
396 }
397
398
10/10
✓ Branch 0 taken 219284 times.
✓ Branch 1 taken 322796 times.
✓ Branch 2 taken 188226 times.
✓ Branch 3 taken 31058 times.
✓ Branch 4 taken 170775 times.
✓ Branch 5 taken 17451 times.
✓ Branch 6 taken 542080 times.
✓ Branch 7 taken 1540 times.
✓ Branch 8 taken 1540 times.
✓ Branch 9 taken 220 times.
543840 VERTICAL_HALF(heighta, height - 1);
399
400
2/2
✓ Branch 0 taken 384560 times.
✓ Branch 1 taken 220 times.
384780 for (x = 0; x < arraysize; x++)
401
2/2
✓ Branch 0 taken 971 times.
✓ Branch 1 taken 383589 times.
384560 if (c_array[x] > max_v)
402 971 max_v = c_array[x];
403 }
404 220 return max_v;
405 }
406
407 // the secret is that tbuffer is an interlaced, offset subset of all the lines
408 390 static void build_abs_diff_mask(const uint8_t *prvp, int prv_linesize,
409 const uint8_t *nxtp, int nxt_linesize,
410 uint8_t *tbuffer, int tbuf_linesize,
411 int width, int height)
412 {
413 int y, x;
414
415 390 prvp -= prv_linesize;
416 390 nxtp -= nxt_linesize;
417
2/2
✓ Branch 0 taken 46800 times.
✓ Branch 1 taken 390 times.
47190 for (y = 0; y < height; y++) {
418
2/2
✓ Branch 0 taken 12025728 times.
✓ Branch 1 taken 46800 times.
12072528 for (x = 0; x < width; x++)
419 12025728 tbuffer[x] = FFABS(prvp[x] - nxtp[x]);
420 46800 prvp += prv_linesize;
421 46800 nxtp += nxt_linesize;
422 46800 tbuffer += tbuf_linesize;
423 }
424 390 }
425
426 /**
427 * Build a map over which pixels differ a lot/a little
428 */
429 390 static void build_diff_map(FieldMatchContext *fm,
430 const uint8_t *prvp, int prv_linesize,
431 const uint8_t *nxtp, int nxt_linesize,
432 uint8_t *dstp, int dst_linesize, int height,
433 int width, int plane)
434 {
435 int x, y, u, diff, count;
436
2/2
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 130 times.
390 int tpitch = plane ? fm->tpitchuv : fm->tpitchy;
437 390 const uint8_t *dp = fm->tbuffer + tpitch;
438
439 390 build_abs_diff_mask(prvp, prv_linesize, nxtp, nxt_linesize,
440 fm->tbuffer, tpitch, width, height>>1);
441
442
2/2
✓ Branch 0 taken 46020 times.
✓ Branch 1 taken 390 times.
46410 for (y = 2; y < height - 2; y += 2) {
443
2/2
✓ Branch 0 taken 11750648 times.
✓ Branch 1 taken 46020 times.
11796668 for (x = 1; x < width - 1; x++) {
444 11750648 diff = dp[x];
445
2/2
✓ Branch 0 taken 8730010 times.
✓ Branch 1 taken 3020638 times.
11750648 if (diff > 3) {
446
4/4
✓ Branch 0 taken 17951849 times.
✓ Branch 1 taken 29955 times.
✓ Branch 2 taken 9251794 times.
✓ Branch 3 taken 8700055 times.
17981804 for (count = 0, u = x-1; u < x+2 && count < 2; u++) {
447 9251794 count += dp[u-tpitch] > 3;
448 9251794 count += dp[u ] > 3;
449 9251794 count += dp[u+tpitch] > 3;
450 }
451
2/2
✓ Branch 0 taken 8722083 times.
✓ Branch 1 taken 7927 times.
8730010 if (count > 1) {
452 8722083 dstp[x] = 1;
453
2/2
✓ Branch 0 taken 5460375 times.
✓ Branch 1 taken 3261708 times.
8722083 if (diff > 19) {
454 5460375 int upper = 0, lower = 0;
455
4/4
✓ Branch 0 taken 16381125 times.
✓ Branch 1 taken 1828737 times.
✓ Branch 2 taken 12749487 times.
✓ Branch 3 taken 3631638 times.
18209862 for (count = 0, u = x-1; u < x+2 && count < 6; u++) {
456
2/2
✓ Branch 0 taken 9792913 times.
✓ Branch 1 taken 2956574 times.
12749487 if (dp[u-tpitch] > 19) { count++; upper = 1; }
457
2/2
✓ Branch 0 taken 11577399 times.
✓ Branch 1 taken 1172088 times.
12749487 if (dp[u ] > 19) count++;
458
2/2
✓ Branch 0 taken 9811153 times.
✓ Branch 1 taken 2938334 times.
12749487 if (dp[u+tpitch] > 19) { count++; lower = 1; }
459 }
460
2/2
✓ Branch 0 taken 5022758 times.
✓ Branch 1 taken 437617 times.
5460375 if (count > 3) {
461
4/4
✓ Branch 0 taken 4798461 times.
✓ Branch 1 taken 224297 times.
✓ Branch 2 taken 4554450 times.
✓ Branch 3 taken 244011 times.
5022758 if (upper && lower) {
462 4554450 dstp[x] |= 1<<1;
463 } else {
464 468308 int upper2 = 0, lower2 = 0;
465
4/4
✓ Branch 0 taken 4613034 times.
✓ Branch 1 taken 46960 times.
✓ Branch 2 taken 4191686 times.
✓ Branch 3 taken 468308 times.
4659994 for (u = FFMAX(x-4,0); u < FFMIN(x+5,width); u++) {
466
4/4
✓ Branch 0 taken 4164662 times.
✓ Branch 1 taken 27024 times.
✓ Branch 2 taken 1979996 times.
✓ Branch 3 taken 2184666 times.
4191686 if (y != 2 && dp[u-2*tpitch] > 19) upper2 = 1;
467
2/2
✓ Branch 0 taken 1848510 times.
✓ Branch 1 taken 2343176 times.
4191686 if ( dp[u- tpitch] > 19) upper = 1;
468
2/2
✓ Branch 0 taken 1813784 times.
✓ Branch 1 taken 2377902 times.
4191686 if ( dp[u+ tpitch] > 19) lower = 1;
469
4/4
✓ Branch 0 taken 4154009 times.
✓ Branch 1 taken 37677 times.
✓ Branch 2 taken 1981974 times.
✓ Branch 3 taken 2172035 times.
4191686 if (y != height-4 && dp[u+2*tpitch] > 19) lower2 = 1;
470 }
471
8/8
✓ Branch 0 taken 410829 times.
✓ Branch 1 taken 57479 times.
✓ Branch 2 taken 71226 times.
✓ Branch 3 taken 339603 times.
✓ Branch 4 taken 6440 times.
✓ Branch 5 taken 64786 times.
✓ Branch 6 taken 57479 times.
✓ Branch 7 taken 6440 times.
468308 if ((upper && (lower || upper2)) ||
472
3/4
✓ Branch 0 taken 57479 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 51083 times.
✓ Branch 3 taken 6396 times.
57479 (lower && (upper || lower2)))
473 455472 dstp[x] |= 1<<1;
474
2/2
✓ Branch 0 taken 9145 times.
✓ Branch 1 taken 3691 times.
12836 else if (count > 5)
475 9145 dstp[x] |= 1<<2;
476 }
477 }
478 }
479 }
480 }
481 }
482 46020 dp += tpitch;
483 46020 dstp += dst_linesize;
484 }
485 390 }
486
487 enum { mP, mC, mN, mB, mU };
488
489 780 static int get_field_base(int match, int field)
490 {
491
1/2
✓ Branch 0 taken 780 times.
✗ Branch 1 not taken.
780 return match < 3 ? 2 - field : 1 + field;
492 }
493
494 780 static AVFrame *select_frame(FieldMatchContext *fm, int match)
495 {
496
3/4
✓ Branch 0 taken 390 times.
✓ Branch 1 taken 390 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 390 times.
780 if (match == mP || match == mB) return fm->prv;
497
2/4
✓ Branch 0 taken 390 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 390 times.
390 else if (match == mN || match == mU) return fm->nxt;
498 390 else /* match == mC */ return fm->src;
499 }
500
501 130 static int compare_fields(FieldMatchContext *fm, int match1, int match2, int field)
502 {
503 int plane, ret;
504 130 uint64_t accumPc = 0, accumPm = 0, accumPml = 0;
505 130 uint64_t accumNc = 0, accumNm = 0, accumNml = 0;
506 int norm1, norm2, mtn1, mtn2;
507 float c1, c2, mr;
508 130 const AVFrame *src = fm->src;
509
510
3/4
✓ Branch 0 taken 520 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 390 times.
✓ Branch 3 taken 130 times.
520 for (plane = 0; plane < (fm->mchroma ? 3 : 1); plane++) {
511 int x, y, temp1, temp2, fbase, stopy;
512 const AVFrame *prev, *next;
513 390 uint8_t *mapp = fm->map_data[plane];
514 390 int map_linesize = fm->map_linesize[plane];
515 390 const uint8_t *srcp = src->data[plane];
516 390 const int src_linesize = src->linesize[plane];
517 390 const int srcf_linesize = src_linesize << 1;
518 int prv_linesize, nxt_linesize;
519 int prvf_linesize, nxtf_linesize;
520 390 const int width = get_width (fm, src, plane, INPUT_MAIN);
521 390 const int height = get_height(fm, src, plane, INPUT_MAIN);
522
2/2
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 130 times.
390 const int y0a = fm->y0 >> (plane ? fm->vsub[INPUT_MAIN] : 0);
523
2/2
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 130 times.
390 const int y1a = fm->y1 >> (plane ? fm->vsub[INPUT_MAIN] : 0);
524
2/2
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 130 times.
390 const int startx = (plane == 0 ? 8 : 8 >> fm->hsub[INPUT_MAIN]);
525 390 const int stopx = width - startx;
526 const uint8_t *srcpf, *srcf, *srcnf;
527 const uint8_t *prvpf, *prvnf, *nxtpf, *nxtnf;
528
529 390 fill_buf(mapp, width, height, map_linesize, 0);
530
531 /* match1 */
532 390 fbase = get_field_base(match1, field);
533 390 srcf = srcp + (fbase + 1) * src_linesize;
534 390 stopy = height - 1 - fbase;
535 390 srcpf = srcf - srcf_linesize;
536 390 srcnf = srcf + srcf_linesize;
537 390 mapp = mapp + fbase * map_linesize;
538 390 prev = select_frame(fm, match1);
539 390 prv_linesize = prev->linesize[plane];
540 390 prvf_linesize = prv_linesize << 1;
541 390 prvpf = prev->data[plane] + fbase * prv_linesize; // previous frame, previous field
542 390 prvnf = prvpf + prvf_linesize; // previous frame, next field
543
544 /* match2 */
545 390 fbase = get_field_base(match2, field);
546 390 next = select_frame(fm, match2);
547 390 nxt_linesize = next->linesize[plane];
548 390 nxtf_linesize = nxt_linesize << 1;
549 390 nxtpf = next->data[plane] + fbase * nxt_linesize; // next frame, previous field
550 390 nxtnf = nxtpf + nxtf_linesize; // next frame, next field
551
552 390 map_linesize <<= 1;
553
3/8
✗ Branch 0 not taken.
✓ Branch 1 taken 390 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 390 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 390 times.
390 if ((match1 >= 3 && field == 1) || (match1 < 3 && field != 1))
554 ✗ build_diff_map(fm, prvpf, prvf_linesize, nxtpf, nxtf_linesize,
555 mapp, map_linesize, height, width, plane);
556 else
557 390 build_diff_map(fm, prvnf, prvf_linesize, nxtnf, nxtf_linesize,
558 mapp + map_linesize, map_linesize, height, width, plane);
559
560
2/2
✓ Branch 0 taken 46020 times.
✓ Branch 1 taken 390 times.
46410 for (y = 2; y < stopy; y += 2) {
561
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 46020 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
46020 if (y0a == y1a || y < y0a || y > y1a) {
562
2/2
✓ Branch 0 taken 11304384 times.
✓ Branch 1 taken 46020 times.
11350404 for (x = startx; x < stopx; x++) {
563
4/4
✓ Branch 0 taken 2987915 times.
✓ Branch 1 taken 8316469 times.
✓ Branch 2 taken 811027 times.
✓ Branch 3 taken 2176888 times.
11304384 if (mapp[x] > 0 || mapp[x + map_linesize] > 0) {
564 9127496 temp1 = srcpf[x] + (srcf[x] << 2) + srcnf[x]; // [1 4 1]
565
566 9127496 temp2 = abs(3 * (prvpf[x] + prvnf[x]) - temp1);
567
5/6
✓ Branch 0 taken 3925691 times.
✓ Branch 1 taken 5201805 times.
✓ Branch 2 taken 195901 times.
✓ Branch 3 taken 3729790 times.
✓ Branch 4 taken 195901 times.
✗ Branch 5 not taken.
9127496 if (temp2 > 23 && ((mapp[x]&1) || (mapp[x + map_linesize]&1)))
568 3925691 accumPc += temp2;
569
2/2
✓ Branch 0 taken 3357735 times.
✓ Branch 1 taken 5769761 times.
9127496 if (temp2 > 42) {
570
4/4
✓ Branch 0 taken 855982 times.
✓ Branch 1 taken 2501753 times.
✓ Branch 2 taken 191399 times.
✓ Branch 3 taken 664583 times.
3357735 if ((mapp[x]&2) || (mapp[x + map_linesize]&2))
571 2693152 accumPm += temp2;
572
4/4
✓ Branch 0 taken 3356596 times.
✓ Branch 1 taken 1139 times.
✓ Branch 2 taken 388 times.
✓ Branch 3 taken 3356208 times.
3357735 if ((mapp[x]&4) || (mapp[x + map_linesize]&4))
573 1527 accumPml += temp2;
574 }
575
576 9127496 temp2 = abs(3 * (nxtpf[x] + nxtnf[x]) - temp1);
577
5/6
✓ Branch 0 taken 6710599 times.
✓ Branch 1 taken 2416897 times.
✓ Branch 2 taken 601052 times.
✓ Branch 3 taken 6109547 times.
✓ Branch 4 taken 601052 times.
✗ Branch 5 not taken.
9127496 if (temp2 > 23 && ((mapp[x]&1) || (mapp[x + map_linesize]&1)))
578 6710599 accumNc += temp2;
579
2/2
✓ Branch 0 taken 5902128 times.
✓ Branch 1 taken 3225368 times.
9127496 if (temp2 > 42) {
580
4/4
✓ Branch 0 taken 2737945 times.
✓ Branch 1 taken 3164183 times.
✓ Branch 2 taken 494968 times.
✓ Branch 3 taken 2242977 times.
5902128 if ((mapp[x]&2) || (mapp[x + map_linesize]&2))
581 3659151 accumNm += temp2;
582
4/4
✓ Branch 0 taken 5894779 times.
✓ Branch 1 taken 7349 times.
✓ Branch 2 taken 3278 times.
✓ Branch 3 taken 5891501 times.
5902128 if ((mapp[x]&4) || (mapp[x + map_linesize]&4))
583 10627 accumNml += temp2;
584 }
585 }
586 }
587 }
588 46020 prvpf += prvf_linesize;
589 46020 prvnf += prvf_linesize;
590 46020 srcpf += srcf_linesize;
591 46020 srcf += srcf_linesize;
592 46020 srcnf += srcf_linesize;
593 46020 nxtpf += nxtf_linesize;
594 46020 nxtnf += nxtf_linesize;
595 46020 mapp += map_linesize;
596 }
597 }
598
599
6/8
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 119 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 10 times.
130 if (accumPm < 500 && accumNm < 500 && (accumPml >= 500 || accumNml >= 500) &&
600 ✗ FFMAX(accumPml,accumNml) > 3*FFMIN(accumPml,accumNml)) {
601 ✗ accumPm = accumPml;
602 ✗ accumNm = accumNml;
603 }
604
605 130 norm1 = (int)((accumPc / 6.0f) + 0.5f);
606 130 norm2 = (int)((accumNc / 6.0f) + 0.5f);
607 130 mtn1 = (int)((accumPm / 6.0f) + 0.5f);
608 130 mtn2 = (int)((accumNm / 6.0f) + 0.5f);
609
6/6
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 75 times.
130 c1 = ((float)FFMAX(norm1,norm2)) / ((float)FFMAX(FFMIN(norm1,norm2),1));
610
6/6
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 50 times.
✓ Branch 5 taken 70 times.
130 c2 = ((float)FFMAX(mtn1, mtn2)) / ((float)FFMAX(FFMIN(mtn1, mtn2), 1));
611
6/6
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 75 times.
130 mr = ((float)FFMAX(mtn1, mtn2)) / ((float)FFMAX(FFMAX(norm1,norm2),1));
612
10/10
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 52 times.
✓ Branch 5 taken 68 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 40 times.
✓ Branch 8 taken 10 times.
✓ Branch 9 taken 12 times.
130 if (((mtn1 >= 500 || mtn2 >= 500) && (mtn1*2 < mtn2*1 || mtn2*2 < mtn1*1)) ||
613
7/8
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 7 times.
22 ((mtn1 >= 1000 || mtn2 >= 1000) && (mtn1*3 < mtn2*2 || mtn2*3 < mtn1*2)) ||
614
7/8
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 4 times.
17 ((mtn1 >= 2000 || mtn2 >= 2000) && (mtn1*5 < mtn2*4 || mtn2*5 < mtn1*4)) ||
615
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
14 ((mtn1 >= 4000 || mtn2 >= 4000) && c2 > c1))
616
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 70 times.
119 ret = mtn1 > mtn2 ? match2 : match1;
617
5/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
11 else if (mr > 0.005 && FFMAX(mtn1, mtn2) > 150 && (mtn1*2 < mtn2*1 || mtn2*2 < mtn1*1))
618 ✗ ret = mtn1 > mtn2 ? match2 : match1;
619 else
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 ret = norm1 > norm2 ? match2 : match1;
621 130 return ret;
622 }
623
624 318 static void copy_fields(const FieldMatchContext *fm, AVFrame *dst,
625 const AVFrame *src, int field, int input)
626 {
627 int plane;
628
4/6
✓ Branch 0 taken 1272 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 954 times.
✓ Branch 3 taken 318 times.
✓ Branch 4 taken 954 times.
✗ Branch 5 not taken.
1272 for (plane = 0; plane < 4 && src->data[plane] && src->linesize[plane]; plane++) {
629 954 const int plane_h = get_height(fm, src, plane, input);
630
2/2
✓ Branch 0 taken 477 times.
✓ Branch 1 taken 477 times.
954 const int nb_copy_fields = (plane_h >> 1) + (field ? 0 : (plane_h & 1));
631 954 av_image_copy_plane(dst->data[plane] + field*dst->linesize[plane], dst->linesize[plane] << 1,
632 954 src->data[plane] + field*src->linesize[plane], src->linesize[plane] << 1,
633 954 get_width(fm, src, plane, input) * fm->bpc, nb_copy_fields);
634 }
635 318 }
636
637 159 static AVFrame *create_weave_frame(AVFilterContext *ctx, int match, int field,
638 const AVFrame *prv, AVFrame *src, const AVFrame *nxt, int input)
639 {
640 AVFrame *dst;
641 159 FieldMatchContext *fm = ctx->priv;
642
643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 if (match == mC) {
644 ✗ dst = av_frame_clone(src);
645 } else {
646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 AVFilterLink *link = input == INPUT_CLEANSRC ? ctx->outputs[0] : ctx->inputs[INPUT_MAIN];
647
648 159 dst = ff_get_video_buffer(link, link->w, link->h);
649
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 159 times.
159 if (!dst)
650 ✗ return NULL;
651 159 av_frame_copy_props(dst, src);
652
653
2/5
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
159 switch (match) {
654 49 case mP: copy_fields(fm, dst, src, 1-field, input); copy_fields(fm, dst, prv, field, input); break;
655 110 case mN: copy_fields(fm, dst, src, 1-field, input); copy_fields(fm, dst, nxt, field, input); break;
656 ✗ case mB: copy_fields(fm, dst, src, field, input); copy_fields(fm, dst, prv, 1-field, input); break;
657 ✗ case mU: copy_fields(fm, dst, src, field, input); copy_fields(fm, dst, nxt, 1-field, input); break;
658 ✗ default: av_assert0(0);
659 }
660 }
661 159 return dst;
662 }
663
664 110 static int checkmm(AVFilterContext *ctx, int *combs, int m1, int m2,
665 AVFrame **gen_frames, int field)
666 {
667 110 const FieldMatchContext *fm = ctx->priv;
668
669 #define LOAD_COMB(mid) do { \
670 if (combs[mid] < 0) { \
671 if (!gen_frames[mid]) \
672 gen_frames[mid] = create_weave_frame(ctx, mid, field, \
673 fm->prv, fm->src, fm->nxt, \
674 INPUT_MAIN); \
675 combs[mid] = calc_combed_score(fm, gen_frames[mid]); \
676 } \
677 } while (0)
678
679
3/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 61 times.
110 LOAD_COMB(m1);
680
2/4
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
110 LOAD_COMB(m2);
681
682
2/6
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 110 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
110 if ((combs[m2] * 3 < combs[m1] || (combs[m2] * 2 < combs[m1] && combs[m1] > fm->combpel)) &&
683 ✗ abs(combs[m2] - combs[m1]) >= 30 && combs[m2] < fm->combpel)
684 ✗ return m2;
685 else
686 110 return m1;
687 }
688
689 static const int fxo0m[] = { mP, mC, mN, mB, mU };
690 static const int fxo1m[] = { mN, mC, mP, mU, mB };
691
692 135 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
693 {
694 135 AVFilterContext *ctx = inlink->dst;
695 135 AVFilterLink *outlink = ctx->outputs[0];
696 135 FilterLink *outl = ff_filter_link(outlink);
697 135 FieldMatchContext *fm = ctx->priv;
698 135 int combs[] = { -1, -1, -1, -1, -1 };
699 135 int order, field, i, match, interlaced_frame, sc = 0, ret = 0;
700 const int *fxo;
701 135 AVFrame *gen_frames[] = { NULL, NULL, NULL, NULL, NULL };
702 135 AVFrame *dst = NULL;
703
704 /* update frames queue(s) */
705 #define SLIDING_FRAME_WINDOW(prv, src, nxt) do { \
706 if (prv != src) /* 2nd loop exception (1st has prv==src and we don't want to loose src) */ \
707 av_frame_free(&prv); \
708 prv = src; \
709 src = nxt; \
710 if (in) \
711 nxt = in; \
712 if (!prv) \
713 prv = src; \
714 if (!prv) /* received only one frame at that point */ \
715 return 0; \
716 av_assert0(prv && src && nxt); \
717 } while (0)
718
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
135 if (FF_INLINK_IDX(inlink) == INPUT_MAIN) {
719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
135 av_assert0(fm->got_frame[INPUT_MAIN] == 0);
720
10/14
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 15 times.
✓ Branch 3 taken 135 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 125 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 130 times.
✓ Branch 9 taken 130 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 130 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 130 times.
135 SLIDING_FRAME_WINDOW(fm->prv, fm->src, fm->nxt);
721 130 fm->got_frame[INPUT_MAIN] = 1;
722 } else {
723 ✗ av_assert0(fm->got_frame[INPUT_CLEANSRC] == 0);
724 ✗ SLIDING_FRAME_WINDOW(fm->prv2, fm->src2, fm->nxt2);
725 ✗ fm->got_frame[INPUT_CLEANSRC] = 1;
726 }
727
2/6
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 130 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
130 if (!fm->got_frame[INPUT_MAIN] || (fm->ppsrc && !fm->got_frame[INPUT_CLEANSRC]))
728 ✗ return 0;
729 130 fm->got_frame[INPUT_MAIN] = fm->got_frame[INPUT_CLEANSRC] = 0;
730 130 in = fm->src;
731
732 /* parity */
733
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
260 order = fm->order != FM_PARITY_AUTO ? fm->order : ((in->flags & AV_FRAME_FLAG_INTERLACED) ?
734
3/4
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 35 times.
✓ Branch 2 taken 95 times.
✗ Branch 3 not taken.
130 !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1);
735
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 field = fm->field != FM_PARITY_AUTO ? fm->field : order;
736
2/8
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 130 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
130 av_assert0(order == 0 || order == 1 || field == 0 || field == 1);
737
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 fxo = field ^ order ? fxo1m : fxo0m;
738
739 /* debug mode: we generate all the fields combinations and their associated
740 * combed score. XXX: inject as frame metadata? */
741
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (fm->combdbg) {
742 ✗ for (i = 0; i < FF_ARRAY_ELEMS(combs); i++) {
743 ✗ if (i > mN && fm->combdbg == COMBDBG_PCN)
744 ✗ break;
745 ✗ gen_frames[i] = create_weave_frame(ctx, i, field, fm->prv, fm->src, fm->nxt, INPUT_MAIN);
746 ✗ if (!gen_frames[i]) {
747 ✗ ret = AVERROR(ENOMEM);
748 ✗ goto fail;
749 }
750 ✗ combs[i] = calc_combed_score(fm, gen_frames[i]);
751 }
752 ✗ av_log(ctx, AV_LOG_INFO, "COMBS: %3d %3d %3d %3d %3d\n",
753 combs[0], combs[1], combs[2], combs[3], combs[4]);
754 } else {
755 130 gen_frames[mC] = av_frame_clone(fm->src);
756
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (!gen_frames[mC]) {
757 ✗ ret = AVERROR(ENOMEM);
758 ✗ goto fail;
759 }
760 }
761
762 /* p/c selection and optional 3-way p/c/n matches */
763 130 match = compare_fields(fm, fxo[mC], fxo[mP], field);
764
2/4
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 130 times.
130 if (fm->mode == MODE_PCN || fm->mode == MODE_PCN_UB)
765 ✗ match = compare_fields(fm, match, fxo[mN], field);
766
767 /* scene change check */
768
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 if (fm->combmatch == COMBMATCH_SC) {
769
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 70 times.
130 if (fm->lastn == outl->frame_count_in - 1) {
770
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 15 times.
60 if (fm->lastscdiff > fm->scthresh)
771 45 sc = 1;
772
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 50 times.
70 } else if (luma_abs_diff(fm->prv, fm->src) > fm->scthresh) {
773 20 sc = 1;
774 }
775
776
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 65 times.
130 if (!sc) {
777 65 fm->lastn = outl->frame_count_in;
778 65 fm->lastscdiff = luma_abs_diff(fm->src, fm->nxt);
779 65 sc = fm->lastscdiff > fm->scthresh;
780 }
781 }
782
783
4/6
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 110 times.
✓ Branch 5 taken 20 times.
130 if (fm->combmatch == COMBMATCH_FULL || (fm->combmatch == COMBMATCH_SC && sc)) {
784
1/7
✗ Branch 0 not taken.
✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
110 switch (fm->mode) {
785 /* 2-way p/c matches */
786 ✗ case MODE_PC:
787 ✗ match = checkmm(ctx, combs, match, match == fxo[mP] ? fxo[mC] : fxo[mP], gen_frames, field);
788 ✗ break;
789 110 case MODE_PC_N:
790 110 match = checkmm(ctx, combs, match, fxo[mN], gen_frames, field);
791 110 break;
792 ✗ case MODE_PC_U:
793 ✗ match = checkmm(ctx, combs, match, fxo[mU], gen_frames, field);
794 ✗ break;
795 ✗ case MODE_PC_N_UB:
796 ✗ match = checkmm(ctx, combs, match, fxo[mN], gen_frames, field);
797 ✗ match = checkmm(ctx, combs, match, fxo[mU], gen_frames, field);
798 ✗ match = checkmm(ctx, combs, match, fxo[mB], gen_frames, field);
799 ✗ break;
800 /* 3-way p/c/n matches */
801 ✗ case MODE_PCN:
802 ✗ match = checkmm(ctx, combs, match, match == fxo[mP] ? fxo[mC] : fxo[mP], gen_frames, field);
803 ✗ break;
804 ✗ case MODE_PCN_UB:
805 ✗ match = checkmm(ctx, combs, match, fxo[mU], gen_frames, field);
806 ✗ match = checkmm(ctx, combs, match, fxo[mB], gen_frames, field);
807 ✗ break;
808 ✗ default:
809 ✗ av_assert0(0);
810 }
811 }
812
813 /* keep fields as-is if not matched properly */
814 130 interlaced_frame = combs[match] >= fm->combpel;
815
3/4
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 110 times.
130 if (interlaced_frame && fm->combmatch == COMBMATCH_FULL) {
816 ✗ match = mC;
817 }
818
819 /* get output frame and drop the others */
820
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (fm->ppsrc) {
821 /* field matching was based on a filtered/post-processed input, we now
822 * pick the untouched fields from the clean source */
823 ✗ dst = create_weave_frame(ctx, match, field, fm->prv2, fm->src2, fm->nxt2, INPUT_CLEANSRC);
824 } else {
825
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (!gen_frames[match]) { // XXX: is that possible?
826 ✗ dst = create_weave_frame(ctx, match, field, fm->prv, fm->src, fm->nxt, INPUT_MAIN);
827 } else {
828 130 dst = gen_frames[match];
829 130 gen_frames[match] = NULL;
830 }
831 }
832
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 130 times.
130 if (!dst) {
833 ✗ ret = AVERROR(ENOMEM);
834 ✗ goto fail;
835 }
836
837 /* mark the frame we are unable to match properly as interlaced so a proper
838 * de-interlacer can take the relay */
839
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 20 times.
130 if (interlaced_frame) {
840 110 dst->flags |= AV_FRAME_FLAG_INTERLACED;
841 110 av_log(ctx, AV_LOG_WARNING, "Frame #%"PRId64" at %s is still interlaced\n",
842 110 outl->frame_count_in, av_ts2timestr(in->pts, &inlink->time_base));
843
1/2
✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
110 if (field)
844 110 dst->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST;
845 else
846 ✗ dst->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST;
847 } else
848 20 dst->flags &= ~AV_FRAME_FLAG_INTERLACED;
849
850 130 av_log(ctx, AV_LOG_DEBUG, "SC:%d | COMBS: %3d %3d %3d %3d %3d (combpel=%d)"
851 " match=%d combed=%s\n", sc, combs[0], combs[1], combs[2], combs[3], combs[4],
852
2/2
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 20 times.
130 fm->combpel, match, (dst->flags & AV_FRAME_FLAG_INTERLACED) ? "YES" : "NO");
853
854 130 fail:
855
2/2
✓ Branch 0 taken 650 times.
✓ Branch 1 taken 130 times.
780 for (i = 0; i < FF_ARRAY_ELEMS(gen_frames); i++)
856 650 av_frame_free(&gen_frames[i]);
857
858
1/2
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
130 if (ret >= 0)
859 130 return ff_filter_frame(outlink, dst);
860 ✗ return ret;
861 }
862
863 240 static int activate(AVFilterContext *ctx)
864 {
865 240 FieldMatchContext *fm = ctx->priv;
866 240 AVFrame *frame = NULL;
867 240 int ret = 0, status;
868 int64_t pts;
869
870
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 240 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
240 FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx);
871
872
3/4
✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135 times.
✓ Branch 3 taken 105 times.
480 if ((fm->got_frame[INPUT_MAIN] == 0) &&
873 240 (ret = ff_inlink_consume_frame(ctx->inputs[INPUT_MAIN], &frame)) > 0) {
874 135 ret = filter_frame(ctx->inputs[INPUT_MAIN], frame);
875
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 135 times.
135 if (ret < 0)
876 ✗ return ret;
877 }
878
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
240 if (ret < 0)
879 ✗ return ret;
880
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
240 if (fm->ppsrc &&
881 ✗ (fm->got_frame[INPUT_CLEANSRC] == 0) &&
882 ✗ (ret = ff_inlink_consume_frame(ctx->inputs[INPUT_CLEANSRC], &frame)) > 0) {
883 ✗ ret = filter_frame(ctx->inputs[INPUT_CLEANSRC], frame);
884 ✗ if (ret < 0)
885 ✗ return ret;
886 }
887
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
240 if (ret < 0) {
888 ✗ return ret;
889
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 240 times.
240 } else if (ff_inlink_acknowledge_status(ctx->inputs[INPUT_MAIN], &status, &pts)) {
890 ✗ if (status == AVERROR_EOF) { // flushing
891 ✗ fm->eof |= 1 << INPUT_MAIN;
892 ✗ ret = filter_frame(ctx->inputs[INPUT_MAIN], NULL);
893 }
894 ✗ ff_outlink_set_status(ctx->outputs[0], status, pts);
895 ✗ return ret;
896
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
240 } else if (fm->ppsrc && ff_inlink_acknowledge_status(ctx->inputs[INPUT_CLEANSRC], &status, &pts)) {
897 ✗ if (status == AVERROR_EOF) { // flushing
898 ✗ fm->eof |= 1 << INPUT_CLEANSRC;
899 ✗ ret = filter_frame(ctx->inputs[INPUT_CLEANSRC], NULL);
900 }
901 ✗ ff_outlink_set_status(ctx->outputs[0], status, pts);
902 ✗ return ret;
903 } else {
904
2/2
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 135 times.
240 if (ff_outlink_frame_wanted(ctx->outputs[0])) {
905
1/2
✓ Branch 0 taken 105 times.
✗ Branch 1 not taken.
105 if (fm->got_frame[INPUT_MAIN] == 0)
906 105 ff_inlink_request_frame(ctx->inputs[INPUT_MAIN]);
907
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
105 if (fm->ppsrc && (fm->got_frame[INPUT_CLEANSRC] == 0))
908 ✗ ff_inlink_request_frame(ctx->inputs[INPUT_CLEANSRC]);
909 }
910 240 return 0;
911 }
912 }
913
914 6 static int query_formats(const AVFilterContext *ctx,
915 AVFilterFormatsConfig **cfg_in,
916 AVFilterFormatsConfig **cfg_out)
917 {
918 6 const FieldMatchContext *fm = ctx->priv;
919
920 static const enum AVPixelFormat pix_fmts[] = {
921 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P,
922 AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
923 AV_PIX_FMT_NONE
924 };
925 static const enum AVPixelFormat unproc_pix_fmts[] = {
926 AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
927 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
928 AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P,
929 AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P,
930 AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ444P,
931 AV_PIX_FMT_YUVJ411P,
932 AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
933 AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
934 AV_PIX_FMT_YUV440P10,
935 AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12,
936 AV_PIX_FMT_YUV440P12,
937 AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14,
938 AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
939 AV_PIX_FMT_NONE
940 };
941 int ret;
942
943 6 AVFilterFormats *fmts_list = ff_make_pixel_format_list(pix_fmts);
944
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!fmts_list)
945 ✗ return AVERROR(ENOMEM);
946
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (!fm->ppsrc) {
947 6 return ff_set_common_formats2(ctx, cfg_in, cfg_out, fmts_list);
948 }
949
950 ✗ if ((ret = ff_formats_ref(fmts_list, &cfg_in[INPUT_MAIN]->formats)) < 0)
951 ✗ return ret;
952 ✗ fmts_list = ff_make_pixel_format_list(unproc_pix_fmts);
953 ✗ if (!fmts_list)
954 ✗ return AVERROR(ENOMEM);
955 ✗ if ((ret = ff_formats_ref(fmts_list, &cfg_out[0]->formats)) < 0)
956 ✗ return ret;
957 ✗ if ((ret = ff_formats_ref(fmts_list, &cfg_in[INPUT_CLEANSRC]->formats)) < 0)
958 ✗ return ret;
959 ✗ return 0;
960 }
961
962 5 static int config_input(AVFilterLink *inlink)
963 {
964 int ret;
965 5 AVFilterContext *ctx = inlink->dst;
966 5 FieldMatchContext *fm = ctx->priv;
967 5 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format);
968 5 const int w = inlink->w;
969 5 const int h = inlink->h;
970
971
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (h <= fm->blocky / 2 || AV_CEIL_RSHIFT(h, pix_desc->log2_chroma_h) < 4) {
972 ✗ av_log(ctx, AV_LOG_ERROR, "Height %d is too small\n", h);
973 ✗ return AVERROR(EINVAL);
974 }
975
976 5 fm->scthresh = (int64_t)((w * h * 255.0 * fm->scthresh_flt) / 100.0);
977
978
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
10 if ((ret = av_image_alloc(fm->map_data, fm->map_linesize, w, h, inlink->format, 32)) < 0 ||
979 5 (ret = av_image_alloc(fm->cmask_data, fm->cmask_linesize, w, h, inlink->format, 32)) < 0)
980 ✗ return ret;
981
982 5 fm->hsub[INPUT_MAIN] = pix_desc->log2_chroma_w;
983 5 fm->vsub[INPUT_MAIN] = pix_desc->log2_chroma_h;
984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (fm->ppsrc) {
985 ✗ pix_desc = av_pix_fmt_desc_get(ctx->inputs[INPUT_CLEANSRC]->format);
986 ✗ fm->hsub[INPUT_CLEANSRC] = pix_desc->log2_chroma_w;
987 ✗ fm->vsub[INPUT_CLEANSRC] = pix_desc->log2_chroma_h;
988 }
989
990 5 fm->tpitchy = FFALIGN(w, 16);
991 5 fm->tpitchuv = FFALIGN(w >> 1, 16);
992
993 5 fm->tbuffer = av_calloc((h/2 + 4) * fm->tpitchy, sizeof(*fm->tbuffer));
994 10 fm->c_array = av_malloc_array((((w + fm->blockx/2)/fm->blockx)+1) *
995 5 (((h + fm->blocky/2)/fm->blocky)+1),
996 4 * sizeof(*fm->c_array));
997
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
5 if (!fm->tbuffer || !fm->c_array)
998 ✗ return AVERROR(ENOMEM);
999
1000 5 return 0;
1001 }
1002
1003 11 static av_cold int fieldmatch_init(AVFilterContext *ctx)
1004 {
1005 11 const FieldMatchContext *fm = ctx->priv;
1006 11 AVFilterPad pad = {
1007 .name = "main",
1008 .type = AVMEDIA_TYPE_VIDEO,
1009 .config_props = config_input,
1010 };
1011 int ret;
1012
1013
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 if ((ret = ff_append_inpad(ctx, &pad)) < 0)
1014 ✗ return ret;
1015
1016
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (fm->ppsrc) {
1017 ✗ pad.name = "clean_src";
1018 ✗ pad.config_props = NULL;
1019 ✗ if ((ret = ff_append_inpad(ctx, &pad)) < 0)
1020 ✗ return ret;
1021 }
1022
1023
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if ((fm->blockx & (fm->blockx - 1)) ||
1024
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 (fm->blocky & (fm->blocky - 1))) {
1025 ✗ av_log(ctx, AV_LOG_ERROR, "blockx and blocky settings must be power of two\n");
1026 ✗ return AVERROR(EINVAL);
1027 }
1028
1029
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (fm->combpel > fm->blockx * fm->blocky) {
1030 ✗ av_log(ctx, AV_LOG_ERROR, "Combed pixel should not be larger than blockx x blocky\n");
1031 ✗ return AVERROR(EINVAL);
1032 }
1033
1034 11 return 0;
1035 }
1036
1037 11 static av_cold void fieldmatch_uninit(AVFilterContext *ctx)
1038 {
1039 11 FieldMatchContext *fm = ctx->priv;
1040
1041
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if (fm->prv != fm->src)
1042 5 av_frame_free(&fm->prv);
1043
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
11 if (fm->nxt != fm->src)
1044 5 av_frame_free(&fm->nxt);
1045
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (fm->prv2 != fm->src2)
1046 ✗ av_frame_free(&fm->prv2);
1047
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (fm->nxt2 != fm->src2)
1048 ✗ av_frame_free(&fm->nxt2);
1049 11 av_frame_free(&fm->src);
1050 11 av_frame_free(&fm->src2);
1051 11 av_freep(&fm->map_data[0]);
1052 11 av_freep(&fm->cmask_data[0]);
1053 11 av_freep(&fm->tbuffer);
1054 11 av_freep(&fm->c_array);
1055 11 }
1056
1057 5 static int config_output(AVFilterLink *outlink)
1058 {
1059 5 FilterLink *outl = ff_filter_link(outlink);
1060 5 AVFilterContext *ctx = outlink->src;
1061 5 FieldMatchContext *fm = ctx->priv;
1062 5 const AVFilterLink *inlink =
1063
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 ctx->inputs[fm->ppsrc ? INPUT_CLEANSRC : INPUT_MAIN];
1064
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 FilterLink *inl = ff_filter_link(ctx->inputs[fm->ppsrc ? INPUT_CLEANSRC : INPUT_MAIN]);
1065 5 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
1066
1067 5 fm->bpc = (desc->comp[0].depth + 7) / 8;
1068 5 outlink->time_base = inlink->time_base;
1069 5 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
1070 5 outl->frame_rate = inl->frame_rate;
1071 5 outlink->w = inlink->w;
1072 5 outlink->h = inlink->h;
1073 5 return 0;
1074 }
1075
1076 static const AVFilterPad fieldmatch_outputs[] = {
1077 {
1078 .name = "default",
1079 .type = AVMEDIA_TYPE_VIDEO,
1080 .config_props = config_output,
1081 },
1082 };
1083
1084 const FFFilter ff_vf_fieldmatch = {
1085 .p.name = "fieldmatch",
1086 .p.description = NULL_IF_CONFIG_SMALL("Field matching for inverse telecine."),
1087 .p.priv_class = &fieldmatch_class,
1088 .p.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
1089 .priv_size = sizeof(FieldMatchContext),
1090 .init = fieldmatch_init,
1091 .activate = activate,
1092 .uninit = fieldmatch_uninit,
1093 FILTER_OUTPUTS(fieldmatch_outputs),
1094 FILTER_QUERY_FUNC2(query_formats),
1095 };
1096