GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 5.7% 20 / 0 / 348
Functions: 11.1% 3 / 0 / 27
Branches: 3.2% 23 / 0 / 714

filters/fps.cpp
Line Branch Exec Source
1 // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
2 // http://avisynth.nl
3
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
17 // http://www.gnu.org/copyleft/gpl.html .
18 //
19 // Linking Avisynth statically or dynamically with other modules is making a
20 // combined work based on Avisynth. Thus, the terms and conditions of the GNU
21 // General Public License cover the whole combination.
22 //
23 // As a special exception, the copyright holders of Avisynth give you
24 // permission to link Avisynth with independent modules that communicate with
25 // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
26 // terms of these independent modules, and to copy and distribute the
27 // resulting combined work under terms of your choice, provided that
28 // every copy of the combined work is accompanied by a complete copy of
29 // the source code of Avisynth (the version of Avisynth used to produce the
30 // combined work), being distributed under the terms of the GNU General
31 // Public License plus this exception. An independent module is a module
32 // which is not derived from or based on Avisynth, such as 3rd-party filters,
33 // import and export plugins, or graphical user interfaces.
34
35
36 #include "fps.h"
37
38 #ifdef AVS_WINDOWS
39 #include <avs/win.h>
40 #else
41 #include <avs/posix.h>
42 #endif
43
44 #include <avs/minmax.h>
45 #include "../core/bitblt.h"
46 #include "../core/internal.h"
47 #include "merge.h"
48 #include <cmath>
49
50
51
52 /********************************************************************
53 ***** Declare index of new filters for Avisynth's filter engine *****
54 ********************************************************************/
55
56 extern const AVSFunction Fps_filters[] = {
57 { "AssumeScaledFPS", BUILTIN_FUNC_PREFIX, "c[multiplier]i[divisor]i[sync_audio]b", AssumeScaledFPS::Create },
58
59 { "AssumeFPS", BUILTIN_FUNC_PREFIX, "ci[]i[sync_audio]b", AssumeFPS::Create }, // dst framerate, sync audio?
60 { "AssumeFPS", BUILTIN_FUNC_PREFIX, "cf[sync_audio]b", AssumeFPS::CreateFloat }, // dst framerate, sync audio?
61 { "AssumeFPS", BUILTIN_FUNC_PREFIX, "cs[sync_audio]b", AssumeFPS::CreatePreset }, // dst framerate, sync audio?
62 { "AssumeFPS", BUILTIN_FUNC_PREFIX, "cc[sync_audio]b", AssumeFPS::CreateFromClip }, // clip with dst framerate, sync audio?
63
64 { "ChangeFPS", BUILTIN_FUNC_PREFIX, "ci[]i[linear]b", ChangeFPS::Create }, // dst framerate, fetch all frames
65 { "ChangeFPS", BUILTIN_FUNC_PREFIX, "cf[linear]b", ChangeFPS::CreateFloat }, // dst framerate, fetch all frames
66 { "ChangeFPS", BUILTIN_FUNC_PREFIX, "cs[linear]b", ChangeFPS::CreatePreset }, // dst framerate, fetch all frames
67 { "ChangeFPS", BUILTIN_FUNC_PREFIX, "cc[linear]b", ChangeFPS::CreateFromClip },// clip with dst framerate, fetch all frames
68
69 { "ConvertFPS", BUILTIN_FUNC_PREFIX, "ci[]i[zone]i[vbi]i", ConvertFPS::Create }, // dst framerate, zone lines, vbi lines
70 { "ConvertFPS", BUILTIN_FUNC_PREFIX, "cf[zone]i[vbi]i", ConvertFPS::CreateFloat }, // dst framerate, zone lines, vbi lines
71 { "ConvertFPS", BUILTIN_FUNC_PREFIX, "cs[zone]i[vbi]i", ConvertFPS::CreatePreset }, // dst framerate, zone lines, vbi lines
72 { "ConvertFPS", BUILTIN_FUNC_PREFIX, "cc[zone]i[vbi]i", ConvertFPS::CreateFromClip }, // clip with dst framerate, zone lines, vbi lines
73
74 { "ContinuedDenominator", BUILTIN_FUNC_PREFIX, "f[]i[limit]i", ContinuedCreate, (void*)0 },
75 { "ContinuedNumerator", BUILTIN_FUNC_PREFIX, "f[]i[limit]i", ContinuedCreate, (void*)1 },
76
77 { NULL }
78 };
79
80
81
82 /*********************************************
83 ****** Float and Rational utility ******
84 *********************************************
85
86 *********************************************
87 ****** Thanks to RAYMOD2 for his help *****
88 ****** in beating continued fractions *****
89 ****** into a usable form. IanB *****
90 *********************************************/
91
92 // This function converts a 32-bit IEEE float into a fraction. This
93 // process is lossless but it may fail for very large or very small
94 // floats. It also discards the sign bit. Since the denominator will
95 // always be a power of 2 and the numerator will always be odd (except
96 // when the denominator is 1) the fraction will already be reduced
97 // to its lowest terms. output range (2^32-2^(32-24))/1 -- (1/2^31)
98 // i.e. 4294967040/1 -- 1/2147483648 (4.65661287307e-10)
99 // returns true if input is out of range
100
101 static bool float_to_frac(float input, unsigned &num, unsigned &den)
102 {
103 union { float f; unsigned i; } value;
104 uint32_t mantissa;
105 int exponent;
106
107 // Bit strip the float
108 value.f = input;
109 mantissa = (value.i & 0x7FFFFF) + 0x800000; // add implicit bit on the left
110 exponent = ((value.i & 0x7F800000) >> 23) - 127 - 23; // remove decimal pt
111
112 // minimize the mantissa by removing any trailing 0's
113 while (!(mantissa & 1)) { mantissa >>= 1; exponent += 1; }
114
115 // if too small try to pull result from the reciprocal
116 if (exponent < -31) {
117 return float_to_frac((float)(1.0/input), den, num);
118 }
119
120 // if exponent is too large try removing leading 0's of mantissa
121 while( (exponent > 0) && !(mantissa & 0x80000000) ) {
122 mantissa <<= 1; exponent -= 1;
123 }
124 if (exponent > 0) { // number too large
125 num = 0xffffffff;
126 den = 1;
127 return true; // Out of range!
128 }
129 num = mantissa;
130 den = 1 << (-exponent);
131
132 return false;
133 }
134
135
136 // This function uses continued fractions to find the rational
137 // approximation such that the result truncated to a float is
138 // equal to value. The semiconvergents for the smallest such
139 // rational pair is then returned. The algorithm is modified
140 // from Wikipedia, Continued Fractions.
141
142 static bool reduce_float(float value, unsigned &num, unsigned &den)
143 {
144 if (float_to_frac(value, num, den)) {
145 return true;
146 }
147
148 uint32_t n0 = 0, n1 = 1, n2, nx = num; // numerators
149 uint32_t d0 = 1, d1 = 0, d2, dx = den; // denominators
150 uint32_t a2, ax, amin; // integer parts of quotients
151 uint32_t f1 = 0, f2; // fractional parts of quotients
152
153 for (;;) // calculate convergents
154 {
155 a2 = nx / dx;
156 f2 = nx % dx;
157 n2 = n0 + n1 * a2;
158 d2 = d0 + d1 * a2;
159
160 if (f2 == 0) break; // no more convergents (n2 / d2 == input)
161
162 // Damn compiler does not correctly cast
163 // to intermediate results to float
164 float f = (float)((double)n2/d2);
165 if (f == value) break;
166
167 n0 = n1; n1 = n2;
168 d0 = d1; d1 = d2;
169 nx = dx; dx = f1 = f2;
170 }
171 if (d2 == 1)
172 {
173 num = n2;
174 den = d2;
175 }
176 else { // we have been through the loop at least twice
177
178 if ((a2 % 2 == 0) && (d0 * f1 > f2 * d1))
179 amin = a2 / 2; // passed 1/2 a_k admissibility test
180 else
181 amin = a2 / 2 + 1;
182
183 // find the sign of the error (actual + error = n2/d2) and then
184 // set (n2/d2) = (num/den + sign * epsilon) = R2 and solve for ax
185 //--------------------
186 // n2 = n0 + n1 * ax
187 // d2 = d0 + d1 * ax
188 //-----------------------------------------------
189 // (n2/d2) = R2 = (n0 + n1 * ax)/(d0 + d1 * ax)
190 // n0 + n1 * ax = R2 * (d0 + d1 * ax)
191 // n0 + n1 * ax = R2 * d0 + R2 * d1 * ax
192 // R2 * d1 * ax - n1 * ax = n0 - R2 * d0
193 // (R2 * d1 - n1) * ax = n0 - R2 * d0
194 // ax = (n0 - R2 * d0)/(R2 * d1 - n1)
195
196 // bump float to adjacent float value
197 union { float f; uint32_t i; } eps; eps.f = value;
198 if (UInt32x32To64(n1, den) > UInt32x32To64(num, d1)) {
199 eps.i -= 1;
200 } else {
201 eps.i += 1;
202 }
203 double r2 = eps.f;
204 r2 += value;
205 r2 /= 2;
206
207 double yn = n0 - r2*d0;
208 double yd = r2*d1 - n1;
209 ax = (uint32_t)((yn + yd)/yd); // ceiling value
210
211 if (ax < amin) ax = amin;
212
213 // calculate nicest semiconvergent
214 num = n0 + n1 * ax;
215 den = d0 + d1 * ax;
216 }
217 return false;
218 }
219
220
221 // This function uses continued fractions to find the best rational
222 // approximation that satisfies (denom <= limit). The algorithm
223 // is from Wikipedia, Continued Fractions.
224 //
225 static void reduce_frac(uint32_t &num, uint32_t &den, uint32_t limit)
226 {
227 uint32_t n0 = 0, n1 = 1, n2, nx = num; // numerators
228 uint32_t d0 = 1, d1 = 0, d2, dx = den; // denominators
229 uint32_t a2, ax, amin; // integer parts of quotients
230 uint32_t f1 = 0, f2; // fractional parts of quotients
231 int i = 0; // number of loop iterations
232
233 for (;;) { // calculate convergents
234 a2 = nx / dx;
235 f2 = nx % dx;
236 n2 = n0 + n1 * a2;
237 d2 = d0 + d1 * a2;
238
239 if (f2 == 0) break;
240 if ((i++) && (d2 >= limit)) break;
241
242 n0 = n1; n1 = n2;
243 d0 = d1; d1 = d2;
244 nx = dx; dx = f1 = f2;
245 }
246 if (d2 <= limit)
247 {
248 num = n2; den = d2; // use last convergent
249 }
250 else { // (d2 > limit)
251 // d2 = d0 + d1 * ax
252 // d1 * ax = d2 - d1
253 ax = (limit - d0) / d1; // set d2 = limit and solve for a2
254
255 if ((a2 % 2 == 0) && (d0 * f1 > f2 * d1))
256 amin = a2 / 2; // passed 1/2 a_k admissibility test
257 else
258 amin = a2 / 2 + 1;
259
260 if (ax < amin) {
261 // use previous convergent
262 num = n1;
263 den = d1;
264 }
265 else {
266 // calculate best semiconvergent
267 num = n0 + n1 * ax;
268 den = d0 + d1 * ax;
269 }
270 }
271 }
272
273
274 AVSValue __cdecl ContinuedCreate(AVSValue args, void* key, IScriptEnvironment* env)
275 {
276 uint32_t num, den;
277
278 if (args[1].IsInt()) { // num, den[, limit] form
279 if (args[0].IsInt()) {
280 num = args[0].AsInt();
281 } else { // IsFloat
282 num = (uint32_t)args[0].AsFloat();
283 if ((float)num != args[0].AsFloatf()) {
284 env->ThrowError("ContinuedFraction: Numerator must be an integer.\n");
285 }
286 }
287 den = args[1].AsInt();
288 reduce_frac(num, den, (uint32_t)args[2].AsInt(1001));
289 } else { // float[, limit] form
290 if (args[2].IsInt()) {
291 if (float_to_frac(args[0].AsFloatf(), num, den)) {
292 env->ThrowError("ContinuedFraction: Float value out of range for rational pair.\n");
293 }
294 reduce_frac(num, den, (uint32_t)args[2].AsInt());
295 } else {
296 if (reduce_float(args[0].AsFloatf(), num, den)) {
297 env->ThrowError("ContinuedFraction: Float value out of range for rational pair.\n");
298 }
299 }
300 }
301 return AVSValue((int)(key ? num : den));
302 }
303
304
305 /***************************************
306 ******* Float to FPS utility ******
307 ***************************************/
308
309 void FloatToFPS(const char *name, float n, uint32_t &num, uint32_t &den, IScriptEnvironment* env)
310 {
311 if (n <= 0)
312 env->ThrowError("%s: FPS must be greater then 0.\n", name);
313
314 float x;
315 uint32_t u = (uint32_t)(n*1001+0.5);
316
317 // Check for the 30000/1001 multiples
318 x = (float)((u/30000*30000)/1001.0);
319 if (x == n) { num = u; den= 1001; return; }
320
321 // Check for the 24000/1001 multiples
322 x = (float)((u/24000*24000)/1001.0);
323 if (x == n) { num = u; den= 1001; return; }
324
325 if (n < 14.986) {
326 // Check for the 30000/1001 factors
327 u = (uint32_t)(30000/n+0.5);
328 x = (float)(30000.0/(u/1001*1001));
329 if (x == n) { num = 30000; den= u; return; }
330
331 // Check for the 24000/1001 factors
332 u = (uint32_t)(24000/n+0.5);
333 x = (float)(24000.0/(u/1001*1001));
334 if (x == n) { num = 24000; den= u; return; }
335 }
336
337 // Find the rational pair with the smallest denominator
338 // that is equal to n within the accuracy of an IEEE float.
339 if (reduce_float(n, num, den))
340 env->ThrowError("%s: FPS value is out of range.\n", name);
341
342 }
343
344
345 /****************************************
346 ******* Preset to FPS utility ****** -- Tritcal, IanB Jan 2006
347 ****************************************/
348
349 void PresetToFPS(const char *name, const char *p, uint32_t &num, uint32_t &den, IScriptEnvironment* env)
350 {
351 if (lstrcmpi(p, "ntsc_film" ) == 0) { num = 24000; den = 1001; }
352 else if (lstrcmpi(p, "ntsc_video" ) == 0) { num = 30000; den = 1001; }
353 else if (lstrcmpi(p, "ntsc_double" ) == 0) { num = 60000; den = 1001; }
354 else if (lstrcmpi(p, "ntsc_quad" ) == 0) { num =120000; den = 1001; }
355
356 else if (lstrcmpi(p, "ntsc_round_film" ) == 0) { num = 2997; den = 125; }
357 else if (lstrcmpi(p, "ntsc_round_video" ) == 0) { num = 2997; den = 100; }
358 else if (lstrcmpi(p, "ntsc_round_double") == 0) { num = 2997; den = 50; }
359 else if (lstrcmpi(p, "ntsc_round_quad" ) == 0) { num = 2997; den = 25; }
360
361 else if (lstrcmpi(p, "film" ) == 0) { num = 24; den = 1; }
362
363 else if (lstrcmpi(p, "pal_film" ) == 0) { num = 25; den = 1; }
364 else if (lstrcmpi(p, "pal_video" ) == 0) { num = 25; den = 1; }
365 else if (lstrcmpi(p, "pal_double" ) == 0) { num = 50; den = 1; }
366 else if (lstrcmpi(p, "pal_quad" ) == 0) { num = 100; den = 1; }
367
368 else if (lstrcmpi(p, "drop24" ) == 0) { num = 24000; den = 1001; }
369 else if (lstrcmpi(p, "drop30" ) == 0) { num = 30000; den = 1001; }
370 else if (lstrcmpi(p, "drop60" ) == 0) { num = 60000; den = 1001; }
371 else if (lstrcmpi(p, "drop120" ) == 0) { num =120000; den = 1001; }
372 /*
373 else if (lstrcmpi(p, "drop25" ) == 0) { num = 25000; den = 1001; }
374 else if (lstrcmpi(p, "drop50" ) == 0) { num = 50000; den = 1001; }
375 else if (lstrcmpi(p, "drop100" ) == 0) { num =100000; den = 1001; }
376
377 else if (lstrcmpi(p, "nondrop24" ) == 0) { num = 24; den = 1; }
378 else if (lstrcmpi(p, "nondrop25" ) == 0) { num = 25; den = 1; }
379 else if (lstrcmpi(p, "nondrop30" ) == 0) { num = 30; den = 1; }
380 else if (lstrcmpi(p, "nondrop50" ) == 0) { num = 50; den = 1; }
381 else if (lstrcmpi(p, "nondrop60" ) == 0) { num = 60; den = 1; }
382 else if (lstrcmpi(p, "nondrop100" ) == 0) { num = 100; den = 1; }
383 else if (lstrcmpi(p, "nondrop120" ) == 0) { num = 120; den = 1; }
384
385 else if (lstrcmpi(p, "23.976" ) == 0) { num = 24000; den = 1001; }
386 else if (lstrcmpi(p, "23.976!" ) == 0) { num = 2997; den = 125; }
387 else if (lstrcmpi(p, "24.0" ) == 0) { num = 24; den = 1; }
388 else if (lstrcmpi(p, "25.0" ) == 0) { num = 25; den = 1; }
389 else if (lstrcmpi(p, "29.97" ) == 0) { num = 30000; den = 1001; }
390 else if (lstrcmpi(p, "29.97!" ) == 0) { num = 2997; den = 100; }
391 else if (lstrcmpi(p, "30.0" ) == 0) { num = 30; den = 1; }
392 else if (lstrcmpi(p, "59.94" ) == 0) { num = 60000; den = 1001; }
393 else if (lstrcmpi(p, "59.94!" ) == 0) { num = 2997; den = 50; }
394 else if (lstrcmpi(p, "60.0" ) == 0) { num = 60; den = 1; }
395 else if (lstrcmpi(p, "100.0" ) == 0) { num = 100; den = 1; }
396 else if (lstrcmpi(p, "119.88" ) == 0) { num =120000; den = 1001; }
397 else if (lstrcmpi(p, "119.88!" ) == 0) { num = 2997; den = 25; }
398 else if (lstrcmpi(p, "120.0" ) == 0) { num = 120; den = 1; }
399 */
400 else {
401 env->ThrowError("%s: invalid preset value used.\n", name);
402 }
403 }
404
405
406
407 /******************************************
408 ******* AssumeScaledFPS Filters ******
409 ******************************************/
410
411 1 AssumeScaledFPS::AssumeScaledFPS(PClip _child, int multiplier, int divisor, bool sync_audio, IScriptEnvironment* env)
412
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 15 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 13 not taken.
1 : NonCachedGenericVideoFilter(_child)
413 {
414
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1 time.
1 if (divisor <= 0)
415 env->ThrowError("AssumeScaledFPS: Divisor must be positive.");
416
417
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (multiplier <= 0)
418 env->ThrowError("AssumeScaledFPS: Multiplier must be positive.");
419
420
1/2
✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 11 not taken.
1 if (sync_audio)
421 {
422 1 vi.audio_samples_per_second = MulDiv(vi.audio_samples_per_second, multiplier, divisor);
423 }
424
1/2
✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 16 not taken.
1 vi.MulDivFPS((uint32_t)multiplier, (uint32_t)divisor);
425 1 }
426
427
428 AVSValue __cdecl AssumeScaledFPS::Create(AVSValue args, void*, IScriptEnvironment* env)
429 {
430 return new AssumeScaledFPS( args[0].AsClip(), args[1].AsInt(1),
431 args[2].AsInt(1), args[3].AsBool(false), env );
432 }
433
434
435
436
437 /************************************
438 ******* AssumeFPS Filters ******
439 ************************************/
440
441 3 AssumeFPS::AssumeFPS(PClip _child, unsigned numerator, unsigned denominator, bool sync_audio, IScriptEnvironment* env)
442
2/4
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 13 not taken.
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 11 not taken.
3 : NonCachedGenericVideoFilter(_child)
443 {
444
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
3 if (denominator == 0)
445 env->ThrowError("AssumeFPS: Denominator cannot be 0 (zero).");
446
447
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 9 taken 2 times.
3 if (sync_audio)
448 {
449 1 int64_t a = int64_t(vi.fps_numerator) * denominator;
450 1 int64_t b = int64_t(vi.fps_denominator) * numerator;
451 1 vi.audio_samples_per_second = int((vi.audio_samples_per_second * b + (a>>1)) / a);
452 }
453
1/2
✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 14 not taken.
3 vi.SetFPS(numerator, denominator);
454 3 }
455
456
457 2 AVSValue __cdecl AssumeFPS::Create(AVSValue args, void*, IScriptEnvironment* env)
458 {
459
2/4
✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 22 not taken.
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 21 not taken.
6 return new AssumeFPS( args[0].AsClip(), args[1].AsInt(),
460
9/20
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 22 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 22 not taken.
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 22 not taken.
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 22 not taken.
✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 22 not taken.
✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 21 not taken.
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 19 not taken.
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 19 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 2 times.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
10 args[2].AsInt(1), args[3].AsBool(false), env );
461 }
462
463
464 AVSValue __cdecl AssumeFPS::CreateFloat(AVSValue args, void*, IScriptEnvironment* env)
465 {
466 uint32_t num, den;
467
468 FloatToFPS("AssumeFPS", args[1].AsFloatf(), num, den, env);
469 return new AssumeFPS(args[0].AsClip(), num, den, args[2].AsBool(false), env);
470 }
471
472 // Tritical Jan 2006
473 AVSValue __cdecl AssumeFPS::CreatePreset(AVSValue args, void*, IScriptEnvironment* env)
474 {
475 uint32_t num, den;
476
477 PresetToFPS("AssumeFPS", args[1].AsString(), num, den, env);
478 return new AssumeFPS(args[0].AsClip(), num, den, args[2].AsBool(false), env);
479 }
480
481 AVSValue __cdecl AssumeFPS::CreateFromClip(AVSValue args, void*, IScriptEnvironment* env)
482 {
483 const VideoInfo& vi = args[1].AsClip()->GetVideoInfo();
484
485 if (!vi.HasVideo()) {
486 env->ThrowError("AssumeFPS: The clip supplied to get the FPS from must contain video.");
487 }
488
489 return new AssumeFPS( args[0].AsClip(), vi.fps_numerator,
490 vi.fps_denominator, args[2].AsBool(false), env );
491 }
492
493
494
495
496
497 /************************************
498 ******* ChangeFPS Filters ******
499 ************************************/
500
501
502 ChangeFPS::ChangeFPS(PClip _child, unsigned new_numerator, unsigned new_denominator, bool _linear, IScriptEnvironment* env)
503 : GenericVideoFilter(_child), linear(_linear)
504 {
505 if (new_denominator == 0)
506 env->ThrowError("ChangeFPS: Denominator cannot be 0 (zero).");
507
508 a = int64_t(vi.fps_numerator) * new_denominator;
509 b = int64_t(vi.fps_denominator) * new_numerator;
510 if (linear && (a + (b>>1))/b > 10)
511 env->ThrowError("ChangeFPS: Ratio must be less than 10 for linear access. Set LINEAR=False.");
512
513 vi.SetFPS(new_numerator, new_denominator);
514 const int64_t num_frames = (vi.num_frames * b + (a >> 1)) / a;
515 if (num_frames > 0x7FFFFFFF) // MAXINT
516 env->ThrowError("ChangeFPS: Maximum number of frames exceeded.");
517
518 vi.num_frames = int(num_frames);
519 lastframe = -1;
520 }
521
522
523 PVideoFrame __stdcall ChangeFPS::GetFrame(int n, IScriptEnvironment* env)
524 {
525 int getframe = int((n * a) / b); // Use Floor! - Which frame to get next?
526
527 if (linear) {
528 if ((lastframe < (getframe-1)) && (getframe - lastframe < 10)) { // Do not decode more than 10 frames
529 while (lastframe < (getframe-1)) {
530 lastframe++;
531 PVideoFrame p = child->GetFrame(lastframe, env); // If MSVC optimizes this I'll kill it ;)
532 }
533 }
534 }
535
536 lastframe = getframe;
537 return child->GetFrame(getframe , env );
538 }
539
540
541 bool __stdcall ChangeFPS::GetParity(int n)
542 {
543 return child->GetParity(int((n * a) / b)); // Use Floor!
544 }
545
546
547 AVSValue __cdecl ChangeFPS::Create(AVSValue args, void*, IScriptEnvironment* env)
548 {
549 return new ChangeFPS(args[0].AsClip(), args[1].AsInt(), args[2].AsInt(1), args[3].AsBool(true), env);
550 }
551
552
553 AVSValue __cdecl ChangeFPS::CreateFloat(AVSValue args, void*, IScriptEnvironment* env)
554 {
555 uint32_t num, den;
556
557 FloatToFPS("ChangeFPS", args[1].AsFloatf(), num, den, env);
558 return new ChangeFPS(args[0].AsClip(), num, den, args[2].AsBool(true), env);
559 }
560
561 // Tritical Jan 2006
562 AVSValue __cdecl ChangeFPS::CreatePreset(AVSValue args, void*, IScriptEnvironment* env)
563 {
564 uint32_t num, den;
565
566 PresetToFPS("ChangeFPS", args[1].AsString(), num, den, env);
567 return new ChangeFPS(args[0].AsClip(), num, den, args[2].AsBool(true), env);
568 }
569
570 AVSValue __cdecl ChangeFPS::CreateFromClip(AVSValue args, void*, IScriptEnvironment* env)
571 {
572 const VideoInfo& vi = args[1].AsClip()->GetVideoInfo();
573
574 if (!vi.HasVideo()) {
575 env->ThrowError("ChangeFPS: The clip supplied to get the FPS from must contain video.");
576 }
577
578 return new ChangeFPS(args[0].AsClip(), vi.fps_numerator, vi.fps_denominator,
579 args[2].AsBool(true), env);
580 }
581
582
583
584
585
586
587
588 /*************************************
589 ******* ConvertFPS Filters ******
590 *************************************/
591
592 ConvertFPS::ConvertFPS(PClip _child, unsigned new_numerator, unsigned new_denominator, int _zone,
593 int _vbi, IScriptEnvironment* env)
594 : GenericVideoFilter(_child), zone(_zone), vbi(_vbi), lps(0)
595 {
596 if (zone >= 0 && !vi.IsYUY2()) // Tritical Jan 2006
597 env->ThrowError("ConvertFPS: zone >= 0 requires YUY2 input");
598
599 fa = int64_t(vi.fps_numerator) * new_denominator;
600 fb = int64_t(vi.fps_denominator) * new_numerator;
601 if (zone >= 0)
602 {
603 if (vbi < 0) vbi = 0;
604 if (vbi > vi.height) vbi = vi.height;
605 lps = int((vi.height + vbi) * fb / fa);
606 if (zone > lps)
607 env->ThrowError("ConvertFPS: 'zone' too large. Maximum allowed %d", lps);
608 }
609 else if (3 * fb < (fa << 1)) {
610 int dec = MulDiv(vi.fps_numerator, 20000, vi.fps_denominator);
611 env->ThrowError("ConvertFPS: New frame rate too small. Must be greater than %d.%04d "
612 "Increase or use 'zone='", dec / 30000, (dec / 3) % 10000);
613 }
614 vi.SetFPS(new_numerator, new_denominator);
615 const int64_t num_frames = (vi.num_frames * fb + (fa >> 1)) / fa;
616 if (num_frames > 0x7FFFFFFF) // MAXINT
617 env->ThrowError("ConvertFPS: Maximum number of frames exceeded.");
618
619 vi.num_frames = int(num_frames);
620 }
621
622
623 PVideoFrame __stdcall ConvertFPS::GetFrame(int n, IScriptEnvironment* env)
624 {
625 // Using int64 modulo instead of modf, for double holds only 53 bits
626 // n*fa worst-like case: (60000/1001 <> 30000/1001)
627 // n = 0x7FFFFFFF; // 31 bits
628 // fa = 1001 * 60000ULL // 26 bits
629 // summa 57 bits, too much, modf((double)n * fa / fb, &nsrc_f); is not enough
630 int64_t modulo = (n * fa) % fb;
631 double frac_f = (double)modulo / fb;
632 int nsrc = int(n * fa / fb);
633
634 if (zone < 0) {
635
636 // Mode 1: Blend full frames
637
638 constexpr double threshold_f = 1.0 / 16.0;
639 // was: 1 << (resolution - 4); // 64/1024
640
641 // Don't bother if the blend ratio is small
642 if (frac_f < threshold_f)
643 return child->GetFrame(nsrc, env);
644
645 if (frac_f > 1.0 - threshold_f)
646 return child->GetFrame(nsrc + 1, env);
647
648 PVideoFrame a = child->GetFrame(nsrc, env);
649 PVideoFrame b = child->GetFrame(nsrc + 1, env);
650
651 env->MakeWritable(&a);
652
653 const int planes_y[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A };
654 const int planes_r[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A };
655 const int *planes;
656
657 int planeCount;
658 planeCount = vi.IsPlanar() ? vi.NumComponents() : 1;
659 planes = (!vi.IsPlanar() || vi.IsYUV() || vi.IsYUVA()) ? planes_y : planes_r;
660
661 const int bits_per_pixel = vi.BitsPerComponent();
662 for (int j = 0; j < planeCount; ++j)
663 {
664 const int plane = planes[j];
665 const BYTE* b_data = b->GetReadPtr(plane);
666 int b_pitch = b->GetPitch(plane);
667 BYTE* a_data = a->GetWritePtr(plane);
668 int a_pitch = a->GetPitch(plane);
669 int row_size = a->GetRowSize(plane);
670 int height = a->GetHeight(plane);
671
672 const float weight = (float)frac_f; // between 0 and 1.0
673 const int pixelsize = bits_per_pixel <= 8 ? 1 : (bits_per_pixel == 32 ? 4 : 2);
674 const int width = row_size / pixelsize;
675 const int cpuFlags = env->GetCPUFlags();
676 if (bits_per_pixel == 32) {
677 get_weighted_merge_float_fn(cpuFlags)(a_data, b_data, a_pitch, b_pitch, width, height, weight);
678 } else {
679 const int weight_i = (int)(weight * 32768.0f + 0.5f);
680 get_weighted_merge_fn(cpuFlags, weight_i)(a_data, b_data, a_pitch, b_pitch, width, height, weight_i, 32768 - weight_i, bits_per_pixel);
681 }
682 }
683
684 return a;
685
686 }
687 else {
688 // Mode 2: Switch to next frame at the scan line corresponding to the source frame's timing.
689 // If zone > 0, perform a gradual transition, i.e. blend one frame into the next
690 // over the given number of lines.
691
692 PVideoFrame a = child->GetFrame(nsrc, env);
693 PVideoFrame b = child->GetFrame(nsrc+1, env);
694 const BYTE* b_data = b->GetReadPtr();
695 int b_pitch = b->GetPitch();
696 const int row_size = a->GetRowSize();
697 const int height = a->GetHeight();
698
699
700 BYTE *pd;
701 const BYTE *pa, *pb, *a_data = a->GetReadPtr();
702 int a_pitch = a->GetPitch();
703
704 int switch_line = (int)(lps * (1.0 - frac_f));
705 int top = switch_line - (zone>>1);
706 int bottom = switch_line + (zone>>1) - lps;
707 if( bottom > 0 && nsrc > 0 ) {
708 // Finish the transition from the previous frame
709 switch_line -= lps;
710 top -= lps;
711 nsrc--;
712 b = a;
713 a = child->GetFrame( nsrc, env );
714 b_pitch = a_pitch;
715 b_data = a_data;
716 a_data = a->GetReadPtr();
717 a_pitch = a->GetPitch();
718 } else if( top >= height )
719 return a;
720
721 // Result goes into a new buffer since it can be made up of a number of source frames
722 PVideoFrame d = env->NewVideoFrameP(vi, &a);
723 BYTE* data = d->GetWritePtr();
724 const int pitch = d->GetPitch();
725 if( top > 0 )
726 env->BitBlt( data, pitch, a_data, a_pitch, row_size, top );
727 loop:
728 bottom = min( switch_line + (zone>>1), height );
729 int safe_top = max(top,0);
730 pd = data + safe_top * pitch;
731 pa = a_data + safe_top * a_pitch;
732 pb = b_data + safe_top * b_pitch;
733 for( int y = safe_top; y < bottom; y++ ) {
734 int scale = y - top;
735 for( int x = 0; x < row_size; x++ )
736 pd[x] = BYTE(pa[x] + ((pb[x] - pa[x]) * scale + (zone>>1)) / zone);
737 pd += pitch;
738 pa += a_pitch;
739 pb += b_pitch;
740 }
741 switch_line += lps;
742 top = switch_line - (zone>>1);
743 int limit = min(height,top);
744 if( bottom < limit ) {
745 pd = data + bottom * pitch;
746 pb = b_data + bottom * b_pitch;
747 env->BitBlt( pd, pitch, pb, b_pitch, row_size, limit-bottom );
748 }
749 if( top < height ) {
750 nsrc++;
751 a = b;
752 b = child->GetFrame(nsrc+1, env);
753 a_pitch = b_pitch;
754 b_pitch = b->GetPitch();
755 a_data = b_data;
756 b_data = b->GetReadPtr();
757 goto loop;
758 }
759 return d;
760 }
761 }
762
763
764 bool __stdcall ConvertFPS::GetParity(int n)
765 {
766 if( vi.IsFieldBased())
767 return child->GetParity(0) ^ (n&1);
768 else
769 return child->GetParity(0);
770 }
771
772 AVSValue __cdecl ConvertFPS::Create(AVSValue args, void*, IScriptEnvironment* env)
773 {
774 return new ConvertFPS( args[0].AsClip(), args[1].AsInt(), args[2].AsInt(1),
775 args[3].AsInt(-1), args[4].AsInt(0), env );
776 }
777
778
779 AVSValue __cdecl ConvertFPS::CreateFloat(AVSValue args, void*, IScriptEnvironment* env)
780 {
781 uint32_t num, den;
782
783 FloatToFPS("ConvertFPS", (float)args[1].AsFloat(), num, den, env);
784 return new ConvertFPS( args[0].AsClip(), num, den, args[2].AsInt(-1), args[3].AsInt(0), env );
785 }
786
787 // Tritical Jan 2006
788 AVSValue __cdecl ConvertFPS::CreatePreset(AVSValue args, void*, IScriptEnvironment* env)
789 {
790 uint32_t num, den;
791
792 PresetToFPS("ConvertFPS", args[1].AsString(), num, den, env);
793 return new ConvertFPS(args[0].AsClip(), num, den, args[2].AsInt(-1), args[3].AsInt(0), env);
794 }
795
796 AVSValue __cdecl ConvertFPS::CreateFromClip(AVSValue args, void*, IScriptEnvironment* env)
797 {
798 const VideoInfo& vi = args[1].AsClip()->GetVideoInfo();
799
800 if (!vi.HasVideo()) {
801 env->ThrowError("ConvertFPS: The clip supplied to get the FPS from must contain video.");
802 }
803
804 return new ConvertFPS( args[0].AsClip(), vi.fps_numerator, vi.fps_denominator,
805 args[2].AsInt(-1), args[3].AsInt(0), env );
806 }
807