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 |