1 /*
2 * Copyright (c) 2012-2015 Fredrik Mellbin
3 *
4 * This file is part of VapourSynth.
5 *
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * VapourSynth is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with VapourSynth; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 // Some metrics calculation used is directly based on TIVTC
22
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <math.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #ifdef _WIN32
30 #include <windows.h>
31 #endif
32 #include "VapourSynth.h"
33 #include "VSHelper.h"
34
35 // Shared
36
isPowerOf2(int i)37 static int isPowerOf2(int i) {
38 return i && !(i & (i - 1));
39 }
40
41 // VFM
42
43 typedef struct {
44 VSNodeRef *node;
45 VSNodeRef *clip2;
46 const VSVideoInfo *vi;
47 double scthresh;
48 int tpitchy;
49 int tpitchuv;
50 int order;
51 int field;
52 int mode;
53 int chroma;
54 int mchroma;
55 int cthresh;
56 int mi;
57 int blockx;
58 int blocky;
59 int y0;
60 int y1;
61 int micmatch;
62 int micout;
63 } VFMData;
64
65
copyField(VSFrameRef * dst,const VSFrameRef * src,int field,const VSAPI * vsapi)66 static void copyField(VSFrameRef *dst, const VSFrameRef *src, int field, const VSAPI *vsapi) {
67 const VSFormat *fi = vsapi->getFrameFormat(src);
68 int plane;
69 for (plane=0; plane<fi->numPlanes; plane++) {
70 vs_bitblt(vsapi->getWritePtr(dst, plane)+field*vsapi->getStride(dst, plane),vsapi->getStride(dst, plane)*2,
71 vsapi->getReadPtr(src, plane)+field*vsapi->getStride(src, plane),vsapi->getStride(src, plane)*2,
72 vsapi->getFrameWidth(src, plane)*fi->bytesPerSample,vsapi->getFrameHeight(src,plane)/2);
73 }
74 }
75
76 // the secret is that tbuffer is an interlaced, offset subset of all the lines
buildABSDiffMask(const uint8_t * prvp,const uint8_t * nxtp,int src_pitch,int tpitch,uint8_t * tbuffer,int width,int height,const VSAPI * vsapi)77 static void buildABSDiffMask(const uint8_t *prvp, const uint8_t *nxtp,
78 int src_pitch, int tpitch, uint8_t *tbuffer, int width, int height,
79 const VSAPI *vsapi) {
80
81 int y, x;
82 for (y=0; y<height; ++y) {
83 for (x=0; x<width; x++)
84 tbuffer[x] = abs(prvp[x]-nxtp[x]);
85
86 prvp += src_pitch;
87 nxtp += src_pitch;
88 tbuffer += tpitch;
89 }
90 }
91
92
calcMI(const VSFrameRef * src,const VSAPI * vsapi,int * blockN,int chroma,int cthresh,VSFrameRef * cmask,int * cArray,int blockx,int blocky)93 static int calcMI(const VSFrameRef *src, const VSAPI *vsapi,
94 int *blockN, int chroma, int cthresh, VSFrameRef *cmask, int *cArray, int blockx, int blocky)
95 {
96 int ret = 0;
97 const int cthresh6 = cthresh*6;
98 int plane;
99 int x, y, u, v;
100 for (plane=0; plane < (chroma ? 3 : 1); plane++) {
101 const uint8_t *srcp = vsapi->getReadPtr(src, plane);
102 const int src_pitch = vsapi->getStride(src, plane);
103 const int Width = vsapi->getFrameWidth(src, plane);
104 const int Height = vsapi->getFrameHeight(src, plane);
105 uint8_t *cmkp = vsapi->getWritePtr(cmask, plane);
106 const int cmk_pitch = vsapi->getStride(cmask, plane);
107 if (cthresh < 0) {
108 memset(cmkp,255,Height*cmk_pitch);
109 continue;
110 }
111 memset(cmkp,0,Height*cmk_pitch);
112 for (x=0; x<Width; ++x) {
113 const int sFirst = srcp[x] - srcp[x + src_pitch];
114 if (sFirst > cthresh || sFirst < -cthresh) {
115 if (abs(srcp[x + 2*src_pitch]+(srcp[x]*4)+srcp[x + 2*src_pitch]-6*srcp[x + src_pitch]) > cthresh6)
116 cmkp[x] = 0xFF;
117 }
118 }
119 srcp += src_pitch;
120 cmkp += cmk_pitch;
121 for (x=0; x<Width; ++x) {
122 const int sFirst = srcp[x] - srcp[x - src_pitch];
123 const int sSecond = srcp[x] - srcp[x + src_pitch];
124 if ((sFirst > cthresh && sSecond > cthresh) || (sFirst < -cthresh && sSecond < -cthresh)) {
125 if (abs(srcp[x + 2*src_pitch]+(srcp[x]*4)+srcp[x + 2*src_pitch]-(3*(srcp[x - src_pitch]+srcp[x + src_pitch]))) > cthresh6)
126 cmkp[x] = 0xFF;
127 }
128 }
129 srcp += src_pitch;
130 cmkp += cmk_pitch;
131
132 for (y=2; y<Height-2; ++y) {
133 for (x=0; x<Width; ++x) {
134 const int sFirst = srcp[x] - srcp[x - src_pitch];
135 const int sSecond = srcp[x] - srcp[x + src_pitch];
136 if ((sFirst > cthresh && sSecond > cthresh) || (sFirst < -cthresh && sSecond < -cthresh)) {
137 if (abs(srcp[x - 2*src_pitch]+(srcp[x]*4)+srcp[x + 2*src_pitch]-(3*(srcp[x - src_pitch]+srcp[x + src_pitch]))) > cthresh6)
138 cmkp[x] = 0xFF;
139 }
140 }
141 srcp += src_pitch;
142 cmkp += cmk_pitch;
143 }
144
145 for (x=0; x<Width; ++x) {
146 const int sFirst = srcp[x] - srcp[x - src_pitch];
147 const int sSecond = srcp[x] - srcp[x + src_pitch];
148 if ((sFirst > cthresh && sSecond > cthresh) || (sFirst < -cthresh && sSecond < -cthresh)) {
149 if (abs(srcp[x - 2*src_pitch]+(srcp[x]*4)+srcp[x - 2*src_pitch]-(3*(srcp[x - src_pitch]+srcp[x + src_pitch]))) > cthresh6)
150 cmkp[x] = 0xFF;
151 }
152 }
153 srcp += src_pitch;
154 cmkp += cmk_pitch;
155 for (x=0; x<Width; ++x) {
156 const int sFirst = srcp[x] - srcp[x - src_pitch];
157 if (sFirst > cthresh || sFirst < -cthresh) {
158 if (abs(2*srcp[x - 2*src_pitch]+(srcp[x]*4)-6*srcp[x - src_pitch]) > cthresh6)
159 cmkp[x] = 0xFF;
160 }
161 }
162 }
163 if (chroma) {
164 const VSFormat *src_fmt = vsapi->getFrameFormat(src);
165
166 uint8_t *cmkp = vsapi->getWritePtr(cmask, 0);
167 uint8_t *cmkpU = vsapi->getWritePtr(cmask, 1);
168 uint8_t *cmkpV = vsapi->getWritePtr(cmask, 2);
169 const int Width = vsapi->getFrameWidth(cmask, 2);
170 const int Height = vsapi->getFrameHeight(cmask, 2);
171 int cmk_pitch = vsapi->getStride(cmask, 0);
172 const int cmk_pitchUV = vsapi->getStride(cmask, 2);
173 uint8_t *cmkpp = cmkp - cmk_pitch;
174 uint8_t *cmkpn = cmkp + cmk_pitch;
175 uint8_t *cmkpnn = cmkpn + cmk_pitch;
176
177 cmk_pitch <<= src_fmt->subSamplingH;
178
179 for (y=1; y<Height-1; ++y) {
180 cmkpp += cmk_pitch;
181 cmkp += cmk_pitch;
182 cmkpn += cmk_pitch;
183 cmkpnn += cmk_pitch;
184 cmkpV += cmk_pitchUV;
185 cmkpU += cmk_pitchUV;
186 for (x=1; x<Width-1; ++x) {
187 if ((cmkpV[x] == 0xFF && (cmkpV[x-1] == 0xFF || cmkpV[x+1] == 0xFF ||
188 cmkpV[x-1 - cmk_pitchUV] == 0xFF || cmkpV[x - cmk_pitchUV] == 0xFF || cmkpV[x+1 - cmk_pitchUV] == 0xFF ||
189 cmkpV[x-1 + cmk_pitchUV] == 0xFF || cmkpV[x + cmk_pitchUV] == 0xFF || cmkpV[x+1 + cmk_pitchUV] == 0xFF)) ||
190 (cmkpU[x] == 0xFF && (cmkpU[x-1] == 0xFF || cmkpU[x+1] == 0xFF ||
191 cmkpU[x-1 - cmk_pitchUV] == 0xFF || cmkpU[x - cmk_pitchUV] == 0xFF || cmkpU[x+1 - cmk_pitchUV] == 0xFF ||
192 cmkpU[x-1 + cmk_pitchUV] == 0xFF || cmkpU[x + cmk_pitchUV] == 0xFF || cmkpU[x+1 + cmk_pitchUV] == 0xFF)))
193 {
194 int xx = x << src_fmt->subSamplingW;
195
196 if (src_fmt->subSamplingH) {
197 if (y % 2 == 1) {
198 cmkpp[xx] = 0xFF;
199 if (src_fmt->subSamplingW)
200 cmkpp[xx + 1] = 0xFF;
201 }
202 }
203
204 cmkp[xx] = 0xFF;
205 if (src_fmt->subSamplingW)
206 cmkp[xx + 1] = 0xFF;
207
208 if (src_fmt->subSamplingH) {
209 cmkpn[xx] = 0xFF;
210 if (src_fmt->subSamplingW)
211 cmkpn[xx + 1] = 0xFF;
212
213 if (y % 2 == 0) {
214 cmkpnn[xx] = 0xFF;
215 if (src_fmt->subSamplingW)
216 cmkpnn[xx + 1] = 0xFF;
217 }
218 }
219 }
220 }
221 }
222 }
223 {
224 int xhalf = blockx/2;
225 int yhalf = blocky/2;
226 const int cmk_pitch = vsapi->getStride(cmask, 0);
227 const uint8_t *cmkp = vsapi->getReadPtr(cmask, 0) + cmk_pitch;
228 const uint8_t *cmkpp = cmkp - cmk_pitch;
229 const uint8_t *cmkpn = cmkp + cmk_pitch;
230 const int Width = vsapi->getFrameWidth(cmask, 0);
231 const int Height = vsapi->getFrameHeight(cmask, 0);
232 const int xblocks = ((Width+xhalf)/blockx) + 1;
233 const int xblocks4 = xblocks<<2;
234 const int yblocks = ((Height+yhalf)/blocky) + 1;
235 const int arraysize = (xblocks*yblocks)<<2;
236 int Heighta = (Height/(blocky/2))*(blocky/2);
237 const int Widtha = (Width/(blockx/2))*(blockx/2);
238 if (Heighta == Height)
239 Heighta = Height-yhalf;
240 memset(&cArray[0],0,arraysize*sizeof(int));
241 for (y=1; y<yhalf; ++y) {
242 const int temp1 = (y/blocky)*xblocks4;
243 const int temp2 = ((y+yhalf)/blocky)*xblocks4;
244 for (x=0; x<Width; ++x) {
245 if (cmkpp[x] == 0xFF && cmkp[x] == 0xFF && cmkpn[x] == 0xFF) {
246 const int box1 = (x/blockx)*4;
247 const int box2 = ((x+xhalf)/blockx)*4;
248 ++cArray[temp1+box1+0];
249 ++cArray[temp1+box2+1];
250 ++cArray[temp2+box1+2];
251 ++cArray[temp2+box2+3];
252 }
253 }
254 cmkpp += cmk_pitch;
255 cmkp += cmk_pitch;
256 cmkpn += cmk_pitch;
257 }
258 for (y=yhalf; y<Heighta; y+=yhalf) {
259 const int temp1 = (y/blocky)*xblocks4;
260 const int temp2 = ((y+yhalf)/blocky)*xblocks4;
261
262 for (x=0; x<Widtha; x+=xhalf) {
263 const uint8_t *cmkppT = cmkpp;
264 const uint8_t *cmkpT = cmkp;
265 const uint8_t *cmkpnT = cmkpn;
266 int sum = 0;
267 for (u=0; u<yhalf; ++u) {
268 for (v=0; v<xhalf; ++v) {
269 if (cmkppT[x+v] == 0xFF && cmkpT[x+v] == 0xFF &&
270 cmkpnT[x+v] == 0xFF) ++sum;
271 }
272 cmkppT += cmk_pitch;
273 cmkpT += cmk_pitch;
274 cmkpnT += cmk_pitch;
275 }
276 if (sum) {
277 const int box1 = (x/blockx)*4;
278 const int box2 = ((x+xhalf)/blockx)*4;
279 cArray[temp1+box1+0] += sum;
280 cArray[temp1+box2+1] += sum;
281 cArray[temp2+box1+2] += sum;
282 cArray[temp2+box2+3] += sum;
283 }
284 }
285
286 for (x=Widtha; x<Width; ++x) {
287 const uint8_t *cmkppT = cmkpp;
288 const uint8_t *cmkpT = cmkp;
289 const uint8_t *cmkpnT = cmkpn;
290 int sum = 0;
291 for (u=0; u<yhalf; ++u) {
292 if (cmkppT[x] == 0xFF && cmkpT[x] == 0xFF &&
293 cmkpnT[x] == 0xFF) ++sum;
294 cmkppT += cmk_pitch;
295 cmkpT += cmk_pitch;
296 cmkpnT += cmk_pitch;
297 }
298 if (sum) {
299 const int box1 = (x/blockx)*4;
300 const int box2 = ((x+xhalf)/blockx)*4;
301 cArray[temp1+box1+0] += sum;
302 cArray[temp1+box2+1] += sum;
303 cArray[temp2+box1+2] += sum;
304 cArray[temp2+box2+3] += sum;
305 }
306 }
307 cmkpp += cmk_pitch*yhalf;
308 cmkp += cmk_pitch*yhalf;
309 cmkpn += cmk_pitch*yhalf;
310 }
311 for (y=Heighta; y<Height-1; ++y) {
312 const int temp1 = (y/blocky)*xblocks4;
313 const int temp2 = ((y+yhalf)/blocky)*xblocks4;
314 for (x=0; x<Width; ++x) {
315 if (cmkpp[x] == 0xFF && cmkp[x] == 0xFF && cmkpn[x] == 0xFF) {
316 const int box1 = (x/blockx)*4;
317 const int box2 = ((x+xhalf)/blockx)*4;
318 ++cArray[temp1+box1+0];
319 ++cArray[temp1+box2+1];
320 ++cArray[temp2+box1+2];
321 ++cArray[temp2+box2+3];
322 }
323 }
324 cmkpp += cmk_pitch;
325 cmkp += cmk_pitch;
326 cmkpn += cmk_pitch;
327 }
328 for (x=0; x<arraysize; ++x) {
329 if (cArray[x] > ret) {
330 ret = cArray[x];
331 if (blockN)
332 *blockN = x;
333 }
334 }
335 }
336 return ret;
337 }
338
339
340 // build a map over which pixels differ a lot/a little
buildDiffMap(const uint8_t * prvp,const uint8_t * nxtp,uint8_t * dstp,int src_pitch,int dst_pitch,int Height,int Width,int tpitch,uint8_t * tbuffer,const VSAPI * vsapi)341 static void buildDiffMap(const uint8_t *prvp, const uint8_t *nxtp,
342 uint8_t *dstp,int src_pitch, int dst_pitch, int Height,
343 int Width, int tpitch, uint8_t *tbuffer, const VSAPI *vsapi)
344 {
345 const uint8_t *dp = tbuffer+tpitch;
346 int x, y, u, diff, count;
347
348 buildABSDiffMask(prvp-src_pitch, nxtp-src_pitch, src_pitch,
349 tpitch, tbuffer, Width, Height>>1, vsapi);
350
351 for (y=2; y<Height-2; y+=2) {
352 for (x=1; x<Width-1; ++x) {
353 diff = dp[x];
354 if (diff > 3) {
355 for (count=0,u=x-1; u<x+2 && count<2; ++u) {
356 if (dp[u-tpitch] > 3) ++count;
357 if (dp[u] > 3) ++count;
358 if (dp[u+tpitch] > 3) ++count;
359 }
360 if (count > 1) {
361 ++dstp[x];
362 if (diff > 19) {
363 int upper = 0, lower = 0;
364 for (count=0, u=x-1; u<x+2 && count<6; ++u) {
365 if (dp[u-tpitch] > 19) { ++count; upper = 1; }
366 if (dp[u] > 19) ++count;
367 if (dp[u+tpitch] > 19) { ++count; lower = 1; }
368 }
369 if (count > 3) {
370 if (!upper || !lower) {
371 int upper2 = 0, lower2 = 0;
372 for (u=VSMAX(x-4,0); u<VSMIN(x+5,Width); ++u)
373 {
374 if (y != 2 && dp[u-2*tpitch] > 19)
375 upper2 = 1;
376 if (dp[u-tpitch] > 19)
377 upper = 1;
378 if (dp[u+tpitch] > 19)
379 lower = 1;
380 if (y != Height-4 && dp[u+2*tpitch] > 19)
381 lower2 = 1;
382 }
383 if ((upper && (lower || upper2)) ||
384 (lower && (upper || lower2)))
385 dstp[x] += 2;
386 else if (count > 5)
387 dstp[x] += 4;
388 }
389 else dstp[x] += 2;
390 }
391 }
392 }
393 }
394 }
395 dp += tpitch;
396 dstp += dst_pitch;
397 }
398 }
399
compareFieldsSlow(const VSFrameRef * prv,const VSFrameRef * src,const VSFrameRef * nxt,VSFrameRef * map,int match1,int match2,int mchroma,int field,int y0,int y1,uint8_t * tbuffer,int tpitchy,int tpitchuv,const VSAPI * vsapi)400 static int compareFieldsSlow(const VSFrameRef *prv, const VSFrameRef *src, const VSFrameRef *nxt, VSFrameRef *map, int match1,
401 int match2, int mchroma, int field, int y0, int y1, uint8_t *tbuffer, int tpitchy, int tpitchuv, const VSAPI *vsapi)
402 {
403 int plane, ret;
404 const uint8_t *prvp = 0, *srcp = 0, *nxtp = 0;
405 const uint8_t *curpf = 0, *curf = 0, *curnf = 0;
406 const uint8_t *prvpf = 0, *prvnf = 0, *nxtpf = 0, *nxtnf = 0;
407 uint8_t *mapp;
408 int src_stride, Width, Height;
409 int curf_pitch, stopx, map_pitch;
410 int x, y, temp1, temp2, startx, y0a, y1a, tp;
411 int stop = mchroma ? 3 : 1;
412 unsigned long accumPc = 0, accumNc = 0, accumPm = 0;
413 unsigned long accumNm = 0, accumPml = 0, accumNml = 0;
414 int norm1, norm2, mtn1, mtn2;
415 float c1, c2, mr;
416
417 const VSFormat *src_fmt = vsapi->getFrameFormat(src);
418
419 for (plane=0; plane<stop; ++plane) {
420 mapp = vsapi->getWritePtr(map, plane);
421 map_pitch = vsapi->getStride(map, plane);
422 prvp = vsapi->getReadPtr(prv, plane);
423 srcp = vsapi->getReadPtr(src, plane);
424 src_stride = vsapi->getStride(src, plane);
425 Width = vsapi->getFrameWidth(src, plane);
426 Height = vsapi->getFrameHeight(src, plane);
427 nxtp = vsapi->getReadPtr(nxt, plane);
428 memset(mapp,0,Height*map_pitch);
429 startx = (plane == 0 ? 8 : 8 >> src_fmt->subSamplingW);
430 stopx = Width - startx;
431 curf_pitch = src_stride<<1;
432 if (plane == 0) {
433 y0a = y0;
434 y1a = y1;
435 tp = tpitchy;
436 } else {
437 y0a = y0>>src_fmt->subSamplingH;
438 y1a = y1>>src_fmt->subSamplingH;
439 tp = tpitchuv;
440 }
441 if (match1 < 3) {
442 curf = srcp + ((3-field)*src_stride);
443 mapp = mapp + ((field == 1 ? 1 : 2)*map_pitch);
444 }
445 if (match1 == 0) {
446 prvpf = prvp + ((field == 1 ? 1 : 2)*src_stride);
447 } else if (match1 == 1) {
448 prvpf = srcp + ((field == 1 ? 1 : 2)*src_stride);
449 } else if (match1 == 2) {
450 prvpf = nxtp + ((field == 1 ? 1 : 2)*src_stride);
451 } else if (match1 == 3) {
452 curf = srcp + ((2+field)*src_stride);
453 prvpf = prvp + ((field == 1 ? 2 : 1)*src_stride);
454 mapp = mapp + ((field == 1 ? 2 : 1)*map_pitch);
455 } else if (match1 == 4) {
456 curf = srcp + ((2+field)*src_stride);
457 prvpf = nxtp + ((field == 1 ? 2 : 1)*src_stride);
458 mapp = mapp + ((field == 1 ? 2 : 1)*map_pitch);
459 }
460 if (match2 == 0) {
461 nxtpf = prvp + ((field == 1 ? 1 : 2)*src_stride);
462 } else if (match2 == 1) {
463 nxtpf = srcp + ((field == 1 ? 1 : 2)*src_stride);
464 } else if (match2 == 2) {
465 nxtpf = nxtp + ((field == 1 ? 1 : 2)*src_stride);
466 } else if (match2 == 3) {
467 nxtpf = prvp + ((field == 1 ? 2 : 1)*src_stride);
468 } else if (match2 == 4)
469 {
470 nxtpf = nxtp + ((field == 1 ? 2 : 1)*src_stride);
471 }
472 prvnf = prvpf + curf_pitch;
473 curpf = curf - curf_pitch;
474 curnf = curf + curf_pitch;
475 nxtnf = nxtpf + curf_pitch;
476 map_pitch <<= 1;
477 if ((match1 >= 3 && field == 1) || (match1 < 3 && field != 1))
478 buildDiffMap(prvpf,nxtpf,mapp,curf_pitch,map_pitch,Height,Width,tp,tbuffer,vsapi);
479 else
480 buildDiffMap(prvnf,nxtnf,mapp + map_pitch,curf_pitch,map_pitch,Height,Width,tp,tbuffer,vsapi);
481
482 for (y=2; y<Height-2; y+=2) {
483 if (y0a == y1a || y < y0a || y > y1a) {
484 for (x=startx; x<stopx; x++) {
485 if (mapp[x] > 0 || mapp[x + map_pitch] > 0) {
486 temp1 = curpf[x]+(curf[x]<<2)+curnf[x];
487 temp2 = abs(3*(prvpf[x]+prvnf[x])-temp1);
488 if (temp2 > 23 && ((mapp[x]&1) || (mapp[x + map_pitch]&1)))
489 accumPc += temp2;
490 if (temp2 > 42) {
491 if ((mapp[x]&2) || (mapp[x + map_pitch]&2))
492 accumPm += temp2;
493 if ((mapp[x]&4) || (mapp[x + map_pitch]&4))
494 accumPml += temp2;
495 }
496 temp2 = abs(3*(nxtpf[x]+nxtnf[x])-temp1);
497 if (temp2 > 23 && ((mapp[x]&1) || (mapp[x + map_pitch]&1)))
498 accumNc += temp2;
499 if (temp2 > 42) {
500 if ((mapp[x]&2) || (mapp[x + map_pitch]&2))
501 accumNm += temp2;
502 if ((mapp[x]&4) || (mapp[x + map_pitch]&4))
503 accumNml += temp2;
504 }
505 }
506 }
507 }
508 prvpf += curf_pitch;
509 prvnf += curf_pitch;
510 curpf += curf_pitch;
511 curf += curf_pitch;
512 curnf += curf_pitch;
513 nxtpf += curf_pitch;
514 nxtnf += curf_pitch;
515 mapp += map_pitch;
516 }
517 }
518 if (accumPm < 500 && accumNm < 500 && (accumPml >= 500 || accumNml >= 500) &&
519 VSMAX(accumPml,accumNml) > 3*VSMIN(accumPml,accumNml))
520 {
521 accumPm = accumPml;
522 accumNm = accumNml;
523 }
524 norm1 = (int)((accumPc / 6.0f) + 0.5f);
525 norm2 = (int)((accumNc / 6.0f) + 0.5f);
526 mtn1 = (int)((accumPm / 6.0f) + 0.5f);
527 mtn2 = (int)((accumNm / 6.0f) + 0.5f);
528 c1 = ((float)VSMAX(norm1,norm2))/((float)VSMAX(VSMIN(norm1,norm2),1));
529 c2 = ((float)VSMAX(mtn1,mtn2))/((float)VSMAX(VSMIN(mtn1,mtn2),1));
530 mr = ((float)VSMAX(mtn1,mtn2))/((float)VSMAX(VSMAX(norm1,norm2),1));
531 if (((mtn1 >= 500 || mtn2 >= 500) && (mtn1*2 < mtn2*1 || mtn2*2 < mtn1*1)) ||
532 ((mtn1 >= 1000 || mtn2 >= 1000) && (mtn1*3 < mtn2*2 || mtn2*3 < mtn1*2)) ||
533 ((mtn1 >= 2000 || mtn2 >= 2000) && (mtn1*5 < mtn2*4 || mtn2*5 < mtn1*4)) ||
534 ((mtn1 >= 4000 || mtn2 >= 4000) && c2 > c1))
535 {
536 if (mtn1 > mtn2)
537 ret = match2;
538 else
539 ret = match1;
540 } else if (mr > 0.005 && VSMAX(mtn1,mtn2) > 150 && (mtn1*2 < mtn2*1 || mtn2*2 < mtn1*1)) {
541 if (mtn1 > mtn2)
542 ret = match2;
543 else
544 ret = match1;
545 } else {
546 if (norm1 > norm2)
547 ret = match2;
548 else
549 ret = match1;
550 }
551 return ret;
552 }
553
554
createWeaveFrame(const VSFrameRef * prv,const VSFrameRef * src,const VSFrameRef * nxt,const VSAPI * vsapi,VSCore * core,int match,int field)555 static const VSFrameRef *createWeaveFrame(const VSFrameRef *prv, const VSFrameRef *src,
556 const VSFrameRef *nxt, const VSAPI *vsapi, VSCore *core, int match, int field) {
557 if (match == 1) {
558 return vsapi->cloneFrameRef(src);
559 } else {
560 VSFrameRef *dst = vsapi->newVideoFrame(vsapi->getFrameFormat(src), vsapi->getFrameWidth(src, 0), vsapi->getFrameHeight(src, 0), src, core);
561
562 if (match == 0) {
563 copyField(dst, src, 1-field, vsapi);
564 copyField(dst, prv, field, vsapi);
565 } else if (match == 2) {
566 copyField(dst, src, 1-field, vsapi);
567 copyField(dst, nxt, field, vsapi);
568 } else if (match == 3) {
569 copyField(dst, src, field, vsapi);
570 copyField(dst, prv, 1-field, vsapi);
571 } else if (match == 4) {
572 copyField(dst, src, field, vsapi);
573 copyField(dst, nxt, 1-field, vsapi);
574 }
575
576 return dst;
577 }
578 }
579
580
checkmm(int m1,int m2,int * m1mic,int * m2mic,int * blockN,int MI,int field,int chroma,int cthresh,const VSFrameRef ** genFrames,const VSFrameRef * prv,const VSFrameRef * src,const VSFrameRef * nxt,VSFrameRef * cmask,int * cArray,int blockx,int blocky,const VSAPI * vsapi,VSCore * core)581 static int checkmm(int m1, int m2, int *m1mic, int *m2mic, int *blockN, int MI, int field, int chroma, int cthresh, const VSFrameRef **genFrames,
582 const VSFrameRef *prv, const VSFrameRef *src, const VSFrameRef *nxt, VSFrameRef *cmask, int *cArray, int blockx, int blocky, const VSAPI *vsapi, VSCore *core) {
583 if (*m1mic < 0) {
584 if (!genFrames[m1])
585 genFrames[m1] = createWeaveFrame(prv, src, nxt, vsapi, core, m1, field);
586 *m1mic = calcMI(genFrames[m1], vsapi, blockN, chroma, cthresh, cmask, cArray, blockx, blocky);
587 }
588
589 if (*m2mic < 0) {
590 if (!genFrames[m2])
591 genFrames[m2] = createWeaveFrame(prv, src, nxt, vsapi, core, m2, field);
592 *m2mic = calcMI(genFrames[m2], vsapi, blockN, chroma, cthresh, cmask, cArray, blockx, blocky);
593 }
594
595 if (((*m2mic)*3 < *m1mic || ((*m2mic)*2 < *m1mic && *m1mic > MI)) &&
596 abs(*m2mic-*m1mic) >= 30 && *m2mic < MI)
597 return m2;
598 else
599 return m1;
600 }
601
vfmInit(VSMap * in,VSMap * out,void ** instanceData,VSNode * node,VSCore * core,const VSAPI * vsapi)602 static void VS_CC vfmInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
603 VFMData *vfm = (VFMData *)*instanceData;
604 vsapi->setVideoInfo(vfm->vi, 1, node);
605 }
606
607 typedef enum {
608 mP = 0,
609 mC = 1,
610 mN = 2,
611 mB = 3,
612 mU = 4
613 } FMP;
614
615 typedef enum {
616 VSFieldBasedProgressive = 0,
617 VSFieldBasedBFF,
618 VSFieldBasedTFF
619 } VSFieldBased;
620
621 typedef enum {
622 VFMOrderBFF = 0,
623 VFMOrderTFF = 1
624 } VFMOrder;
625
626 typedef enum {
627 VFMFieldBottom = VFMOrderBFF,
628 VFMFieldTop = VFMOrderTFF,
629 VFMFieldSameAsOrder,
630 VFMFieldOppositeOfOrder
631 } VFMField;
632
vfmGetFrame(int n,int activationReason,void ** instanceData,void ** frameData,VSFrameContext * frameCtx,VSCore * core,const VSAPI * vsapi)633 static const VSFrameRef *VS_CC vfmGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
634 const VFMData *vfm = (const VFMData *)*instanceData;
635 n = VSMIN(vfm->vi->numFrames - 1, n);
636 if (activationReason == arInitial) {
637 if (n > 0) {
638 vsapi->requestFrameFilter(n-1, vfm->node, frameCtx);
639 if (vfm->clip2)
640 vsapi->requestFrameFilter(n-1, vfm->clip2, frameCtx);
641 }
642 vsapi->requestFrameFilter(n, vfm->node, frameCtx);
643 if (vfm->clip2)
644 vsapi->requestFrameFilter(n, vfm->clip2, frameCtx);
645 if (n < vfm->vi->numFrames - 1) {
646 vsapi->requestFrameFilter(n+1, vfm->node, frameCtx);
647 if (vfm->clip2)
648 vsapi->requestFrameFilter(n+1, vfm->clip2, frameCtx);
649 }
650 } else if (activationReason == arAllFramesReady) {
651 const VSFrameRef *prv = vsapi->getFrameFilter(n > 0 ? n-1 : 0, vfm->node, frameCtx);
652 const VSFrameRef *src = vsapi->getFrameFilter(n, vfm->node, frameCtx);
653 const VSFrameRef *nxt = vsapi->getFrameFilter(n < vfm->vi->numFrames - 1 ? n+1 : vfm->vi->numFrames - 1, vfm->node, frameCtx);
654 int mics[] = { -1,-1,-1,-1,-1 };
655
656 int order, field;
657 int missing;
658 const VSMap *props = vsapi->getFramePropsRO(src);
659 int fieldBased = int64ToIntS(vsapi->propGetInt(props, "_FieldBased", 0, &missing));
660 if (missing || (fieldBased != VSFieldBasedBFF && fieldBased != VSFieldBasedTFF))
661 order = vfm->order;
662 else
663 order = fieldBased - 1;
664
665 if (vfm->field == VFMFieldSameAsOrder)
666 field = order;
667 else if (vfm->field == VFMFieldOppositeOfOrder)
668 field = !order;
669 else
670 field = vfm->field;
671
672 // selected based on field^order
673 const int fxo0m[] = { 0, 1, 2, 3, 4 };
674 const int fxo1m[] = { 2, 1, 0, 4, 3};
675 const int *fxo = field ^ order ? fxo1m : fxo0m;
676 int match;
677 int i;
678 const VSFrameRef *genFrames[] = { NULL, NULL, NULL, NULL, NULL };
679 int blockN;
680 const VSFrameRef *dst1;
681 VSFrameRef *dst2;
682 VSMap *m;
683 int sc = 0;
684 const VSFormat *format = vsapi->getFrameFormat(src);
685 int width = vsapi->getFrameWidth(src, 0);
686 int height = vsapi->getFrameHeight(src, 0);
687
688 VSFrameRef *map = vsapi->newVideoFrame(format, width, height, NULL, core);
689 VSFrameRef *cmask = vsapi->newVideoFrame(format, width, height, NULL, core);
690
691 uint8_t *tbuffer = (uint8_t *)malloc((height>>1)*vfm->tpitchy*sizeof(uint8_t));
692 int *cArray = (int *)malloc((((width+vfm->blockx/2)/vfm->blockx)+1)*(((height+vfm->blocky/2)/vfm->blocky)+1)*4*sizeof(int));
693
694 // check if it's a scenechange so micmatch can be used
695 // only relevant for mm mode 1
696 if (vfm->micmatch == 1) {
697 sc = vsapi->propGetFloat(props, "VFMPlaneStatsDiff", 0, 0) > vfm->scthresh;
698
699 if (!sc) {
700 props = vsapi->getFramePropsRO(nxt);
701 sc = vsapi->propGetFloat(props, "VFMPlaneStatsDiff", 0, 0) > vfm->scthresh;
702 }
703 }
704
705 // p/c selection
706 match = compareFieldsSlow(prv, src, nxt, map, fxo[mC], fxo[mP], vfm->mchroma, field, vfm->y0, vfm->y1, tbuffer, vfm->tpitchy, vfm->tpitchuv, vsapi);
707 // the mode has 3-way p/c/n matches
708 if (vfm->mode >= 4)
709 match = compareFieldsSlow(prv, src, nxt, map, match, fxo[mN], vfm->mchroma, field, vfm->y0, vfm->y1, tbuffer, vfm->tpitchy, vfm->tpitchuv, vsapi);
710
711 genFrames[mC] = vsapi->cloneFrameRef(src);
712
713 // calculate all values for mic output, checkmm calculates and prepares it for the two matches if not already done
714 if (vfm->micout) {
715 checkmm(0, 1, &mics[0], &mics[1], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
716 checkmm(2, 3, &mics[2], &mics[3], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
717 checkmm(4, 0, &mics[4], &mics[0], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
718 }
719
720 // check the micmatches to see if one of the options are better
721 // here come the huge ass mode tables
722
723 if (vfm->micmatch == 2 || (sc && vfm->micmatch == 1)) {
724 // here comes the conditional hell to try to approximate mode 0-5 in tfm
725 if (vfm->mode == 0) {
726 // maybe not completely appropriate but go back and see if the discarded match is less sucky
727 match = checkmm(match, match == fxo[mP] ? fxo[mC] : fxo[mP], &mics[match], &mics[match == fxo[mP] ? fxo[mC] : fxo[mP]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
728 } else if (vfm->mode == 1) {
729 match = checkmm(match, fxo[mN], &mics[match], &mics[fxo[mN]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
730 } else if (vfm->mode == 2) {
731 match = checkmm(match, fxo[mU], &mics[match], &mics[fxo[mU]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
732 } else if (vfm->mode == 3) {
733 match = checkmm(match, fxo[mN], &mics[match], &mics[fxo[mN]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
734 match = checkmm(match, fxo[mU], &mics[match], &mics[fxo[mU]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
735 match = checkmm(match, fxo[mB], &mics[match], &mics[fxo[mB]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
736 } else if (vfm->mode == 4) {
737 // degenerate check because I'm lazy
738 match = checkmm(match, match == fxo[mP] ? fxo[mC] : fxo[mP], &mics[match], &mics[match == fxo[mP] ? fxo[mC] : fxo[mP]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky,vsapi, core);
739 } else if (vfm->mode == 5) {
740 match = checkmm(match, fxo[mU], &mics[match], &mics[fxo[mU]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
741 match = checkmm(match, fxo[mB], &mics[match], &mics[fxo[mB]], &blockN, vfm->mi, field, vfm->chroma, vfm->cthresh, genFrames, prv, src, nxt, cmask, &cArray[0], vfm->blockx, vfm->blocky, vsapi, core);
742 }
743 }
744
745 // Make sure mic is always calculated for selected match so _Combed will work
746 if (mics[match] < 0) {
747 if (!genFrames[match])
748 genFrames[match] = createWeaveFrame(prv, src, nxt, vsapi, core, match, field);
749 mics[match] = calcMI(genFrames[match], vsapi, &blockN, vfm->chroma, vfm->cthresh, cmask, cArray, vfm->blockx, vfm->blocky);
750 }
751
752 // Alternative clip handling
753 if (vfm->clip2) {
754 const VSFrameRef *prv2 = vsapi->getFrameFilter(n > 0 ? n-1 : 0, vfm->clip2, frameCtx);
755 const VSFrameRef *src2 = vsapi->getFrameFilter(n, vfm->clip2, frameCtx);
756 const VSFrameRef *nxt2 = vsapi->getFrameFilter(n < vfm->vi->numFrames - 1 ? n+1 : vfm->vi->numFrames - 1, vfm->clip2, frameCtx);
757 dst1 = createWeaveFrame(prv2, src2, nxt2, vsapi, core, match, field);
758 vsapi->freeFrame(prv2);
759 vsapi->freeFrame(src2);
760 vsapi->freeFrame(nxt2);
761 } else {
762 if (!genFrames[match])
763 genFrames[match] = createWeaveFrame(prv, src, nxt, vsapi, core, match, field);
764 dst1 = vsapi->cloneFrameRef(genFrames[match]);
765 }
766
767 vsapi->freeFrame(prv);
768 vsapi->freeFrame(src);
769 vsapi->freeFrame(nxt);
770
771 for (i = 0; i < 5; i++)
772 vsapi->freeFrame(genFrames[i]);
773
774 free(tbuffer);
775 free(cArray);
776 vsapi->freeFrame(map);
777 vsapi->freeFrame(cmask);
778
779 dst2 = vsapi->copyFrame(dst1, core);
780 vsapi->freeFrame(dst1);
781 m = vsapi->getFramePropsRW(dst2);
782 vsapi->propSetInt(m, "_FieldBased", 0, paReplace);
783 for (i = 0; i < 5; i++)
784 vsapi->propSetInt(m, "VFMMics", mics[i], i ? paAppend : paReplace);
785 vsapi->propSetInt(m, "_Combed", mics[match] >= vfm->mi, paReplace);
786 vsapi->propSetInt(m, "VFMMatch", match, paReplace);
787 vsapi->propSetInt(m, "VFMSceneChange", sc, paReplace);
788 return dst2;
789 }
790 return NULL;
791 }
792
vfmFree(void * instanceData,VSCore * core,const VSAPI * vsapi)793 static void VS_CC vfmFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
794 VFMData *vfm = (VFMData *)instanceData;
795 vsapi->freeNode(vfm->node);
796 vsapi->freeNode(vfm->clip2);
797 free(vfm);
798 }
799
invokePlaneDifference(VSNodeRef * node,VSCore * core,const VSAPI * vsapi)800 static VSMap *invokePlaneDifference(VSNodeRef *node, VSCore *core, const VSAPI *vsapi) {
801 VSNodeRef *node2;
802 VSMap *args, *ret;
803 const char *prop = "VFMPlaneStats";
804 VSPlugin *stdplugin = vsapi->getPluginById("com.vapoursynth.std", core);
805
806 args = vsapi->createMap();
807 vsapi->propSetNode(args, "clip", node, paAppend);
808 vsapi->propSetInt(args, "frames", 0, paAppend);
809 ret = vsapi->invoke(stdplugin, "DuplicateFrames", args);
810 if (vsapi->getError(ret)) {
811 vsapi->freeMap(args);
812 return ret;
813 }
814 node2 = vsapi->propGetNode(ret, "clip", 0, 0);
815 vsapi->freeMap(ret);
816 vsapi->clearMap(args);
817
818 vsapi->propSetNode(args, "clipa", node, paAppend);
819 vsapi->propSetNode(args, "clipb", node2, paAppend);
820 vsapi->freeNode(node2);
821 vsapi->propSetInt(args, "plane", 0, paAppend);
822 vsapi->propSetData(args, "prop", prop, -1, paAppend);
823 ret = vsapi->invoke(stdplugin, "PlaneStats", args);
824 if (vsapi->getError(ret)) {
825 vsapi->freeMap(args);
826 return ret;
827 }
828 node2 = vsapi->propGetNode(ret, "clip", 0, 0);
829 vsapi->freeMap(ret);
830 vsapi->clearMap(args);
831
832 vsapi->propSetNode(args, "clip", node2, paAppend);
833 vsapi->freeNode(node2);
834 ret = vsapi->invoke(stdplugin, "Cache", args);
835 vsapi->freeMap(args);
836 return ret;
837 }
838
prefix_string(const char * message,const char * prefix)839 static char *prefix_string(const char *message, const char *prefix) {
840 size_t message_length = strlen(message);
841 size_t prefix_length = strlen(prefix);
842 size_t length = message_length + prefix_length + 1;
843
844 char *result = (char *)malloc(length);
845
846 memcpy(result, prefix, prefix_length);
847 memcpy(result + prefix_length, message, message_length);
848
849 result[length - 1] = '\0';
850
851 return result;
852 }
853
createVFM(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)854 static void VS_CC createVFM(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
855 int err;
856 VFMData vfm;
857 VFMData *vfmd ;
858 const VSVideoInfo *vi;
859
860 vfm.order = int64ToIntS(vsapi->propGetInt(in, "order", 0, 0));
861 vfm.field = int64ToIntS(vsapi->propGetInt(in, "field", 0, &err));
862 if (err)
863 vfm.field = VFMFieldSameAsOrder;
864
865 vfm.mode = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
866 if (err)
867 vfm.mode = 1;
868 vfm.mchroma = !!vsapi->propGetInt(in, "mchroma", 0, &err);
869 if (err)
870 vfm.mchroma = 1;
871 vfm.cthresh = int64ToIntS(vsapi->propGetInt(in, "cthresh", 0, &err));
872 if (err)
873 vfm.cthresh = 9;
874 vfm.mi = int64ToIntS(vsapi->propGetInt(in, "mi", 0, &err));
875 if (err)
876 vfm.mi = 80;
877 vfm.chroma = !!vsapi->propGetInt(in, "chroma", 0, &err);
878 if (err)
879 vfm.chroma = 1;
880 vfm.blockx = int64ToIntS(vsapi->propGetInt(in, "blockx", 0, &err));
881 if (err)
882 vfm.blockx = 16;
883 vfm.blocky = int64ToIntS(vsapi->propGetInt(in, "blocky", 0, &err));
884 if (err)
885 vfm.blocky = 16;
886 vfm.y0 = int64ToIntS(vsapi->propGetInt(in, "y0", 0, &err));
887 if (err)
888 vfm.y0 = 16;
889 vfm.y1 = int64ToIntS(vsapi->propGetInt(in, "y1", 0, &err));
890 if (err)
891 vfm.y1 = 16;
892 vfm.scthresh = vsapi->propGetFloat(in, "scthresh", 0, &err);
893 if (err)
894 vfm.scthresh = 12.0;
895 vfm.micmatch = int64ToIntS(vsapi->propGetInt(in, "micmatch", 0, &err));
896 if (err)
897 vfm.micmatch = 1;
898 vfm.micout = !!vsapi->propGetInt(in, "micout", 0, &err);
899
900 if (vfm.order < VFMOrderBFF || vfm.order > VFMOrderTFF) {
901 vsapi->setError(out, "VFM: Invalid order specified; only 0-1 allowed");
902 return;
903 }
904
905 if (vfm.field < VFMFieldBottom || vfm.field > VFMFieldOppositeOfOrder) {
906 vsapi->setError(out, "VFM: Invalid field specified; only 0-3 allowed");
907 return;
908 }
909
910 if (vfm.mode < 0 || vfm.mode > 5) {
911 vsapi->setError(out, "VFM: Invalid mode specified, only 0-5 allowed");
912 return;
913 }
914
915 if (vfm.blockx < 4 || vfm.blockx > 512 || !isPowerOf2(vfm.blockx) || vfm.blocky < 4 || vfm.blocky > 512 || !isPowerOf2(vfm.blocky)) {
916 vsapi->setError(out, "VFM: invalid blocksize, must be between 4 and 512 and be a power of 2");
917 return;
918 }
919
920 if (vfm.mi < 0 || vfm.mi > vfm.blockx * vfm.blocky) {
921 vsapi->setError(out, "VFM: Invalid mi threshold specified");
922 return;
923 }
924
925 if (vfm.scthresh < 0 || vfm.scthresh > 100) {
926 vsapi->setError(out, "VFM: Invalid scthresh specified");
927 return;
928 }
929
930 if (vfm.cthresh < -1 || vfm.cthresh > 255) {
931 vsapi->setError(out, "VFM: invalid cthresh specified");
932 return;
933 }
934
935 if (vfm.micmatch < 0 || vfm.micmatch > 2) {
936 vsapi->setError(out, "VFM: invalid micmatch mode specified");
937 return;
938 }
939
940 vfm.node = vsapi->propGetNode(in, "clip", 0, 0);
941 vfm.clip2 = vsapi->propGetNode(in, "clip2", 0, &err);
942 vfm.vi = vsapi->getVideoInfo(vfm.clip2 ? vfm.clip2 : vfm.node);
943 vi = vsapi->getVideoInfo(vfm.node);
944
945 if (!isConstantFormat(vi) || (vi->format->id != pfYUV420P8 &&
946 vi->format->id != pfYUV422P8 &&
947 vi->format->id != pfYUV440P8 &&
948 vi->format->id != pfYUV444P8 &&
949 vi->format->id != pfGray8)) {
950 vsapi->setError(out, "VFM: input clip must be constant format YUV420P8, YUV422P8, YUV440P8, YUV444P8, or GRAY8");
951 vsapi->freeNode(vfm.node);
952 vsapi->freeNode(vfm.clip2);
953 return;
954 }
955
956 if (vi->numFrames != vfm.vi->numFrames || !isConstantFormat(vfm.vi)) {
957 vsapi->setError(out, "VFM: the number of frames must be the same in both input clips and clip2 must be constant format");
958 vsapi->freeNode(vfm.node);
959 vsapi->freeNode(vfm.clip2);
960 return;
961 }
962
963 if (vi->format->colorFamily == cmGray) {
964 vfm.chroma = 0;
965 vfm.mchroma = 0;
966 }
967
968 vfm.scthresh = vfm.scthresh / 100.0;
969
970 if (vfm.micmatch == 1) {
971 VSMap *ret = invokePlaneDifference(vfm.node, core, vsapi);
972 vsapi->freeNode(vfm.node);
973 const char *error = vsapi->getError(ret);
974 if (error) {
975 vsapi->freeMap(ret);
976 char *error2 = prefix_string(error, "VFM: ");
977 vsapi->setError(out, error2);
978 free(error2);
979 vsapi->freeNode(vfm.clip2);
980 return;
981 }
982 vfm.node = vsapi->propGetNode(ret, "clip", 0, 0);
983 vsapi->freeMap(ret);
984 }
985
986 vfm.tpitchy = (vi->width&15) ? vi->width+16-(vi->width&15) : vi->width;
987
988 int widthuv = vi->width >> vi->format->subSamplingW;
989 vfm.tpitchuv = (widthuv&15) ? widthuv+16-(widthuv&15) : widthuv;
990
991 vfmd = (VFMData *)malloc(sizeof(vfm));
992 *vfmd = vfm;
993 vsapi->createFilter(in, out, "VFM", vfmInit, vfmGetFrame, vfmFree, fmParallel, 0, vfmd, core);
994 }
995
996 // VDecimate
997
998 typedef struct {
999 int64_t num;
1000 int64_t den;
1001 } FrameDuration;
1002
1003 typedef struct {
1004 int64_t maxbdiff;
1005 int64_t totdiff;
1006 } VDInfo;
1007
1008 typedef struct CycleInfo {
1009 int num; // The number of the cycle's first frame divided by the cycle length.
1010 signed char drop; // Index of the frame to drop from the cycle.
1011 VDInfo *metrics; // Metrics for the input frames in the cycle.
1012 FrameDuration *durations; // Durations of the output frames in the cycle. Allocated only if !dryrun.
1013 } CycleInfo;
1014
1015 typedef struct CycleCache {
1016 CycleInfo *cycles;
1017 int size; // Number of cycles in the cache.
1018 } CycleCache;
1019
1020
1021 typedef struct {
1022 VSNodeRef *node;
1023 VSNodeRef *clip2;
1024 VSVideoInfo vi;
1025 int inCycle;
1026 int outCycle;
1027 int chroma;
1028 int tail;
1029 int inputNumFrames;
1030 int64_t dupthresh;
1031 int64_t scthresh;
1032 int blockx;
1033 int blocky;
1034 int nxblocks;
1035 int nyblocks;
1036 int bdiffsize;
1037 int64_t *bdiffs;
1038 const char *ovrfile;
1039 int dryrun;
1040 signed char *drop;
1041 CycleCache cache;
1042 } VDecimateData;
1043
1044
1045 static const signed char DropUnknown = -1;
1046 #define MaxCycleLength 25
1047
1048 #define STR(x) STR_(x)
1049 #define STR_(x) #x
1050
1051
initCache(CycleCache * cache,VDecimateData * vdm)1052 static void initCache(CycleCache *cache, VDecimateData *vdm) {
1053 cache->size = 200 / vdm->inCycle; // Cache 200 frames.
1054
1055 cache->cycles = (CycleInfo *)malloc(cache->size * sizeof(CycleInfo));
1056 memset(cache->cycles, 0, cache->size * sizeof(CycleInfo));
1057
1058 for (int i = 0; i < cache->size; i++) {
1059 CycleInfo *cycle = &cache->cycles[i];
1060
1061 cycle->num = cycle->drop = DropUnknown;
1062
1063 cycle->metrics = (VDInfo *)malloc(vdm->inCycle * sizeof(VDInfo));
1064 for (int j = 0; j < vdm->inCycle; j++)
1065 cycle->metrics[j].maxbdiff = cycle->metrics[j].totdiff = DropUnknown;
1066
1067 if (!vdm->dryrun) {
1068 cycle->durations = (FrameDuration *)malloc(vdm->outCycle * sizeof(FrameDuration));
1069 memset(cycle->durations, 0, vdm->outCycle * sizeof(FrameDuration));
1070 }
1071 }
1072 }
1073
freeCache(CycleCache * cache)1074 static void freeCache(CycleCache *cache) {
1075 if (!cache->cycles)
1076 return;
1077
1078 for (int i = 0; i < cache->size; i++) {
1079 CycleInfo *cycle = &cache->cycles[i];
1080
1081 free(cycle->metrics);
1082 if (cycle->durations)
1083 free(cycle->durations);
1084 }
1085
1086 free(cache->cycles);
1087 cache->cycles = NULL;
1088 cache->size = 0;
1089 }
1090
getCycleFromCache(int cycleNum,CycleCache * cache,VDecimateData * vdm)1091 CycleInfo *getCycleFromCache(int cycleNum, CycleCache *cache, VDecimateData *vdm) {
1092 int index = -1;
1093
1094 // Find the requested cycle.
1095 for (int i = 0; i < cache->size; i++) {
1096 if (cache->cycles[i].num == cycleNum) {
1097 index = i;
1098 break;
1099 }
1100 }
1101
1102 // The requested cycle was not found, so reuse the last cycle in the cache.
1103 if (index == -1)
1104 index = cache->size - 1;
1105
1106 // If the requested cycle is close to the end, move it to the top.
1107 if (index > cache->size / 3 * 2) {
1108 CycleInfo temp = cache->cycles[index];
1109 memmove(&cache->cycles[1], &cache->cycles[0], index * sizeof(CycleInfo));
1110 cache->cycles[0] = temp;
1111
1112 index = 0;
1113 }
1114
1115 CycleInfo *cycle = &cache->cycles[index];
1116
1117 // Reset it if needed.
1118 if (cycle->num != cycleNum) {
1119 cycle->num = cycleNum;
1120
1121 cycle->drop = DropUnknown;
1122
1123 for (int i = 0; i < vdm->inCycle; i++)
1124 cycle->metrics[i].maxbdiff = cycle->metrics[i].totdiff = DropUnknown;
1125 if (cycle->durations)
1126 memset(cycle->durations, 0, vdm->outCycle * sizeof(FrameDuration));
1127 }
1128
1129 return cycle;
1130 }
1131
calcMetric(const VSFrameRef * f1,const VSFrameRef * f2,int64_t * totdiff,VDecimateData * vdm,const VSAPI * vsapi)1132 static int64_t calcMetric(const VSFrameRef *f1, const VSFrameRef *f2, int64_t *totdiff, VDecimateData *vdm, const VSAPI *vsapi) {
1133 int64_t *bdiffs = vdm->bdiffs;
1134 int numplanes = vdm->chroma ? 3 : 1;
1135 int64_t maxdiff = -1;
1136 memset(bdiffs, 0, vdm->bdiffsize * sizeof(int64_t));
1137 for (int plane = 0; plane < numplanes; plane++) {
1138 int stride = vsapi->getStride(f1, plane);
1139 const uint8_t *f1p = vsapi->getReadPtr(f1, plane);
1140 const uint8_t *f2p = vsapi->getReadPtr(f2, plane);
1141 const VSFormat *fi = vsapi->getFrameFormat(f1);
1142
1143 int width = vsapi->getFrameWidth(f1, plane);
1144 int height = vsapi->getFrameHeight(f1, plane);
1145 int hblockx = vdm->blockx/2;
1146 int hblocky = vdm->blocky/2;
1147 int nxblocks = vdm->nxblocks;
1148 // adjust for subsampling
1149 if (plane > 0) {
1150 hblockx /= 1 << fi->subSamplingW;
1151 hblocky /= 1 << fi->subSamplingH;
1152 }
1153
1154 for (int y = 0; y < height; y++) {
1155 int ydest = y / hblocky;
1156 int xdest = 0;
1157 // some slight code duplication to not put an if statement for 8/16 bit processing in the inner loop
1158 if (fi->bitsPerSample == 8) {
1159 for (int x = 0; x < width; x+= hblockx) {
1160 int acc = 0;
1161 int m = VSMIN(width, x + hblockx);
1162 for (int xl = x; xl < m; xl++)
1163 acc += abs(f1p[xl] - f2p[xl]);
1164 bdiffs[ydest * nxblocks + xdest] += acc;
1165 xdest++;
1166 }
1167 } else {
1168 for (int x = 0; x < width; x+= hblockx) {
1169 int acc = 0;
1170 int m = VSMIN(width, x + hblockx);
1171 for (int xl = x; xl < m; xl++)
1172 acc += abs(((const uint16_t *)f1p)[xl] - ((const uint16_t *)f2p)[xl]);
1173 bdiffs[ydest * nxblocks + xdest] += acc;
1174 xdest++;
1175 }
1176 }
1177 f1p += stride;
1178 f2p += stride;
1179 }
1180 }
1181
1182 for (int i = 0; i < vdm->nyblocks - 1; i++) {
1183 for (int j = 0; j < vdm->nxblocks - 1; j++) {
1184 int64_t tmp = bdiffs[i * vdm->nxblocks + j] + bdiffs[i * vdm->nxblocks + j + 1] + bdiffs[(i + 1) * vdm->nxblocks + j] + bdiffs[(i + 1) * vdm->nxblocks + j + 1];
1185 if (tmp > maxdiff)
1186 maxdiff = tmp;
1187 }
1188 }
1189
1190 *totdiff = 0;
1191 for (int i = 0; i < vdm->bdiffsize; i++)
1192 *totdiff += bdiffs[i];
1193 return maxdiff;
1194 }
1195
vdecimateLoadOVR(const char * ovrfile,signed char * drop,int cycle,int numFrames,char * err,size_t errlen)1196 static int vdecimateLoadOVR(const char *ovrfile, signed char *drop, int cycle, int numFrames, char *err, size_t errlen) {
1197 int line = 0;
1198 char buf[80];
1199 char* pos;
1200 #ifdef _WIN32
1201 FILE* moo = NULL;
1202 int len, ret;
1203 wchar_t *ovrfile_wc;
1204 len = MultiByteToWideChar(CP_UTF8, 0, ovrfile, -1, NULL, 0);
1205 ovrfile_wc = malloc(len * sizeof(wchar_t));
1206 if (ovrfile_wc) {
1207 ret = MultiByteToWideChar(CP_UTF8, 0, ovrfile, -1, ovrfile_wc, len);
1208 if (ret == len)
1209 moo = _wfopen(ovrfile_wc, L"rb");
1210 free(ovrfile_wc);
1211 }
1212 #else
1213 FILE* moo = fopen(ovrfile, "r");
1214 #endif
1215 if (!moo) {
1216 snprintf(err, errlen, "VDecimate: can't open ovr file");
1217 return 1;
1218 }
1219
1220 memset(buf, 0, sizeof(buf));
1221 while (fgets(buf, 80, moo)) {
1222 int frame = -1;
1223 int frame_start = -1;
1224 int frame_end = -1;
1225
1226 signed char drop_char = 0;
1227 signed char drop_pattern[MaxCycleLength + 1] = { 0 };
1228 ptrdiff_t drop_pos = -1;
1229
1230 line++;
1231 pos = buf + strspn(buf, " \t\r\n");
1232
1233 if (pos[0] == '#' || pos[0] == 0) {
1234 continue;
1235 } else if (sscanf(pos, " %u, %u %" STR(MaxCycleLength) "s", &frame_start, &frame_end, drop_pattern) == 3) {
1236 signed char *tmp = (signed char *)strchr((const char *)drop_pattern, '-');
1237 if (tmp) {
1238 drop_pos = tmp - drop_pattern;
1239 }
1240 } else if (sscanf(pos, " %u %c", &frame, (char *)&drop_char) == 2) {
1241 ;
1242 } else {
1243 snprintf(err, errlen, "VDecimate: sscanf failed to parse override at line %d", line);
1244 fclose(moo);
1245 return 1;
1246 }
1247
1248 if (frame >= 0 && frame < numFrames && drop_char == '-') {
1249 if (drop[frame / cycle] < 0)
1250 drop[frame / cycle] = frame % cycle;
1251 } else if (frame_start >= 0 && frame_start < numFrames &&
1252 frame_end >= 0 && frame_end < numFrames &&
1253 frame_start < frame_end &&
1254 strlen((const char *)drop_pattern) == (size_t)cycle &&
1255 drop_pos > -1) {
1256 ptrdiff_t i;
1257 for (i = frame_start + drop_pos; i <= frame_end; i += cycle) {
1258 if (drop[i / cycle] < 0)
1259 drop[i / cycle] = (signed char)(i % cycle);
1260 }
1261 } else {
1262 snprintf(err, errlen, "VDecimate: Bad override at line %d in ovr", line);
1263 fclose(moo);
1264 return 1;
1265 }
1266
1267 while (buf[78] != 0 && buf[78] != '\n' && fgets(buf, 80, moo)) {
1268 ; // slurp the rest of a long line
1269 }
1270 }
1271
1272 fclose(moo);
1273 return 0;
1274 }
1275
vdecimateInit(VSMap * in,VSMap * out,void ** instanceData,VSNode * node,VSCore * core,const VSAPI * vsapi)1276 static void VS_CC vdecimateInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
1277 VDecimateData *vdm = (VDecimateData *)*instanceData;
1278 vsapi->setVideoInfo(&vdm->vi, 1, node);
1279 }
1280
findOutputFrame(int requestedFrame,int cycleStart,int outCycle,int drop,int dryrun)1281 static inline int findOutputFrame(int requestedFrame, int cycleStart, int outCycle, int drop, int dryrun) {
1282 if (dryrun)
1283 return requestedFrame;
1284 else {
1285 int outputFrame;
1286 int frameInOutputCycle = requestedFrame % outCycle;
1287 outputFrame = cycleStart + frameInOutputCycle;
1288 if (frameInOutputCycle >= drop)
1289 outputFrame++;
1290
1291 return outputFrame;
1292 }
1293 }
1294
findDropFrame(VDInfo * metrics,int cycleLength,int64_t scthresh,int64_t dupthresh)1295 static inline signed char findDropFrame(VDInfo *metrics, int cycleLength, int64_t scthresh, int64_t dupthresh) {
1296 int scpos = DropUnknown;
1297 int duppos = DropUnknown;
1298 int lowest = 0;
1299 signed char drop;
1300
1301 // make a decision
1302 // precalculate the position of the lowest dup metric frame
1303 // the last sc and the lowest dup, if any
1304
1305 for (int i = 0; i < cycleLength; i++) {
1306 if (metrics[i].totdiff > scthresh)
1307 scpos = i;
1308 if (metrics[i].maxbdiff < metrics[lowest].maxbdiff)
1309 lowest = i;
1310 }
1311
1312 if (metrics[lowest].maxbdiff < dupthresh)
1313 duppos = lowest;
1314
1315 // no dups so drop the frame right after the sc to keep things smooth
1316 if (scpos != DropUnknown && duppos == DropUnknown) {
1317 drop = scpos;
1318 } else {
1319 drop = lowest;
1320 }
1321
1322 return drop;
1323 }
1324
calculateNewDurations(const FrameDuration * oldDurations,FrameDuration * newDurations,int cycleLength,int drop)1325 static void calculateNewDurations(const FrameDuration *oldDurations, FrameDuration *newDurations, int cycleLength, int drop) {
1326 FrameDuration dropDuration = oldDurations[drop];
1327 FrameDuration cycleDuration = { .num = 0, .den = 1 };
1328 int missingDurations = 0;
1329
1330 for (int i = 0; i < cycleLength; i++)
1331 if (oldDurations[i].den == 0 || oldDurations[i].num == 0) {
1332 missingDurations = 1;
1333 break;
1334 }
1335
1336 if (missingDurations) {
1337 for (int i = 0; i < cycleLength - 1; i++)
1338 newDurations[i].num = newDurations[i].den = -1;
1339 return;
1340 }
1341
1342 for (int i = 0; i < cycleLength; i++) {
1343 if (i != drop) {
1344 vs_addRational(&cycleDuration.num, &cycleDuration.den, oldDurations[i].num, oldDurations[i].den);
1345 }
1346 }
1347
1348 for (int i = 0; i < cycleLength - 1; i++) {
1349 if (i == drop)
1350 oldDurations++;
1351
1352 // newDuration = oldDuration / cycleDuration * dropDuration + oldDuration
1353 *newDurations = *oldDurations;
1354 muldivRational(&newDurations->num, &newDurations->den, cycleDuration.den, cycleDuration.num);
1355 muldivRational(&newDurations->num, &newDurations->den, dropDuration.num, dropDuration.den);
1356 vs_addRational(&newDurations->num, &newDurations->den, oldDurations->num, oldDurations->den);
1357
1358 newDurations++;
1359 oldDurations++;
1360 }
1361 }
1362
getCycleBoundaries(int n,int * cyclestart,int * cycleend,VDecimateData * vdm)1363 static inline void getCycleBoundaries(int n, int *cyclestart, int *cycleend, VDecimateData *vdm) {
1364 *cyclestart = (n / vdm->outCycle) * vdm->inCycle;
1365 *cycleend = *cyclestart + vdm->inCycle;
1366 if (*cycleend > vdm->inputNumFrames)
1367 *cycleend = vdm->inputNumFrames;
1368 }
1369
vdecimateGetFrame(int n,int activationReason,void ** instanceData,void ** frameData,VSFrameContext * frameCtx,VSCore * core,const VSAPI * vsapi)1370 static const VSFrameRef *VS_CC vdecimateGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
1371 VDecimateData *vdm = (VDecimateData *)*instanceData;
1372
1373 if (activationReason == arInitial) {
1374 int cyclestart, cycleend;
1375
1376 getCycleBoundaries(n, &cyclestart, &cycleend, vdm);
1377
1378 CycleInfo *cycle = getCycleFromCache(cyclestart / vdm->inCycle, &vdm->cache, vdm);
1379
1380 if (vdm->drop && vdm->drop[cyclestart / vdm->inCycle] != DropUnknown)
1381 cycle->drop = vdm->drop[cyclestart / vdm->inCycle];
1382
1383 if (cycle->drop == DropUnknown || (vdm->dryrun && cycle->metrics[0].totdiff == DropUnknown)) {
1384 if (cyclestart > 0)
1385 vsapi->requestFrameFilter(cyclestart - 1, vdm->node, frameCtx);
1386
1387 for (int i = cyclestart; i < cycleend; i++) {
1388 vsapi->requestFrameFilter(i, vdm->node, frameCtx);
1389
1390 if (vdm->dryrun && vdm->clip2)
1391 vsapi->requestFrameFilter(i, vdm->clip2, frameCtx);
1392 }
1393 }
1394
1395 if (cycle->drop != DropUnknown) {
1396 int outputFrame = findOutputFrame(n, cyclestart, vdm->outCycle, cycle->drop, vdm->dryrun);
1397
1398 vsapi->requestFrameFilter(outputFrame, vdm->clip2 ? vdm->clip2 : vdm->node, frameCtx);
1399 }
1400
1401 if (!vdm->dryrun && cycle->durations[n % vdm->outCycle].den == 0)
1402 for (int i = cyclestart; i < cycleend; i++)
1403 vsapi->requestFrameFilter(i, vdm->clip2 ? vdm->clip2 : vdm->node, frameCtx);
1404
1405 return NULL;
1406 }
1407
1408 if (activationReason == arAllFramesReady) {
1409 int cyclestart, cycleend;
1410
1411 getCycleBoundaries(n, &cyclestart, &cycleend, vdm);
1412
1413 CycleInfo *cycle = getCycleFromCache(cyclestart / vdm->inCycle, &vdm->cache, vdm);
1414
1415 if (vdm->drop && vdm->drop[cyclestart / vdm->inCycle] != DropUnknown)
1416 cycle->drop = vdm->drop[cyclestart / vdm->inCycle];
1417
1418 if (cycle->drop == DropUnknown || (vdm->dryrun && cycle->metrics[0].totdiff == DropUnknown)) {
1419 // Calculate metrics
1420 for (int i = cyclestart; i < cycleend; i++) {
1421 const VSFrameRef *prv = vsapi->getFrameFilter(VSMAX(i - 1, 0), vdm->node, frameCtx);
1422 const VSFrameRef *cur = vsapi->getFrameFilter(i, vdm->node, frameCtx);
1423 cycle->metrics[i - cyclestart].maxbdiff = calcMetric(prv, cur, &cycle->metrics[i - cyclestart].totdiff, vdm, vsapi);
1424 vsapi->freeFrame(prv);
1425 vsapi->freeFrame(cur);
1426 }
1427
1428 // The first frame's metrics are always 0, thus it's always considered a duplicate.
1429 // Unless we do something about it.
1430 if (cyclestart == 0) {
1431 cycle->metrics[0].maxbdiff = cycle->metrics[1].maxbdiff;
1432 cycle->metrics[0].totdiff = vdm->scthresh + 1;
1433 }
1434
1435 if (cycle->drop == DropUnknown) {
1436 cycle->drop = findDropFrame(cycle->metrics, cycleend - cyclestart, vdm->scthresh, vdm->dupthresh);
1437 if (vdm->drop)
1438 vdm->drop[cyclestart / vdm->inCycle] = cycle->drop;
1439 }
1440 }
1441
1442 if (!vdm->dryrun && cycle->durations[n % vdm->outCycle].den == 0) {
1443 FrameDuration oldDurations[MaxCycleLength];
1444
1445 for (int i = cyclestart; i < cyclestart + vdm->inCycle; i++) {
1446 const VSFrameRef *frame = vsapi->getFrameFilter(i, vdm->clip2 ? vdm->clip2 : vdm->node, frameCtx);
1447 const VSMap *frameProps = vsapi->getFramePropsRO(frame);
1448 int err;
1449 oldDurations[i % vdm->inCycle].num = vsapi->propGetInt(frameProps, "_DurationNum", 0, &err);
1450 oldDurations[i % vdm->inCycle].den = vsapi->propGetInt(frameProps, "_DurationDen", 0, &err);
1451 vsapi->freeFrame(frame);
1452 }
1453
1454 calculateNewDurations(oldDurations, cycle->durations, vdm->inCycle, cycle->drop);
1455 }
1456
1457 int outputFrame = findOutputFrame(n, cyclestart, vdm->outCycle, cycle->drop, vdm->dryrun);
1458
1459 const VSFrameRef *src = vsapi->getFrameFilter(outputFrame, vdm->clip2 ? vdm->clip2 : vdm->node, frameCtx);
1460 VSFrameRef *dst = vsapi->copyFrame(src, core);
1461 vsapi->freeFrame(src);
1462 VSMap *dstProps = vsapi->getFramePropsRW(dst);
1463
1464 if (vdm->dryrun) {
1465 vsapi->propSetInt(dstProps, "VDecimateDrop", outputFrame % vdm->inCycle == cycle->drop, paReplace);
1466 vsapi->propSetInt(dstProps, "VDecimateTotalDiff", cycle->metrics[outputFrame % vdm->inCycle].totdiff, paReplace);
1467 vsapi->propSetInt(dstProps, "VDecimateMaxBlockDiff", cycle->metrics[outputFrame % vdm->inCycle].maxbdiff, paReplace);
1468 } else {
1469 if (cycle->durations[n % vdm->outCycle].den > 0) {
1470 vsapi->propSetInt(dstProps, "_DurationNum", cycle->durations[n % vdm->outCycle].num, paReplace);
1471 vsapi->propSetInt(dstProps, "_DurationDen", cycle->durations[n % vdm->outCycle].den, paReplace);
1472 }
1473 }
1474
1475 return dst;
1476 }
1477
1478 return NULL;
1479 }
1480
vdecimateFree(void * instanceData,VSCore * core,const VSAPI * vsapi)1481 static void VS_CC vdecimateFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
1482 VDecimateData *vdm = (VDecimateData *)instanceData;
1483 vsapi->freeNode(vdm->node);
1484 vsapi->freeNode(vdm->clip2);
1485 free(vdm->bdiffs);
1486 if (vdm->drop)
1487 free(vdm->drop);
1488 freeCache(&vdm->cache);
1489 free(vdm);
1490 }
1491
createVDecimate(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)1492 static void VS_CC createVDecimate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
1493 VDecimateData vdm;
1494 memset(&vdm, 0, sizeof(vdm));
1495 int err;
1496
1497 vdm.inCycle = int64ToIntS(vsapi->propGetInt(in, "cycle", 0, &err));
1498 if (err)
1499 vdm.inCycle = 5;
1500 vdm.blockx = int64ToIntS(vsapi->propGetInt(in, "blockx", 0, &err));
1501 if (err)
1502 vdm.blockx = 32;
1503 vdm.blocky = int64ToIntS(vsapi->propGetInt(in, "blocky", 0, &err));
1504 if (err)
1505 vdm.blocky = 32;
1506 double dupthresh = vsapi->propGetFloat(in, "dupthresh", 0, &err);
1507 if (err)
1508 dupthresh = 1.1;
1509 double scthresh = vsapi->propGetFloat(in, "scthresh", 0, &err);
1510 if (err)
1511 scthresh = 15.0;
1512
1513 if (vdm.inCycle < 2 || vdm.inCycle > MaxCycleLength) {
1514 vsapi->setError(out, "VDecimate: Invalid cycle size specified");
1515 return;
1516 }
1517
1518 if (vdm.blockx < 4 || vdm.blockx > 512 || !isPowerOf2(vdm.blockx) || vdm.blocky < 4 || vdm.blocky > 512 || !isPowerOf2(vdm.blocky)) {
1519 vsapi->setError(out, "VDecimate: invalid blocksize, must be between 4 and 512 and be a power of 2");
1520 return;
1521 }
1522
1523 if (dupthresh < 0 || dupthresh > 100) {
1524 vsapi->setError(out, "VDecimate: invalid dupthresh specified");
1525 return;
1526 }
1527
1528 if (scthresh < 0 || scthresh > 100) {
1529 vsapi->setError(out, "VDecimate: invalid scthresh specified");
1530 return;
1531 }
1532
1533 vdm.node = vsapi->propGetNode(in, "clip", 0, 0);
1534 vdm.clip2 = vsapi->propGetNode(in, "clip2", 0, &err);
1535 vdm.vi = *vsapi->getVideoInfo(vdm.clip2 ? vdm.clip2 : vdm.node);
1536
1537 const VSVideoInfo *vi = vsapi->getVideoInfo(vdm.node);
1538 if (!isConstantFormat(vi) || vi->format->bitsPerSample > 16 || vi->format->sampleType != stInteger) {
1539 vsapi->setError(out, "VDecimate: input clip must be constant format, with 8..16 bits per sample");
1540 vsapi->freeNode(vdm.node);
1541 vsapi->freeNode(vdm.clip2);
1542 return;
1543 }
1544
1545 if (vi->numFrames != vdm.vi.numFrames) {
1546 vsapi->setError(out, "VDecimate: the number of frames must be the same in both input clips");
1547 vsapi->freeNode(vdm.node);
1548 vsapi->freeNode(vdm.clip2);
1549 return;
1550 }
1551
1552 vdm.chroma = !!vsapi->propGetInt(in, "chroma", 0, &err);
1553 if (err)
1554 vdm.chroma = vi->format->colorFamily != cmGray;
1555 else {
1556 if (vdm.chroma && vi->format->colorFamily == cmGray) {
1557 vsapi->setError(out, "VDecimate: it makes no sense to enable chroma when the input clip is grayscale");
1558 vsapi->freeNode(vdm.node);
1559 vsapi->freeNode(vdm.clip2);
1560 return;
1561 } else if (!vdm.chroma && vi->format->colorFamily == cmRGB) {
1562 vsapi->setError(out, "VDecimate: it makes no sense to disable chroma when the input clip is RGB");
1563 vsapi->freeNode(vdm.node);
1564 vsapi->freeNode(vdm.clip2);
1565 return;
1566 }
1567 }
1568
1569 vdm.ovrfile = vsapi->propGetData(in, "ovr", 0, &err);
1570
1571 vdm.dryrun = !!vsapi->propGetInt(in, "dryrun", 0, &err);
1572
1573 int max_value = (1 << vi->format->bitsPerSample) - 1;
1574 // Casting max_value to int64_t to avoid losing the high 32 bits of the result
1575 vdm.scthresh = (int64_t)(((int64_t)max_value * vi->width * vi->height * scthresh)/100);
1576 vdm.dupthresh = (int64_t)((max_value * vdm.blockx * vdm.blocky * dupthresh)/100);
1577
1578 vdm.nxblocks = (vdm.vi.width + vdm.blockx/2 - 1)/(vdm.blockx/2);
1579 vdm.nyblocks = (vdm.vi.height + vdm.blocky/2 - 1)/(vdm.blocky/2);
1580 vdm.bdiffsize = vdm.nxblocks * vdm.nyblocks;
1581 vdm.bdiffs = (int64_t *)malloc(vdm.bdiffsize * sizeof(int64_t));
1582
1583 if (vdm.ovrfile) {
1584 vdm.drop = (signed char *)malloc(vdm.vi.numFrames / vdm.inCycle + 1);
1585 memset(vdm.drop, DropUnknown, vdm.vi.numFrames / vdm.inCycle + 1);
1586
1587 char err2[80];
1588
1589 if (vdecimateLoadOVR(vdm.ovrfile, vdm.drop, vdm.inCycle, vdm.vi.numFrames, err2, sizeof(err2))) {
1590 free(vdm.drop);
1591 free(vdm.bdiffs);
1592 vsapi->freeNode(vdm.node);
1593 vsapi->freeNode(vdm.clip2);
1594 vsapi->setError(out, err2);
1595 return;
1596 }
1597 }
1598
1599 if (vdm.dryrun)
1600 vdm.outCycle = vdm.inCycle;
1601 else
1602 vdm.outCycle = vdm.inCycle - 1;
1603
1604 vdm.inputNumFrames = vdm.vi.numFrames;
1605 if (!vdm.dryrun) {
1606 vdm.tail = vdm.vi.numFrames % vdm.inCycle;
1607 vdm.vi.numFrames /= vdm.inCycle;
1608 vdm.vi.numFrames *= vdm.outCycle;
1609 vdm.vi.numFrames += vdm.tail;
1610 if (vdm.vi.fpsNum && vdm.vi.fpsDen)
1611 muldivRational(&vdm.vi.fpsNum, &vdm.vi.fpsDen, vdm.outCycle, vdm.inCycle);
1612 }
1613
1614 initCache(&vdm.cache, &vdm);
1615
1616 VDecimateData *d = (VDecimateData *)malloc(sizeof(vdm));
1617 *d = vdm;
1618 vsapi->createFilter(in, out, "VDecimate", vdecimateInit, vdecimateGetFrame, vdecimateFree, fmUnordered, 0, d, core);
1619 }
1620
1621 // Needed to silence warnings
1622 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin);
1623
VapourSynthPluginInit(VSConfigPlugin configFunc,VSRegisterFunction registerFunc,VSPlugin * plugin)1624 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) {
1625 configFunc("org.ivtc.v", "vivtc", "VFM", VAPOURSYNTH_API_VERSION, 1, plugin);
1626 // add ovr support
1627 registerFunc("VFM",
1628 "clip:clip;"
1629 "order:int;"
1630 "field:int:opt;"
1631 "mode:int:opt;"
1632 "mchroma:int:opt;"
1633 "cthresh:int:opt;"
1634 "mi:int:opt;"
1635 "chroma:int:opt;"
1636 "blockx:int:opt;"
1637 "blocky:int:opt;"
1638 "y0:int:opt;"
1639 "y1:int:opt;"
1640 "scthresh:float:opt;"
1641 "micmatch:int:opt;"
1642 "micout:int:opt;"
1643 "clip2:clip:opt;"
1644 , createVFM, NULL, plugin);
1645 registerFunc("VDecimate",
1646 "clip:clip;"
1647 "cycle:int:opt;"
1648 "chroma:int:opt;"
1649 "dupthresh:float:opt;"
1650 "scthresh:float:opt;"
1651 "blockx:int:opt;"
1652 "blocky:int:opt;"
1653 "clip2:clip:opt;"
1654 "ovr:data:opt;"
1655 "dryrun:int:opt;"
1656 , createVDecimate, NULL, plugin);
1657 }
1658