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