GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 41.8% 1074 / 0 / 2572
Functions: 48.2% 163 / 0 / 338
Branches: 21.8% 806 / 0 / 3702

core/avisynth.cpp
Line Branch Exec Source
1 // Avisynth v2.6. Copyright 2002-2009 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 #include <avisynth.h>
36 #include "../core/internal.h"
37 #include "InternalEnvironment.h"
38 #include "./parser/script.h"
39 #include <avs/minmax.h>
40 #include <avs/alignment.h>
41 #include "strings.h"
42 #include <avs/cpuid.h>
43 #include <unordered_set>
44 #include "bitblt.h"
45 #include "FilterConstructor.h"
46 #include "PluginManager.h"
47 #include "../filters/conditional/conditional_reader.h"
48 #include "MappedList.h"
49 #include <chrono>
50 #include <vector>
51 #include <iostream>
52 #include <fstream>
53 #include <sstream>
54 #include <exception>
55
56 #define __STDC_FORMAT_MACROS
57 #include <inttypes.h>
58
59 #ifdef AVS_WINDOWS
60 #include <avs/win.h>
61 #include <objbase.h>
62 #else
63 #include "avisynth_conf.h"
64 #if defined(AVS_MACOS)
65 #include <mach/host_info.h>
66 #include <mach/mach_host.h>
67 #include <mach/mach_init.h>
68 #include <sys/sysctl.h>
69 #elif defined(AVS_BSD)
70 #include <sys/sysctl.h>
71 #else
72 #include <avs/filesystem.h>
73 #include <set>
74 #if defined(AVS_HAIKU)
75 #include <OS.h>
76 #else
77 #include <sys/sysinfo.h>
78 #endif
79 #endif
80 #include <avs/posix.h>
81 #endif
82
83 #include <string>
84 #include <cstdio>
85 #include <cstdarg>
86 #include <cassert>
87 #include "MTGuard.h"
88 #include "cache.h"
89 #include <clocale>
90 #include <cmath>
91 #include <limits>
92 #include <set>
93
94 #include "FilterGraph.h"
95 #include "DeviceManager.h"
96 #include "AVSMap.h"
97
98 #ifndef YieldProcessor // low power spin idle
99 #define YieldProcessor() __nop(void)
100 #endif
101
102 class ScriptEnvironment; // forward
103
104 /* Global Lock Manager */
105 class GlobalLockManager
106 {
107 public:
108 // Get a named mutex. If it doesn't exist, it's created.
109 static std::mutex& get_mutex(const std::string& name);
110
111 // Track which environments hold which locks for cleanup.
112 static void acquire_lock_for_env(const std::string& name, ScriptEnvironment* env);
113 static void release_lock_for_env(const std::string& name, ScriptEnvironment* env);
114
115 // Called when an IScriptEnvironment is destroyed.
116 static void on_environment_exit(ScriptEnvironment* env);
117
118 private:
119 GlobalLockManager() = delete;
120 ~GlobalLockManager() = delete;
121
122 static std::map<std::string, std::unique_ptr<std::mutex>> s_namedMutexes;
123 static std::mutex s_mutexMapLock; // Protects s_namedMutexes
124
125 static std::map<ScriptEnvironment*, std::set<std::string>> s_envToHeldLocks;
126 static std::mutex s_envLocksMapLock; // Protects s_envToHeldLocks
127 };
128
129 std::map<std::string, std::unique_ptr<std::mutex>> GlobalLockManager::s_namedMutexes;
130 std::mutex GlobalLockManager::s_mutexMapLock;
131 std::map<ScriptEnvironment*, std::set<std::string>> GlobalLockManager::s_envToHeldLocks;
132 std::mutex GlobalLockManager::s_envLocksMapLock;
133
134 std::mutex& GlobalLockManager::get_mutex(const std::string& name)
135 {
136 std::lock_guard<std::mutex> lock(s_mutexMapLock);
137 if (s_namedMutexes.find(name) == s_namedMutexes.end())
138 {
139 s_namedMutexes[name] = std::make_unique<std::mutex>();
140 }
141 return *s_namedMutexes[name];
142 }
143
144 void GlobalLockManager::acquire_lock_for_env(const std::string& name, ScriptEnvironment* env)
145 {
146 std::lock_guard<std::mutex> envLock(s_envLocksMapLock);
147 s_envToHeldLocks[env].insert(name);
148 }
149
150 void GlobalLockManager::release_lock_for_env(const std::string& name, ScriptEnvironment* env)
151 {
152 std::lock_guard<std::mutex> envLock(s_envLocksMapLock);
153 if (s_envToHeldLocks.count(env))
154 {
155 s_envToHeldLocks[env].erase(name);
156 if (s_envToHeldLocks[env].empty())
157 {
158 s_envToHeldLocks.erase(env);
159 }
160 }
161 }
162
163 453 void GlobalLockManager::on_environment_exit(ScriptEnvironment* env)
164 {
165
1/2
✓ Branch 2 → 3 taken 453 times.
✗ Branch 2 → 12 not taken.
453 std::lock_guard<std::mutex> envLock(s_envLocksMapLock);
166
1/2
✓ Branch 3 → 4 taken 453 times.
✗ Branch 3 → 10 not taken.
453 auto it = s_envToHeldLocks.find(env);
167
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 453 times.
453 if (it != s_envToHeldLocks.end())
168 {
169 // Note: The actual mutexes are not unlocked here, only the tracking is cleared.
170 // A truly stuck mutex requires more complex solutions (e.g., timed_mutex with recovery,
171 // or ensuring threads exit gracefully). For Avisynth's use case, clearing the tracking
172 // is important to prevent memory leaks in the map.
173
174 // If it tried to call unlock() on a mutex held by another (potentially crashed) thread,
175 // that would lead to undefined behavior.By simply removing the tracking entry,
176 // we avoid this dangerous operation.
177 s_envToHeldLocks.erase(it);
178 }
179 453 }
180
181 extern const AVSFunction Audio_filters[],
182 Combine_filters[],
183 Convert_filters[],
184 Convolution_filters[],
185 Edit_filters[],
186 Field_filters[],
187 Focus_filters[],
188 Fps_filters[],
189 Histogram_filters[],
190 Layer_filters[],
191 Levels_filters[],
192 Misc_filters[],
193 Plugin_functions[],
194 Resample_filters[],
195 Resize_filters[],
196 Script_functions[],
197 Source_filters[],
198 Text_filters[],
199 Transform_filters[],
200 Merge_filters[],
201 Color_filters[],
202 Debug_filters[],
203 Turn_filters[],
204 Conditional_filters[],
205 Conditional_funtions_filters[],
206 Cache_filters[],
207 Greyscale_filters[],
208 Swap_filters[],
209 Overlay_filters[],
210 Exprfilter_filters[],
211 FilterGraph_filters[],
212 Device_filters[]
213 ;
214
215
216 const AVSFunction* const builtin_functions[] = {
217 Audio_filters,
218 Combine_filters,
219 Convert_filters,
220 Convolution_filters,
221 Edit_filters,
222 Field_filters,
223 Focus_filters,
224 Fps_filters,
225 Histogram_filters,
226 Layer_filters,
227 Levels_filters,
228 Misc_filters,
229 Resample_filters,
230 Resize_filters,
231 Script_functions,
232 Source_filters,
233 Text_filters,
234 Transform_filters,
235 Merge_filters,
236 Color_filters,
237 Debug_filters,
238 Turn_filters,
239 Conditional_filters,
240 Conditional_funtions_filters,
241 Plugin_functions,
242 Cache_filters,
243 Overlay_filters,
244 Greyscale_filters,
245 Swap_filters,
246 FilterGraph_filters,
247 Device_filters,
248 Exprfilter_filters
249 };
250
251 #if 0
252 // Global statistics counters
253 struct {
254 unsigned int CleanUps;
255 unsigned int Losses;
256 unsigned int PlanA1;
257 unsigned int PlanA2;
258 unsigned int PlanB;
259 unsigned int PlanC;
260 unsigned int PlanD;
261 char tag[36];
262 } g_Mem_stats = { 0, 0, 0, 0, 0, 0, 0, "CleanUps, Losses, Plan[A1,A2,B,C,D]" };
263 #endif
264
265 const _PixelClip PixelClip;
266
267
268 #ifdef MSVC
269 // Helper function to count set bits in the processor mask.
270 template<typename T>
271 static uint32_t CountSetBits(T bitMask)
272 {
273 uint32_t LSHIFT = sizeof(T) * 8 - 1;
274 uint32_t bitSetCount = 0;
275 T bitTest = (T)1 << LSHIFT;
276 uint32_t i;
277
278 for (i = 0; i <= LSHIFT; ++i)
279 {
280 bitSetCount += ((bitMask & bitTest) ? 1 : 0);
281 bitTest /= 2;
282 }
283
284 return bitSetCount;
285 }
286 #endif
287
288 static size_t GetNumPhysicalCPUs()
289 {
290 #if defined(AVS_WINDOWS)
291 #ifdef MSVC
292 typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
293 LPFN_GLPI glpi;
294 BOOL done = FALSE;
295 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
296 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
297 DWORD returnLength = 0;
298 [[maybe_unused]] DWORD logicalProcessorCount = 0;
299 [[maybe_unused]] DWORD numaNodeCount = 0;
300 [[maybe_unused]] DWORD processorCoreCount = 0;
301 [[maybe_unused]] DWORD processorL1CacheCount = 0;
302 [[maybe_unused]] DWORD processorL2CacheCount = 0;
303 [[maybe_unused]] DWORD processorL3CacheCount = 0;
304 [[maybe_unused]] DWORD processorPackageCount = 0;
305 DWORD byteOffset = 0;
306 PCACHE_DESCRIPTOR Cache;
307
308 glpi = (LPFN_GLPI)GetProcAddress(
309 GetModuleHandle(TEXT("kernel32")),
310 "GetLogicalProcessorInformation");
311 if (NULL == glpi)
312 {
313 // _tprintf(TEXT("\nGetLogicalProcessorInformation is not supported.\n"));
314 return (0);
315 }
316
317 while (!done)
318 {
319 BOOL rc = glpi(buffer, &returnLength);
320
321 if (FALSE == rc)
322 {
323 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
324 {
325 if (buffer)
326 free(buffer);
327
328 buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(
329 returnLength);
330
331 if (NULL == buffer)
332 {
333 // _tprintf(TEXT("\nError: Allocation failure\n"));
334 return (0);
335 }
336 }
337 else
338 {
339 // _tprintf(TEXT("\nError %d\n"), GetLastError());
340 return (0);
341 }
342 }
343 else
344 {
345 done = TRUE;
346 }
347 }
348
349 ptr = buffer;
350
351 while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength)
352 {
353 switch (ptr->Relationship)
354 {
355 case RelationNumaNode:
356 // Non-NUMA systems report a single record of this type.
357 numaNodeCount++;
358 break;
359
360 case RelationProcessorCore:
361 processorCoreCount++;
362
363 // A hyperthreaded core supplies more than one logical processor.
364 logicalProcessorCount += CountSetBits<ULONG_PTR>(ptr->ProcessorMask);
365 break;
366
367 case RelationCache:
368 // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache.
369 Cache = &ptr->Cache;
370 if (Cache->Level == 1)
371 {
372 processorL1CacheCount++;
373 }
374 else if (Cache->Level == 2)
375 {
376 processorL2CacheCount++;
377 }
378 else if (Cache->Level == 3)
379 {
380 processorL3CacheCount++;
381 }
382 break;
383
384 case RelationProcessorPackage:
385 // Logical processors share a physical package.
386 processorPackageCount++;
387 break;
388
389 default:
390 // _tprintf(TEXT("\nError: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.\n"));
391 return (0);
392 }
393 byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
394 ptr++;
395 }
396
397 /*
398 _tprintf(TEXT("\nGetLogicalProcessorInformation results:\n"));
399 _tprintf(TEXT("Number of NUMA nodes: %d\n"),
400 numaNodeCount);
401 _tprintf(TEXT("Number of physical processor packages: %d\n"),
402 processorPackageCount);
403 _tprintf(TEXT("Number of processor cores: %d\n"),
404 processorCoreCount);
405 _tprintf(TEXT("Number of logical processors: %d\n"),
406 logicalProcessorCount);
407 _tprintf(TEXT("Number of processor L1/L2/L3 caches: %d/%d/%d\n"),
408 processorL1CacheCount,
409 processorL2CacheCount,
410 processorL3CacheCount);
411 */
412
413 free(buffer);
414
415 return processorCoreCount;
416 #else
417 return 4; // TODO: GCC on Windows?
418 #endif
419 #elif defined(AVS_LINUX)
420 std::set<int> core_ids;
421 for (auto& p : fs::directory_iterator("/sys/devices/system/cpu")) {
422 if (!p.path().filename().string().rfind("cpu", 0)) {
423 std::ifstream ifs(p.path() / "topology/core_id");
424 int core_id;
425 if (ifs) {
426 ifs >> core_id;
427 if (ifs)
428 core_ids.insert(core_id);
429 }
430 }
431 }
432 return core_ids.size();
433 #elif defined(AVS_MACOS)
434 int cpu_cnt = 0;
435 size_t cpu_cnt_size = sizeof(cpu_cnt);
436 return sysctlbyname("hw.physicalcpu", &cpu_cnt, &cpu_cnt_size, NULL, 0) ? 0 : cpu_cnt;
437 #else
438 return 4; // AVS_BSD TODO
439 #endif
440 }
441
442 #ifdef MSVC
443 static std::string FormatString(const char* fmt, va_list args)
444 {
445 va_list args2;
446 va_copy(args2, args);
447 _locale_t locale = _create_locale(LC_NUMERIC, "C"); // decimal point: dot
448
449 int count = _vscprintf_l(fmt, locale, args2);
450 // don't use _vsnprintf_l(NULL, 0, fmt, locale, args) here,
451 // it returns -1 instead of the buffer size under Wine (February, 2017)
452 std::vector<char> buf(count + 1);
453 _vsnprintf_l(buf.data(), buf.size(), fmt, locale, args2);
454
455 _free_locale(locale);
456 va_end(args2);
457
458 return std::string(buf.data());
459 }
460 #else
461 33 static std::string FormatString(const char* fmt, va_list args)
462 {
463 va_list args2;
464 33 va_copy(args2, args);
465
466 // There is no locale specific version of vsnprintf under Linux/gcc.
467 // During program startup, the equivalent of setlocale(LC_ALL, "C")
468 // is executed before any user code is run. But Avisynth is just a dll/shared object.
469 // If the host program calls std::setlocale(LC_ALL, "") - typically in its 'main()'
470 // then it sets current locale by the environment variables.
471 // Unfortunately setlocale is not thread safe and modifies global state which
472 // affects execution of locale-dependent functions, it is undefined behavior to call
473 // it from one thread, while another thread is executing any of such functions (e.g. sprintf)
474 // It can happen that a plugin sets setlocale(LC_ALL, ""). The effect is global,
475 // other formatting functions in the same program will start using the new
476 // (environment) setting.
477 // One must set LC_NUMERIC part of the locale to "C" before the host executable,
478 // to be sure that after such unsafe setlocale(LC_ALL, "") then formatting to dot
479 // remains O.K.
480
481 #if 0
482 // Finally we don't do that. Not 100% safe.
483 std::string prev_loc = std::setlocale(LC_NUMERIC, nullptr);
484 setlocale(LC_NUMERIC, "C"); // Set C locale for '.' and no thousand sep
485 #endif
486
487 33 int count = vsnprintf(NULL, 0, fmt, args);
488
1/2
✓ Branch 4 → 5 taken 33 times.
✗ Branch 4 → 16 not taken.
33 std::vector<char> buf(count + 1);
489 33 vsnprintf(buf.data(), buf.size(), fmt, args2);
490
491 33 va_end(args2);
492
493 #if 0
494 std::setlocale(LC_NUMERIC, prev_loc.c_str()); // Restore the previous locale.
495 #endif
496
497
1/2
✓ Branch 11 → 12 taken 33 times.
✗ Branch 11 → 19 not taken.
66 return std::string(buf.data());
498 33 }
499 #endif
500
501 940 void* VideoFrame::operator new(size_t size) {
502 940 return ::operator new(size);
503 }
504
505 905 VideoFrame::VideoFrame(VideoFrameBuffer* _vfb, AVSMap* avsmap, int _offset, int _pitch, int _row_size, int _height, int _pixel_type)
506 905 : refcount(0), vfb(_vfb), offset(_offset), pitch(_pitch), row_size(_row_size), height(_height),
507 905 offsetU(_offset), offsetV(_offset), pitchUV(0), row_sizeUV(0), heightUV(0), // PitchUV=0 so this doesn't take up additional space
508 905 offsetA(0), pitchA(0), row_sizeA(0),
509 905 properties(avsmap),
510 905 pixel_type(_pixel_type)
511 {
512 905 InterlockedIncrement(&vfb->refcount);
513 905 }
514
515 32 VideoFrame::VideoFrame(VideoFrameBuffer* _vfb, AVSMap* avsmap, int _offset, int _pitch, int _row_size, int _height,
516 32 int _offsetU, int _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV, int _pixel_type)
517 32 : refcount(0), vfb(_vfb), offset(_offset), pitch(_pitch), row_size(_row_size), height(_height),
518 32 offsetU(_offsetU), offsetV(_offsetV), pitchUV(_pitchUV), row_sizeUV(_row_sizeUV), heightUV(_heightUV),
519 32 offsetA(0), pitchA(0), row_sizeA(0),
520 32 properties(avsmap),
521 32 pixel_type(_pixel_type)
522 {
523 32 InterlockedIncrement(&vfb->refcount);
524 32 }
525
526 3 VideoFrame::VideoFrame(VideoFrameBuffer* _vfb, AVSMap* avsmap, int _offset, int _pitch, int _row_size, int _height,
527 3 int _offsetU, int _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV, int _offsetA, int _pixel_type)
528 3 : refcount(0), vfb(_vfb), offset(_offset), pitch(_pitch), row_size(_row_size), height(_height),
529 3 offsetU(_offsetU), offsetV(_offsetV), pitchUV(_pitchUV), row_sizeUV(_row_sizeUV), heightUV(_heightUV),
530 3 offsetA(_offsetA), pitchA(_pitch), row_sizeA(_row_size),
531 3 properties(avsmap),
532 3 pixel_type(_pixel_type)
533 {
534 3 InterlockedIncrement(&vfb->refcount);
535 3 }
536 // Hack note: Use of Subframe will require an "InterlockedDecrement(&retval->refcount);" after
537 // assignment to a PVideoFrame, the same as for a "New VideoFrame" to keep the refcount consistant.
538 // P.F. ?? so far it works automatically
539
540 14 VideoFrame* VideoFrame::Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const {
541 // Change planar color formats to Y-only, which is quasi-interleaved
542 int new_pixel_type;
543
1/2
✓ Branch 2 → 3 taken 14 times.
✗ Branch 2 → 4 not taken.
14 if (pixel_type & VideoInfo::CS_PLANAR)
544 14 new_pixel_type = VideoInfo::CS_GENERIC_Y | (pixel_type & VideoInfo::CS_Sample_Bits_Mask);
545 else
546 new_pixel_type = pixel_type;
547
548 return new VideoFrame(vfb, new AVSMap(), offset + rel_offset, new_pitch, new_row_size, new_height,
549
4/12
✓ Branch 6 → 7 taken 14 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 14 times.
✗ Branch 7 → 16 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 14 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 14 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
28 new_pixel_type);
550 }
551
552 32 VideoFrame* VideoFrame::Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height,
553 int rel_offsetU, int rel_offsetV, int new_pitchUV) const {
554 // Maintain plane size relationship
555
1/2
✓ Branch 2 → 3 taken 32 times.
✗ Branch 2 → 4 not taken.
32 const int new_row_sizeUV = !row_size ? 0 : MulDiv(new_row_size, row_sizeUV, row_size);
556
1/2
✓ Branch 5 → 6 taken 32 times.
✗ Branch 5 → 7 not taken.
32 const int new_heightUV = !height ? 0 : MulDiv(new_height, heightUV, height);
557
558 // Remove alpha from color format
559 int new_pixel_type;
560
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 32 times.
32 if (pixel_type & VideoInfo::CS_YUVA)
561 new_pixel_type = (pixel_type & ~VideoInfo::CS_YUVA) | VideoInfo::CS_YUV;
562
2/2
✓ Branch 10 → 11 taken 22 times.
✓ Branch 10 → 12 taken 10 times.
32 else if (pixel_type & VideoInfo::CS_RGBA_TYPE)
563 22 new_pixel_type = (pixel_type & ~VideoInfo::CS_RGBA_TYPE) | VideoInfo::CS_RGB_TYPE;
564 else
565 10 new_pixel_type = pixel_type;
566
567 return new VideoFrame(vfb, new AVSMap(), offset + rel_offset, new_pitch, new_row_size, new_height,
568 32 rel_offsetU + offsetU, rel_offsetV + offsetV, new_pitchUV, new_row_sizeUV, new_heightUV,
569
4/12
✓ Branch 14 → 15 taken 32 times.
✗ Branch 14 → 27 not taken.
✓ Branch 15 → 16 taken 32 times.
✗ Branch 15 → 24 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 32 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 32 times.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 29 not taken.
64 new_pixel_type);
570 }
571
572 // alpha support
573 3 VideoFrame* VideoFrame::Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height,
574 int rel_offsetU, int rel_offsetV, int new_pitchUV,
575 int rel_offsetA) const {
576 // Maintain plane size relationship
577
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 4 not taken.
3 const int new_row_sizeUV = !row_size ? 0 : MulDiv(new_row_size, row_sizeUV, row_size);
578
1/2
✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 7 not taken.
3 const int new_heightUV = !height ? 0 : MulDiv(new_height, heightUV, height);
579
580 return new VideoFrame(vfb, new AVSMap(), offset + rel_offset, new_pitch, new_row_size, new_height,
581 3 offsetU + rel_offsetU, offsetV + rel_offsetV, new_pitchUV, new_row_sizeUV, new_heightUV,
582 3 offsetA + rel_offsetA,
583
4/12
✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 22 not taken.
✓ Branch 10 → 11 taken 3 times.
✗ Branch 10 → 19 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 3 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 3 times.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
6 pixel_type);
584 }
585
586 VideoFrameBuffer::VideoFrameBuffer() :
587 data(nullptr),
588 data_size(0),
589 sequence_number(0),
590 refcount(1),
591 device(nullptr)
592 { }
593
594
595 891 VideoFrameBuffer::VideoFrameBuffer(int size, int margin, Device* device) :
596 891 data(device->Allocate(size, margin)),
597 891 data_size(size),
598 891 sequence_number(0),
599 891 refcount(0),
600 891 device(device)
601 891 { }
602
603 891 VideoFrameBuffer::~VideoFrameBuffer() { DESTRUCTOR(); }
604 891 void VideoFrameBuffer::DESTRUCTOR() {
605 // _ASSERTE(refcount == 0);
606 891 InterlockedIncrement(&sequence_number); // HACK: Notify any children with a pointer, this buffer has changed!!!
607
1/2
✓ Branch 2 → 3 taken 891 times.
✗ Branch 2 → 4 not taken.
891 if (data) device->Free(data);
608 891 data = nullptr; // and mark it invalid!!
609 891 data_size = 0; // and don't forget to set the size to 0 as well!
610 891 device = nullptr; // no longer related to a device
611 891 }
612
613
614 class AtExiter {
615 struct AtExitRec {
616 const IScriptEnvironment::ShutdownFunc func;
617 void* const user_data;
618 AtExitRec* const next;
619 8 AtExitRec(IScriptEnvironment::ShutdownFunc _func, void* _user_data, AtExitRec* _next)
620 8 : func(_func), user_data(_user_data), next(_next) {}
621 };
622 AtExitRec* atexit_list;
623
624 public:
625 453 AtExiter() {
626 453 atexit_list = 0;
627 453 }
628
629 8 void Add(IScriptEnvironment::ShutdownFunc f, void* d) {
630
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 8 times.
8 atexit_list = new AtExitRec(f, d, atexit_list);
631 8 }
632
633 453 void Execute(IScriptEnvironment* env) {
634
2/2
✓ Branch 7 → 3 taken 8 times.
✓ Branch 7 → 8 taken 453 times.
461 while (atexit_list) {
635 8 AtExitRec* next = atexit_list->next;
636 8 atexit_list->func(atexit_list->user_data, env);
637
1/2
✓ Branch 4 → 5 taken 8 times.
✗ Branch 4 → 6 not taken.
8 delete atexit_list;
638 8 atexit_list = next;
639 }
640 453 }
641 };
642
643
644 84 static std::string NormalizeString(const std::string& str)
645 {
646 // lowercase
647 84 std::string ret = str;
648
2/2
✓ Branch 8 → 4 taken 1364 times.
✓ Branch 8 → 9 taken 84 times.
1448 for (size_t i = 0; i < ret.size(); ++i)
649
2/4
✓ Branch 4 → 5 taken 1364 times.
✗ Branch 4 → 25 not taken.
✓ Branch 5 → 6 taken 1364 times.
✗ Branch 5 → 25 not taken.
1364 ret[i] = tolower(ret[i]);
650
651 // trim trailing spaces
652 84 size_t endpos = ret.find_last_not_of(" \t");
653
1/2
✓ Branch 10 → 11 taken 84 times.
✗ Branch 10 → 15 not taken.
84 if (std::string::npos != endpos)
654
1/2
✓ Branch 11 → 12 taken 84 times.
✗ Branch 11 → 23 not taken.
84 ret = ret.substr(0, endpos + 1);
655
656 // trim leading spaces
657 84 size_t startpos = ret.find_first_not_of(" \t");
658
1/2
✓ Branch 16 → 17 taken 84 times.
✗ Branch 16 → 21 not taken.
84 if (std::string::npos != startpos)
659
1/2
✓ Branch 17 → 18 taken 84 times.
✗ Branch 17 → 24 not taken.
84 ret = ret.substr(startpos);
660
661 84 return ret;
662 }
663
664 typedef enum class _MtWeight
665 {
666 MT_WEIGHT_0_DEFAULT,
667 MT_WEIGHT_1_USERSPEC,
668 MT_WEIGHT_2_USERFORCE,
669 MT_WEIGHT_MAX
670 } MtWeight;
671
672 class ClipDataStore
673 {
674 public:
675
676 // The clip instance that we hold data for.
677 IClip* Clip = nullptr;
678
679 // Clip was created directly by an Invoke() call
680 bool CreatedByInvoke = false;
681
682 13 ClipDataStore(IClip* clip) : Clip(clip) {};
683 };
684
685 class MtModeEvaluator
686 {
687 public:
688 int NumChainedNice = 0;
689 int NumChainedMulti = 0;
690 int NumChainedSerial = 0;
691
692 7 MtMode GetFinalMode(MtMode topInvokeMode)
693 {
694
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 7 times.
7 if (NumChainedSerial > 0)
695 {
696 return MT_SERIALIZED;
697 }
698
2/2
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 8 taken 1 time.
7 else if (NumChainedMulti > 0)
699 {
700
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 6 times.
6 if (MT_SERIALIZED == topInvokeMode)
701 {
702 return MT_SERIALIZED;
703 }
704 else
705 {
706 6 return MT_MULTI_INSTANCE;
707 }
708 }
709 else
710 {
711 1 return topInvokeMode;
712 }
713 }
714
715 void Accumulate(const MtModeEvaluator& other)
716 {
717 NumChainedNice += other.NumChainedNice;
718 NumChainedMulti += other.NumChainedMulti;
719 NumChainedSerial += other.NumChainedSerial;
720 }
721
722 7 void Accumulate(MtMode mode)
723 {
724
2/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 6 times.
✗ Branch 2 → 5 not taken.
✗ Branch 2 → 6 not taken.
7 switch (mode)
725 {
726 1 case MT_NICE_FILTER:
727 1 ++NumChainedNice;
728 1 break;
729 6 case MT_MULTI_INSTANCE:
730 6 ++NumChainedMulti;
731 6 break;
732 case MT_SERIALIZED:
733 ++NumChainedSerial;
734 break;
735 default:
736 assert(0);
737 break;
738 }
739 7 }
740
741 20 static bool ClipSpecifiesMtMode(const PClip& clip)
742 {
743 20 int val = clip->SetCacheHints(CACHE_GET_MTMODE, 0);
744
4/6
✓ Branch 6 → 7 taken 20 times.
✗ Branch 6 → 10 not taken.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 7 → 10 taken 8 times.
✓ Branch 8 → 9 taken 12 times.
✗ Branch 8 → 10 not taken.
20 return (clip->GetVersion() >= 5) && (val > MT_INVALID) && (val < MT_MODE_COUNT);
745 }
746
747 7 static MtMode GetInstanceMode(const PClip& clip, MtMode defaultMode)
748 {
749
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 6 times.
7 return ClipSpecifiesMtMode(clip) ? (MtMode)clip->SetCacheHints(CACHE_GET_MTMODE, 0) : defaultMode;
750 }
751
752 6 static MtMode GetInstanceMode(const PClip& clip)
753 {
754 6 return (MtMode)clip->SetCacheHints(CACHE_GET_MTMODE, 0);
755 }
756
757 7 static MtMode GetMtMode(const PClip& clip, const Function* invokeCall, const InternalEnvironment* env)
758 {
759 bool invokeModeForced;
760
761
1/2
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 12 not taken.
7 MtMode invokeMode = env->GetFilterMTMode(invokeCall, &invokeModeForced);
762
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 7 times.
7 if (invokeModeForced) {
763 return invokeMode;
764 }
765
766
1/2
✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 12 not taken.
7 bool hasInstanceMode = ClipSpecifiesMtMode(clip);
767
2/2
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 9 taken 1 time.
7 if (hasInstanceMode) {
768
1/2
✓ Branch 7 → 8 taken 6 times.
✗ Branch 7 → 12 not taken.
6 return GetInstanceMode(clip);
769 }
770 else {
771 1 return invokeMode;
772 }
773 }
774
775 6 static bool UsesDefaultMtMode(const PClip& clip, const Function* invokeCall, const InternalEnvironment* env)
776 {
777
3/4
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 5 times.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 7 not taken.
6 return !ClipSpecifiesMtMode(clip) && !env->FilterHasMtMode(invokeCall);
778 }
779
780 7 void AddChainedFilter(const PClip& clip, MtMode defaultMode)
781 {
782 7 MtMode mode = GetInstanceMode(clip, defaultMode);
783 7 Accumulate(mode);
784 7 }
785 };
786
787
788 OneTimeLogTicket::OneTimeLogTicket(ELogTicketType type)
789 : _type(type)
790 {}
791
792 1 OneTimeLogTicket::OneTimeLogTicket(ELogTicketType type, const Function* func)
793 1 : _type(type), _function(func)
794 1 {}
795
796 OneTimeLogTicket::OneTimeLogTicket(ELogTicketType type, const std::string& str)
797 : _type(type), _string(str)
798 {}
799
800 bool OneTimeLogTicket::operator==(const OneTimeLogTicket& other) const
801 {
802 return (_type == other._type)
803 && (_function == other._function)
804 && (_string.compare(other._string) == 0);
805 }
806
807 namespace std
808 {
809 template <>
810 struct hash<OneTimeLogTicket>
811 {
812 2 std::size_t operator()(const OneTimeLogTicket& k) const
813 {
814 // TODO: This is a pretty poor combination function for hashes.
815 // Find something better than a simple XOR.
816 2 return hash<int>()(k._type)
817 2 ^ hash<void*>()((void*)k._function)
818
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 10 not taken.
2 ^ hash<std::string>()((std::string)k._string);
819 }
820 };
821 }
822
823 #include "vartable.h"
824 #include "ThreadPool.h"
825 #include <map>
826 #include <unordered_set>
827 #include <atomic>
828 #include <stack>
829 #include "Prefetcher.h"
830 #include "BufferPool.h"
831 #include "ScriptEnvironmentTLS.h"
832
833 class ThreadScriptEnvironment;
834
835 // order is not important, unlike in IScriptEnvironment variants.
836 class ScriptEnvironment {
837 public:
838 ScriptEnvironment();
839 void CheckVersion(int version);
840 int GetCPUFlags();
841 int64_t GetCPUFlagsEx();
842 void AddFunction(const char* name, const char* params, INeoEnv::ApplyFunc apply, void* user_data = 0);
843 void AddFunction25(const char* name, const char* params, INeoEnv::ApplyFunc apply, void* user_data = 0);
844 void AddFunctionPreV11C(const char* name, const char* params, INeoEnv::ApplyFunc apply, void* user_data = 0);
845 bool FunctionExists(const char* name);
846 PVideoFrame NewVideoFrameOnDevice(const VideoInfo& vi, int align, Device* device);
847 PVideoFrame NewVideoFrameOnDevice(int row_size, int height, int align, int pixel_type, Device* device);
848 PVideoFrame NewVideoFrame(const VideoInfo& vi, const PDevice& device);
849 // variant #3, with frame property source
850 PVideoFrame NewVideoFrameOnDevice(const VideoInfo& vi, int align, Device* device, const PVideoFrame *prop_src);
851 // variant #1, with frame property source
852 PVideoFrame NewVideoFrameOnDevice(int row_size, int height, int align, int pixel_type, Device* device, const PVideoFrame* prop_src);
853 // variant #2, with frame property source
854 PVideoFrame NewVideoFrame(const VideoInfo& vi, const PDevice& device, const PVideoFrame* prop_src);
855
856 PVideoFrame NewPlanarVideoFrame(int row_size, int height, int row_sizeUV, int heightUV, int align, bool U_first, int pixel_type, Device* device);
857
858 bool MakeWritable(PVideoFrame* pvf);
859 void BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height);
860 void AtExit(IScriptEnvironment::ShutdownFunc function, void* user_data);
861 PVideoFrame Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height);
862 int SetMemoryMax(int mem);
863 int SetWorkingDir(const char* newdir);
864 bool AcquireGlobalLock(const char* name);
865 void ReleaseGlobalLock(const char* name);
866 AVSC_CC ~ScriptEnvironment();
867 void* ManageCache(int key, void* data);
868 bool PlanarChromaAlignment(IScriptEnvironment::PlanarChromaAlignmentMode key);
869 PVideoFrame SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV);
870 void DeleteScriptEnvironment();
871 void ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, int textcolor, int halocolor, int bgcolor);
872 void ApplyMessageEx(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, int textcolor, int halocolor, int bgcolor, bool utf8);
873 const AVS_Linkage* GetAVSLinkage();
874
875 // alpha support
876 PVideoFrame NewPlanarVideoFrame(int row_size, int height, int row_sizeUV, int heightUV, int align, bool U_first, bool alpha, int pixel_type, Device* device);
877 PVideoFrame SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA);
878 PVideoFrame SubframePlanarA(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA);
879
880 void copyFrameProps(const PVideoFrame& src, PVideoFrame& dst);
881 const AVSMap* getFramePropsRO(const PVideoFrame& frame) AVS_NOEXCEPT;
882 AVSMap* getFramePropsRW(PVideoFrame& frame) AVS_NOEXCEPT;
883 int propNumKeys(const AVSMap* map) AVS_NOEXCEPT;
884 const char* propGetKey(const AVSMap* map, int index) AVS_NOEXCEPT;
885 int propNumElements(const AVSMap* map, const char* key) AVS_NOEXCEPT;
886 char propGetType(const AVSMap* map, const char* key) AVS_NOEXCEPT;
887 int propDeleteKey(AVSMap* map, const char* key) AVS_NOEXCEPT;
888 int64_t propGetInt(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
889 int propGetIntSaturated(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
890 double propGetFloat(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
891 float propGetFloatSaturated(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
892 const char* propGetData(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
893 int propGetDataSize(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
894 int propGetDataTypeHint(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
895 PClip propGetClip(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
896 const PVideoFrame propGetFrame(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT;
897 int propSetInt(AVSMap* map, const char* key, int64_t i, int append) AVS_NOEXCEPT;
898 int propSetFloat(AVSMap* map, const char* key, double d, int append) AVS_NOEXCEPT;
899 int propSetData(AVSMap* map, const char* key, const char* d, int length, int append) AVS_NOEXCEPT;
900 int propSetDataH(AVSMap* map, const char* key, const char* d, int length, int type, int append) AVS_NOEXCEPT;
901 int propSetClip(AVSMap* map, const char* key, PClip& clip, int append) AVS_NOEXCEPT;
902 int propSetFrame(AVSMap* map, const char* key, const PVideoFrame& frame, int append) AVS_NOEXCEPT;
903
904 const int64_t* propGetIntArray(const AVSMap* map, const char* key, int* error) AVS_NOEXCEPT;
905 const double* propGetFloatArray(const AVSMap* map, const char* key, int* error) AVS_NOEXCEPT;
906 int propSetIntArray(AVSMap* map, const char* key, const int64_t* i, int size) AVS_NOEXCEPT;
907 int propSetFloatArray(AVSMap* map, const char* key, const double* d, int size) AVS_NOEXCEPT;
908
909 AVSMap* createMap() AVS_NOEXCEPT;
910 void freeMap(AVSMap* map) AVS_NOEXCEPT;
911 void clearMap(AVSMap* map) AVS_NOEXCEPT;
912
913 PVideoFrame NewVideoFrame(const VideoInfo& vi, const PVideoFrame* prop_src, int align = FRAME_ALIGN);
914
915 bool MakePropertyWritable(PVideoFrame* pvf); // V9
916
917 /* IScriptEnvironment2 */
918 bool LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result);
919 void AddAutoloadDir(const char* dirPath, bool toFront);
920 void ClearAutoloadDirs();
921 void AutoloadPlugins();
922 void AddFunction(const char* name, const char* params, INeoEnv::ApplyFunc apply, void* user_data, const char *exportVar);
923 bool InternalFunctionExists(const char* name);
924 void AdjustMemoryConsumption(size_t amount, bool minus);
925 void SetFilterMTMode(const char* filter, MtMode mode, bool force);
926 void SetFilterMTMode(const char* filter, MtMode mode, MtWeight weight);
927 void SetFilterProp(const char* filter, const char* key, const AVSValue& value, int mode);
928 void SetFilterPropConditional(const char* filter, const char* param_name, const AVSValue& param_match,
929 const char* key, const AVSValue& value, int mode);
930 const char* GetFilterProps();
931 void SetFilterPropPassthrough(const char* filter);
932 MtMode GetFilterMTMode(const Function* filter, bool* is_forced) const;
933 void ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion);
934 IJobCompletion* NewCompletion(size_t capacity);
935 size_t GetEnvProperty(AvsEnvProperty prop);
936 ClipDataStore* ClipData(IClip* clip);
937 MtMode GetDefaultMtMode() const;
938 bool FilterHasMtMode(const Function* filter) const;
939 void SetLogParams(const char* target, int level);
940 void LogMsg(int level, const char* fmt, ...);
941 void LogMsg_valist(int level, const char* fmt, va_list va);
942 void LogMsgOnce(const OneTimeLogTicket& ticket, int level, const char* fmt, ...);
943 void LogMsgOnce_valist(const OneTimeLogTicket& ticket, int level, const char* fmt, va_list va);
944
945 void SetMaxCPU(const char *features); // fixme: why is here InternalEnvironment?
946
947 /* INeoEnv */
948 bool Invoke_(AVSValue *result, const AVSValue& implicit_last,
949 const char* name, const Function *f, const AVSValue& args, const char* const* arg_names,
950 InternalEnvironment* env_thread, bool is_runtime);
951
952 PDevice GetDevice(AvsDeviceType device_type, int device_index) const;
953 int SetMemoryMax(AvsDeviceType type, int index, int mem);
954
955 PVideoFrame GetOnDeviceFrame(const PVideoFrame& src, Device* device);
956 void ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion, InternalEnvironment *env);
957 ThreadPool* NewThreadPool(size_t nThreads);
958 void SetGraphAnalysis(bool enable) { graphAnalysisEnable = enable; }
959
960 char* ListAutoloadDirs();
961
962 2264 void IncEnvCount() { InterlockedIncrement(&EnvCount); }
963 2249 void DecEnvCount() { InterlockedDecrement(&EnvCount); }
964
965 2265 ConcurrentVarStringFrame* GetTopFrame() { return &top_frame; }
966 void SetCacheMode(CacheMode mode) { cacheMode = mode; }
967 5 CacheMode GetCacheMode() { return cacheMode; }
968 void SetDeviceOpt(DeviceOpt opt, int val);
969 size_t GetInvokeStackSize() { return invoke_stack.size(); }
970
971 void UpdateFunctionExports(const char* funcName, const char* funcParams, const char* exportVar);
972
973 // public, AVSMap error should access
974 void ThrowError(const char* fmt, ...);
975
976 453 ThreadScriptEnvironment* GetMainThreadEnv() { return threadEnv.get(); }
977
978 class VFBStorage : public VideoFrameBuffer {
979 public:
980 std::atomic<int64_t> last_freed_timestamp;
981 int free_count;
982 int margin;
983 PGraphMemoryNode memory_node;
984
985 // Helper to update VFB timestamp from external code
986 static void UpdateVFBFreeTimestamp(VideoFrameBuffer* vfb) {
987 static std::atomic<int64_t> global_free_counter{ 0 };
988 static_cast<VFBStorage*>(vfb)->last_freed_timestamp = ++global_free_counter;
989 }
990
991 VFBStorage()
992 : VideoFrameBuffer(),
993 free_count(0),
994 margin(0)
995 {
996 }
997
998 891 VFBStorage(int size, int margin, Device* device)
999 891 : VideoFrameBuffer(size, margin, device),
1000 891 free_count(0),
1001
1/2
✓ Branch 3 → 4 taken 891 times.
✗ Branch 3 → 5 not taken.
891 margin(margin)
1002 {
1003 891 }
1004
1005 903 void Attach(FilterGraphNode* node) {
1006
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 903 times.
903 if (memory_node) {
1007 memory_node->OnFree(data_size, device);
1008 memory_node = nullptr;
1009 }
1010
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 13 taken 903 times.
903 if (node != nullptr) {
1011 memory_node = node->GetMemoryNode();
1012 memory_node->OnAllocate(data_size, device);
1013 }
1014 903 }
1015
1016 891 ~VFBStorage() {
1017
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 891 times.
891 if (memory_node) {
1018 memory_node->OnFree(data_size, device);
1019 memory_node = nullptr;
1020 }
1021 #ifdef _DEBUG
1022 if (data && (device->device_type == DEV_TYPE_CPU)) {
1023 // check buffer overrun
1024 int* pInt = (int*)(data + margin + data_size);
1025 if (pInt[0] != 0xDEADBEEF ||
1026 pInt[1] != 0xDEADBEEF ||
1027 pInt[2] != 0xDEADBEEF ||
1028 pInt[3] != 0xDEADBEEF)
1029 {
1030 printf("Buffer overrun!!!\n");
1031 }
1032 }
1033 #endif
1034 891 }
1035 };
1036
1037 private:
1038 typedef IScriptEnvironment::NotFound NotFound;
1039 typedef IScriptEnvironment::ApplyFunc ApplyFunc;
1040
1041 // Tritical May 2005
1042 // Note order here!!
1043 // AtExiter has functions which
1044 // rely on StringDump elements.
1045 ConcurrentVarStringFrame top_frame;
1046 std::unique_ptr<ThreadScriptEnvironment> threadEnv;
1047 std::mutex string_mutex;
1048
1049 AtExiter at_exit;
1050 ThreadPool* thread_pool;
1051
1052 PluginManager* plugin_manager;
1053 std::recursive_mutex plugin_mutex;
1054
1055 long EnvCount; // for ThreadScriptEnvironment leak detection
1056
1057 void VThrowError(const char* fmt, va_list va);
1058
1059 const Function* Lookup(const char* search_name, const AVSValue* args, size_t num_args,
1060 bool &pstrict, size_t args_names_count, const char* const* arg_names, IScriptEnvironment2* env_thread);
1061 bool CheckArguments(const Function* f, const AVSValue* args, size_t num_args,
1062 bool &pstrict, size_t args_names_count, const char* const* arg_names);
1063 std::unordered_map<IClip*, ClipDataStore> clip_data;
1064
1065 void ExportBuiltinFilters();
1066
1067 bool PlanarChromaAlignmentState;
1068
1069 long hrfromcoinit;
1070 uint32_t coinitThreadId;
1071
1072 struct DebugTimestampedFrame
1073 {
1074 VideoFrame* frame;
1075
1076 #ifdef _DEBUG
1077 std::chrono::time_point<std::chrono::high_resolution_clock> timestamp;
1078 #endif
1079
1080 940 DebugTimestampedFrame(VideoFrame* _frame)
1081 940 : frame(_frame)
1082 #ifdef _DEBUG
1083 , timestamp(std::chrono::high_resolution_clock::now())
1084 #endif
1085 940 {}
1086 };
1087
1088 typedef std::vector<DebugTimestampedFrame> VideoFrameArrayType;
1089 typedef std::map<VideoFrameBuffer *, VideoFrameArrayType> FrameBufferRegistryType;
1090 typedef std::map<size_t, FrameBufferRegistryType> FrameRegistryType2; // post r1825 P.F.
1091 typedef mapped_list<AvsCache*> CacheRegistryType;
1092
1093
1094 FrameRegistryType2 FrameRegistry2; // P.F.
1095 #ifdef _DEBUG
1096 void ListFrameRegistry(size_t min_size, size_t max_size, bool someframes);
1097 #endif
1098
1099 std::unique_ptr<DeviceManager> Devices;
1100 CacheRegistryType CacheRegistry;
1101 AvsCache* FrontCache;
1102 VideoFrame* GetNewFrame(size_t vfb_size, size_t margin, Device* device);
1103 VideoFrame* GetFrameFromRegistry(size_t vfb_size, Device* device);
1104 void RegisterSubFrameInRegistry(size_t vfb_size, VideoFrameBuffer* vfb, VideoFrame* new_frame);
1105 void ShrinkCache(Device* device);
1106 VideoFrame* AllocateFrame(size_t vfb_size, size_t margin, Device* device);
1107 std::recursive_mutex memory_mutex;
1108 std::recursive_mutex invoke_mutex; // 3.7.2
1109 // 3.7.2:
1110 // Distinct invoke mutex from memory_mutex because a background GetFrame and Invoke would deadlock
1111 // when called specially by AvsPMod: Eval, then the resulting clip variable is ConvertToRGB32'd
1112 // by using Invoke.
1113 //
1114 // GetFrame can require accessing FrameRegistry (NewVideoFrame).
1115 // When an Clip is obtained from Eval which script has e.g. Prefetch(2) at the end then it runs
1116 // worker threads in the background, doing GetFrame calls.
1117 // When this AVS Clip is further ConvertToYUV444'd (using Invoke) it calles GetFrame(0) in its
1118 // constructor for getting frame properties and prepare the filter upon them. (this is not a real
1119 // frame property but rather a clip property which is the same for all frames, we are using frame#0 for
1120 // frame property source.
1121 //
1122 // Two things are running parallel:
1123 // - a GetFrame from Invoke which holds a memory_mutex, then locks a MT_SERIALIZED mutex.
1124 // - a GetFrame from the prefetcher of the already Eval'd Clip which first holds a
1125 // MT_SERIALIZED mutex then may lock a memory_mutex.
1126 // Finally they deadlock on the source filter's ChildFilter MT guard (MtGuard::GetFrame, MT_SERIALIZED).
1127 // Serialized access from prefetch
1128 // - gets a ChildFilter mutex, calls GetFrame which would require memory_mutex (NewVideoFrame).
1129 // Serialized access from Invoke
1130 // - _Invoke locks the memory_mutex, then would obtain ChildFilter mutex.
1131 //
1132 // Under specific timing conditions which surely happens in tenth of seconds we get deadlock.
1133 //
1134 // Solution: a new invoke_mutex is introduced besides memory_mutex.
1135 // Other sub-tasks are already guarded with other mutexes: e.g. plugin_mutex, string_mutex.
1136 // Note: An earlier attempt to avoid this problem was introducing
1137 // int ScriptEnvironment::suppressThreadCount; (GetSuppressThreadCount)
1138 // "Concurrent GetFrame with Invoke causes deadlock.
1139 // Increment this variable when Invoke running
1140 // to prevent submitting job to threadpool"
1141 // But this cannot handle the above mentioned case, because by that time threadpool is already working.
1142
1143 int frame_align;
1144 int plane_align;
1145
1146 //BufferPool BufferPool;
1147
1148 typedef std::vector<MTGuard*> MTGuardRegistryType;
1149 MTGuardRegistryType MTGuardRegistry;
1150
1151 std::vector <std::unique_ptr<ThreadPool>> ThreadPoolRegistry;
1152 size_t nTotalThreads;
1153 size_t nMaxFilterInstances;
1154
1155 // Members used to reconstruct Association between Invoke() calls and filter instances
1156 std::stack<MtModeEvaluator*> invoke_stack;
1157
1158 // MT mode specifications
1159 std::unordered_map<std::string, std::pair<MtMode, MtWeight>> MtMap;
1160 MtMode DefaultMtMode = MtMode::MT_MULTI_INSTANCE;
1161 static const std::string DEFAULT_MODE_SPECIFIER;
1162
1163 // Auto filter-property injection
1164 struct PropEntry {
1165 std::string key; // frame property key to inject
1166 AVSValue value; // int, float, string, function, or undefined (capture from named arg)
1167 int mode; // AVSPropAppendMode
1168 std::string param_name; // if non-empty: only inject when named call arg 'param_name' == param_match
1169 AVSValue param_match; // the value to compare against (only used when param_name is non-empty)
1170 };
1171 std::unordered_map<std::string, std::vector<PropEntry>> FilterPropMap;
1172 std::unordered_set<std::string> FilterPropPassthroughSet; // filters that need input-prop forwarding
1173
1174 // Logging-related members
1175 int LogLevel;
1176 std::string LogTarget;
1177 std::ofstream LogFileStream;
1178 std::unordered_set<OneTimeLogTicket> LogTickets;
1179
1180 // filter graph
1181 bool graphAnalysisEnable;
1182
1183 typedef std::vector<FilterGraphNode*> GraphNodeRegistryType;
1184 GraphNodeRegistryType GraphNodeRegistry;
1185
1186 CacheMode cacheMode;
1187
1188 int64_t cpuFlagsEx; // extended 64 bits CPU flags
1189 size_t cache_size_L2;
1190
1191 void InitMT();
1192 };
1193
1194 #ifdef ALTERNATIVE_VFB_TIMESTAMP
1195 // wrapper for VideoFrameBuffer destroy
1196 namespace VFBHelper {
1197 void UpdateVFBFreeTimestamp(VideoFrameBuffer* vfb) {
1198 ScriptEnvironment::VFBStorage::UpdateVFBFreeTimestamp(vfb);
1199 }
1200 }
1201 #endif
1202
1203 const std::string ScriptEnvironment::DEFAULT_MODE_SPECIFIER = "DEFAULT_MT_MODE";
1204
1205 // Only ThrowError and Sprintf is implemented(Used by destructor)
1206 class MinimumScriptEnvironment : public IScriptEnvironment {
1207 VarTable var_table;
1208 public:
1209 MinimumScriptEnvironment(ConcurrentVarStringFrame* top_frame) : var_table(top_frame) { }
1210 virtual ~MinimumScriptEnvironment() {}
1211 virtual int __stdcall GetCPUFlags() {
1212 throw AvisynthError("Not Implemented");
1213 }
1214 virtual char* __stdcall SaveString(const char* s, int length = -1) {
1215 return var_table.SaveString(s, length);
1216 }
1217 virtual char* Sprintf(const char* fmt, ...) {
1218 va_list val;
1219 va_start(val, fmt);
1220 char* result = VSprintf(fmt, val);
1221 va_end(val);
1222 return result;
1223 }
1224 virtual char* __stdcall VSprintf(const char* fmt, va_list val) {
1225 try {
1226 std::string str = FormatString(fmt, val);
1227 return var_table.SaveString(str.c_str(), int(str.size())); // SaveString will add the NULL in len mode.
1228 }
1229 catch (...) {
1230 return NULL;
1231 }
1232 }
1233 __declspec(noreturn) virtual void ThrowError(const char* fmt, ...) {
1234 va_list val;
1235 va_start(val, fmt);
1236 VThrowError(fmt, val);
1237 va_end(val);
1238 // Compiler doesn't recognize that the function would not return.
1239 // With this line we are satisfying the noreturn attribute.
1240 // Anyway, this is unreachable code.
1241 std::terminate();
1242 }
1243 void __stdcall VThrowError(const char* fmt, va_list va)
1244 {
1245 std::string msg;
1246 try {
1247 msg = FormatString(fmt, va);
1248 }
1249 catch (...) {
1250 msg = "Exception while processing ScriptEnvironment::ThrowError().";
1251 }
1252 // Throw...
1253 throw AvisynthError(var_table.SaveString(msg.c_str()));
1254 }
1255 virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) {
1256 throw AvisynthError("Not Implemented");
1257 }
1258 virtual bool __stdcall FunctionExists(const char* name) {
1259 throw AvisynthError("Not Implemented");
1260 }
1261 virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char* const* arg_names = 0) {
1262 throw AvisynthError("Not Implemented");
1263 }
1264 virtual AVSValue __stdcall GetVar(const char* name) {
1265 throw AvisynthError("Not Implemented");
1266 }
1267 virtual bool __stdcall SetVar(const char* name, const AVSValue& val) {
1268 throw AvisynthError("Not Implemented");
1269 }
1270 virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) {
1271 throw AvisynthError("Not Implemented");
1272 }
1273 virtual void __stdcall PushContext(int level = 0) {
1274 throw AvisynthError("Not Implemented");
1275 }
1276 virtual void __stdcall PopContext() {
1277 throw AvisynthError("Not Implemented");
1278 }
1279 virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align = FRAME_ALIGN) {
1280 throw AvisynthError("Not Implemented");
1281 }
1282 virtual bool __stdcall MakeWritable(PVideoFrame* pvf) {
1283 throw AvisynthError("Not Implemented");
1284 }
1285 virtual void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) {
1286 throw AvisynthError("Not Implemented");
1287 }
1288 virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) {
1289 throw AvisynthError("Not Implemented");
1290 }
1291 virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) {
1292 throw AvisynthError("Not Implemented");
1293 }
1294 virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) {
1295 throw AvisynthError("Not Implemented");
1296 }
1297 virtual int __stdcall SetMemoryMax(int mem) {
1298 throw AvisynthError("Not Implemented");
1299 }
1300 virtual int __stdcall SetWorkingDir(const char * newdir) {
1301 throw AvisynthError("Not Implemented");
1302 }
1303 virtual void* __stdcall ManageCache(int key, void* data) {
1304 throw AvisynthError("Not Implemented");
1305 }
1306 virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) {
1307 throw AvisynthError("Not Implemented");
1308 }
1309 virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size,
1310 int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) {
1311 throw AvisynthError("Not Implemented");
1312 }
1313 virtual void __stdcall DeleteScriptEnvironment() {
1314 throw AvisynthError("Not Implemented");
1315 }
1316 virtual void __stdcall ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size,
1317 int textcolor, int halocolor, int bgcolor) {
1318 throw AvisynthError("Not Implemented");
1319 }
1320 virtual const AVS_Linkage* __stdcall GetAVSLinkage() {
1321 throw AvisynthError("Not Implemented");
1322 }
1323 virtual AVSValue __stdcall GetVarDef(const char* name, const AVSValue& def = AVSValue()) {
1324 throw AvisynthError("Not Implemented");
1325 }
1326 };
1327
1328 /* ---------------------------------------------------------------------------------
1329 * Interface transformation hack
1330 * ---------------------------------------------------------------------------------
1331 */
1332 31 InternalEnvironment* GetAndRevealCamouflagedEnv(IScriptEnvironment* env) {
1333 InternalEnvironment* IEnv;
1334
1335 // This function is called from CacheGuard::GetFrame/GetAudio, Prefetcher::GetFrame/GetAudio,
1336 // ScriptClip::GetFrame, ConditionalFilter::GetFrame, and other runtime filters.
1337 // When GetFrame is called from an AviSynth C++ 2.5 or PreV11C plugin constructor (xx_Create),
1338 // or such a plugin is inside a runtime function, then
1339 // 'env' is a disguised IScriptEnvironment_Avs25/AvsPreV11C, which we cannot
1340 // static_cast to InternalEnvironment directly.
1341 // We need to determine whether the environment is v2.5/PreV11C and act accordingly.
1342
1343
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 31 times.
31 if (env->ManageCache((int)MC_QueryAvs25, nullptr) == (intptr_t*)1) {
1344 IEnv = static_cast<InternalEnvironment*>(reinterpret_cast<IScriptEnvironment_Avs25*>(env));
1345 }
1346
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 12 taken 31 times.
31 else if (env->ManageCache((int)MC_QueryAvsPreV11C, nullptr) == (intptr_t*)1) {
1347 IEnv = static_cast<InternalEnvironment*>(reinterpret_cast<IScriptEnvironment_AvsPreV11C*>(env));
1348 }
1349 else {
1350 31 IEnv = static_cast<InternalEnvironment*>(env);
1351 }
1352 31 return IEnv;
1353 }
1354
1355 /* ---------------------------------------------------------------------------------
1356 * Per thread data
1357 * ---------------------------------------------------------------------------------
1358 */
1359 struct ScriptEnvironmentTLS
1360 {
1361 const int thread_id;
1362 // PF 161223 why do we need thread-local global variables?
1363 // comment remains here until it gets cleared, anyway, I make it of no use
1364 VarTable var_table;
1365 BufferPool buffer_pool;
1366 Device* currentDevice;
1367 bool closing; // Used to avoid deadlock, if vartable is being accessed while shutting down (Popcontext)
1368 bool supressCaching;
1369 int ImportDepth;
1370 int getFrameRecursiveCount;
1371
1372 // Concurrent GetFrame with Invoke causes deadlock.
1373 // Increment this variable when Invoke running
1374 // to prevent submitting job to threadpool
1375 int suppressThreadCount;
1376
1377 FilterGraphNode* currentGraphNode;
1378 volatile long refcount;
1379
1380 2265 ScriptEnvironmentTLS(int thread_id, InternalEnvironment* core)
1381 2265 : thread_id(thread_id)
1382 2265 , var_table(core->GetTopFrame())
1383
1/2
✓ Branch 4 → 5 taken 2263 times.
✗ Branch 4 → 6 not taken.
2265 , buffer_pool(core)
1384 2263 , currentDevice(NULL)
1385 2263 , closing(false)
1386 2263 , supressCaching(false)
1387 2263 , ImportDepth(0)
1388 2263 , getFrameRecursiveCount(0)
1389 2263 , suppressThreadCount(0)
1390 2263 , currentGraphNode(nullptr)
1391 2263 , refcount(1)
1392 {
1393 2263 }
1394 };
1395
1396 // per thread data is bound to a thread (not ThreadScriptEnvironment)
1397 // since some filter (e.g. svpflow1) ignores env given for GetFrame, and always use main thread's env.
1398 // this is a work-around for that.
1399 #if defined(AVS_WINDOWS) && !defined(__GNUC__)
1400 # ifdef XP_TLS
1401 extern DWORD dwTlsIndex;
1402 # else
1403 // does not work on XP when DLL is dynamic loaded. see dwTlsIndex instead
1404 __declspec(thread) ScriptEnvironmentTLS* g_TLS = nullptr;
1405 # endif
1406 #else
1407 __thread ScriptEnvironmentTLS* g_TLS = nullptr;
1408 #endif
1409
1410 class ThreadScriptEnvironment : public InternalEnvironment
1411 {
1412 ScriptEnvironment* core;
1413 ScriptEnvironmentTLS* coreTLS;
1414 ScriptEnvironmentTLS myTLS;
1415 public:
1416
1417 2265 ThreadScriptEnvironment(int thread_id, ScriptEnvironment* core, ScriptEnvironmentTLS* coreTLS)
1418 4530 : core(core)
1419 2265 , coreTLS(coreTLS)
1420
1/2
✓ Branch 3 → 4 taken 2263 times.
✗ Branch 3 → 14 not taken.
2265 , myTLS(thread_id, this)
1421 {
1422
2/2
✓ Branch 4 → 5 taken 453 times.
✓ Branch 4 → 6 taken 1810 times.
2263 if (coreTLS == nullptr) {
1423 // when this is main thread TLS
1424 453 this->coreTLS = &myTLS;
1425 }
1426
2/2
✓ Branch 6 → 7 taken 1810 times.
✓ Branch 6 → 10 taken 453 times.
2263 if (thread_id != 0) {
1427 // thread pool thread
1428 #ifdef XP_TLS
1429 ScriptEnvironmentTLS* g_TLS = (ScriptEnvironmentTLS*)(TlsGetValue(dwTlsIndex));
1430 #endif
1431
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1810 times.
1810 if (g_TLS != nullptr) {
1432 ThrowError("Detected multiple ScriptEnvironmentTLSs for a single thread");
1433 }
1434 1811 g_TLS = &myTLS;
1435 #ifdef XP_TLS
1436 if (!TlsSetValue(dwTlsIndex, g_TLS)) {
1437 ThrowError("Could not store thread local value for ScriptEnvironmentTLS");
1438 }
1439 #endif
1440 }
1441 2264 core->IncEnvCount(); // for leak detection
1442 2263 }
1443
1444 4513 ~ThreadScriptEnvironment() {
1445 2257 core->DecEnvCount(); // for leak detection
1446 4513 }
1447
1448 906 ScriptEnvironmentTLS* GetTLS() { return &myTLS; }
1449
1450 #ifdef XP_TLS
1451 // a ? : b, evaluate 'a' only once
1452 #define IFNULL(a, b) ([&](){ auto val = (a); return ((val) == nullptr ? (b) : (val)); }())
1453 #define DISPATCH(name) IFNULL((ScriptEnvironmentTLS*)(TlsGetValue(dwTlsIndex)), coreTLS)->name
1454 #else
1455 #define DISPATCH(name) (g_TLS ? g_TLS : coreTLS)->name
1456 #endif
1457
1458
1459 AVSValue __stdcall GetVar(const char* name)
1460 {
1461 if (DISPATCH(closing)) return AVSValue(); // We easily risk being inside the critical section below, while deleting variables.
1462 AVSValue val;
1463 if (DISPATCH(var_table).Get(name, &val))
1464 return val;
1465 else
1466 throw IScriptEnvironment::NotFound();
1467 }
1468
1469 19 bool __stdcall SetVar(const char* name, const AVSValue& val)
1470 {
1471
2/4
✓ Branch 2 → 3 taken 19 times.
✗ Branch 2 → 4 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 19 times.
19 if (DISPATCH(closing)) return true; // We easily risk being inside the critical section below, while deleting variables.
1472
1/2
✓ Branch 7 → 8 taken 19 times.
✗ Branch 7 → 9 not taken.
19 return DISPATCH(var_table).Set(name, val);
1473 }
1474
1475 256852 bool __stdcall SetGlobalVar(const char* name, const AVSValue& val)
1476 {
1477
2/4
✓ Branch 2 → 3 taken 256852 times.
✗ Branch 2 → 4 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 256852 times.
256852 if (DISPATCH(closing)) return true; // We easily risk being inside the critical section below, while deleting variables.
1478
1/2
✓ Branch 7 → 8 taken 256852 times.
✗ Branch 7 → 9 not taken.
256852 return DISPATCH(var_table).SetGlobal(name, val);
1479 }
1480
1481 void __stdcall PushContext(int level = 0)
1482 {
1483 DISPATCH(var_table).Push();
1484 }
1485
1486 void __stdcall PopContext()
1487 {
1488 DISPATCH(var_table).Pop();
1489 }
1490
1491 void __stdcall PushContextGlobal()
1492 {
1493 DISPATCH(var_table).PushGlobal();
1494 }
1495
1496 void __stdcall PopContextGlobal()
1497 {
1498 DISPATCH(var_table).PopGlobal();
1499 }
1500
1501 3752 bool __stdcall GetVarTry(const char* name, AVSValue* val) const
1502 {
1503
2/4
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3752 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3752 times.
3752 if (DISPATCH(closing)) return false; // We easily risk being inside the critical section below, while deleting variables.
1504
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 3752 times.
3752 return DISPATCH(var_table).Get(name, val);
1505 }
1506
1507 10 AVSValue __stdcall GetVarDef(const char* name, const AVSValue& def)
1508 {
1509
2/6
✓ Branch 2 → 3 taken 10 times.
✗ Branch 2 → 4 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 10 times.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 20 not taken.
10 if (DISPATCH(closing)) return def; // We easily risk being inside the critical section below, while deleting variables.
1510
1/2
✓ Branch 8 → 9 taken 10 times.
✗ Branch 8 → 20 not taken.
10 AVSValue val;
1511
3/4
✓ Branch 9 → 10 taken 10 times.
✗ Branch 9 → 18 not taken.
✓ Branch 10 → 11 taken 6 times.
✓ Branch 10 → 13 taken 4 times.
10 if (this->GetVarTry(name, &val))
1512
1/2
✓ Branch 11 → 12 taken 6 times.
✗ Branch 11 → 18 not taken.
6 return val;
1513 else
1514
1/2
✓ Branch 13 → 14 taken 4 times.
✗ Branch 13 → 18 not taken.
4 return def;
1515 10 }
1516
1517 bool __stdcall GetVarBool(const char* name, bool def) const
1518 {
1519 if (DISPATCH(closing)) return false; // We easily risk being inside the critical section below, while deleting variables.
1520 AVSValue val;
1521 if (this->GetVarTry(name, &val))
1522 return val.AsBool(def);
1523 else
1524 return def;
1525 }
1526
1527 int __stdcall GetVarInt(const char* name, int def) const
1528 {
1529 if (DISPATCH(closing)) return def; // We easily risk being inside the critical section below, while deleting variables.
1530 AVSValue val;
1531 if (this->GetVarTry(name, &val))
1532 return val.AsInt(def);
1533 else
1534 return def;
1535 }
1536
1537 108 double __stdcall GetVarDouble(const char* name, double def) const
1538 {
1539
2/4
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 108 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 108 times.
108 if (DISPATCH(closing)) return def; // We easily risk being inside the critical section below, while deleting variables.
1540
1/2
✓ Branch 7 → 8 taken 108 times.
✗ Branch 7 → 18 not taken.
108 AVSValue val;
1541
3/4
✓ Branch 8 → 9 taken 108 times.
✗ Branch 8 → 16 not taken.
✓ Branch 9 → 10 taken 3 times.
✓ Branch 9 → 12 taken 105 times.
108 if (this->GetVarTry(name, &val))
1542
1/2
✓ Branch 10 → 11 taken 3 times.
✗ Branch 10 → 16 not taken.
3 return val.AsDblDef(def);
1543 else
1544 105 return def;
1545 108 }
1546
1547 3624 const char* __stdcall GetVarString(const char* name, const char* def) const
1548 {
1549
2/4
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 3624 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3624 times.
3624 if (DISPATCH(closing)) return def; // We easily risk being inside the critical section below, while deleting variables.
1550
1/2
✓ Branch 7 → 8 taken 3624 times.
✗ Branch 7 → 18 not taken.
3624 AVSValue val;
1551
3/4
✓ Branch 8 → 9 taken 3624 times.
✗ Branch 8 → 16 not taken.
✓ Branch 9 → 10 taken 1812 times.
✓ Branch 9 → 12 taken 1812 times.
3624 if (this->GetVarTry(name, &val))
1552
1/2
✓ Branch 10 → 11 taken 1812 times.
✗ Branch 10 → 16 not taken.
1812 return val.AsString(def);
1553 else
1554 1812 return def;
1555 3624 }
1556
1557 #ifdef _MSC_VER
1558 #pragma warning(push)
1559 #pragma warning(disable: 4244) // conversion from __int64, possible loss of data
1560 #endif
1561 int64_t __stdcall GetVarLong(const char* name, int64_t def) const
1562 {
1563 if (DISPATCH(closing)) return def; // We easily risk being inside the critical section below, while deleting variables.
1564 AVSValue val;
1565 if (this->GetVarTry(name, &val))
1566 return val.AsLong(def); // v11: real int64 support
1567 else
1568 return def;
1569 }
1570 #ifdef _MSC_VER
1571 #pragma warning(pop)
1572 #endif
1573
1574 254 void* __stdcall Allocate(size_t nBytes, size_t alignment, AvsAllocType type)
1575 {
1576
3/4
✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 5 taken 250 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 4 times.
254 if ((type != AVS_NORMAL_ALLOC) && (type != AVS_POOLED_ALLOC))
1577 return NULL;
1578
1/2
✓ Branch 5 → 6 taken 254 times.
✗ Branch 5 → 7 not taken.
254 return DISPATCH(buffer_pool).Allocate(nBytes, alignment, type == AVS_POOLED_ALLOC);
1579 }
1580
1581 614 void __stdcall Free(void* ptr)
1582 {
1583
1/2
✓ Branch 2 → 3 taken 614 times.
✗ Branch 2 → 4 not taken.
614 DISPATCH(buffer_pool).Free(ptr);
1584 614 }
1585
1586 5 Device* __stdcall GetCurrentDevice() const
1587 {
1588
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5 times.
5 return DISPATCH(currentDevice);
1589 }
1590
1591 Device* __stdcall SetCurrentDevice(Device* device)
1592 {
1593 Device* old = DISPATCH(currentDevice);
1594 DISPATCH(currentDevice) = device;
1595 return old;
1596 }
1597
1598 629 PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align)
1599 {
1600
1/2
✓ Branch 2 → 3 taken 629 times.
✗ Branch 2 → 4 not taken.
629 return core->NewVideoFrameOnDevice(vi, align, DISPATCH(currentDevice));
1601 }
1602
1603 163 PVideoFrame __stdcall NewVideoFrameP(const VideoInfo& vi, const PVideoFrame *prop_src, int align)
1604 {
1605
1/2
✓ Branch 2 → 3 taken 163 times.
✗ Branch 2 → 4 not taken.
163 return core->NewVideoFrameOnDevice(vi, align, DISPATCH(currentDevice), prop_src);
1606 }
1607
1608 void* __stdcall GetDeviceStream()
1609 {
1610 return DISPATCH(currentDevice)->GetComputeStream();
1611 }
1612
1613 void __stdcall DeviceAddCallback(void(*cb)(void*), void* user_data)
1614 {
1615 DeviceCompleteCallbackData cbdata = { cb, user_data };
1616 DISPATCH(currentDevice)->AddCompleteCallback(cbdata);
1617 }
1618
1619 PVideoFrame __stdcall GetFrame(PClip c, int n, const PDevice& device)
1620 {
1621 DeviceSetter setter(this, (Device*)(void*)device);
1622 return c->GetFrame(n, this);
1623 }
1624
1625
1626 /* ---------------------------------------------------------------------------------
1627 * S T U B S
1628 * ---------------------------------------------------------------------------------
1629 */
1630
1631 bool __stdcall InternalFunctionExists(const char* name)
1632 {
1633 return core->InternalFunctionExists(name);
1634 }
1635
1636 508 void __stdcall AdjustMemoryConsumption(size_t amount, bool minus)
1637 {
1638 508 core->AdjustMemoryConsumption(amount, minus);
1639 508 }
1640
1641 void __stdcall CheckVersion(int version)
1642 {
1643 core->CheckVersion(version);
1644 }
1645
1646 260 int __stdcall GetCPUFlags()
1647 {
1648 260 return core->GetCPUFlags();
1649 }
1650
1651 3 int64_t __stdcall GetCPUFlagsEx()
1652 {
1653 3 return core->GetCPUFlagsEx();
1654 }
1655
1656 256404 char* __stdcall SaveString(const char* s, int length = -1)
1657 {
1658
1/2
✓ Branch 2 → 3 taken 256404 times.
✗ Branch 2 → 4 not taken.
256404 return DISPATCH(var_table).SaveString(s, length);
1659 }
1660
1661 char* __stdcall SaveString(const char* s, int length, bool escape)
1662 {
1663 return DISPATCH(var_table).SaveString(s, length, escape);
1664 }
1665
1666 char* Sprintf(const char* fmt, ...)
1667 {
1668 va_list val;
1669 va_start(val, fmt);
1670 // do not call core->Sprintf, because cannot pass ... further
1671 char* result = VSprintf(fmt, val);
1672 va_end(val);
1673 return result;
1674 }
1675
1676 char* __stdcall VSprintf(const char* fmt, va_list val)
1677 {
1678 try {
1679 std::string str = FormatString(fmt, val);
1680 return DISPATCH(var_table).SaveString(str.c_str(), int(str.size())); // SaveString will add the NULL in len mode.
1681 }
1682 catch (...) {
1683 return NULL;
1684 }
1685 }
1686
1687 33 void ThrowError(const char* fmt, ...)
1688 {
1689 va_list val;
1690 33 va_start(val, fmt);
1691
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 33 times.
33 VThrowError(fmt, val);
1692 va_end(val);
1693 }
1694
1695 33 void __stdcall VThrowError(const char* fmt, va_list va)
1696 {
1697 33 std::string msg;
1698 try {
1699
1/2
✓ Branch 3 → 4 taken 33 times.
✗ Branch 3 → 17 not taken.
33 msg = FormatString(fmt, va);
1700 }
1701 catch (...) {
1702 msg = "Exception while processing ScriptEnvironment::ThrowError().";
1703 }
1704
1705 // Also log the error before throwing
1706
1/2
✓ Branch 8 → 9 taken 33 times.
✗ Branch 8 → 26 not taken.
33 this->LogMsg(LOGLEVEL_ERROR, msg.c_str());
1707
1708 // Throw...
1709
2/4
✓ Branch 10 → 11 taken 33 times.
✗ Branch 10 → 12 not taken.
✓ Branch 14 → 15 taken 33 times.
✗ Branch 14 → 24 not taken.
33 throw AvisynthError(DISPATCH(var_table).SaveString(msg.c_str()));
1710 33 }
1711
1712 3 PVideoFrame __stdcall SubframePlanarA(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA)
1713 {
1714
2/4
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 10 not taken.
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 8 not taken.
3 return core->SubframePlanarA(src, rel_offset, new_pitch, new_row_size, new_height, rel_offsetU, rel_offsetV, new_pitchUV, rel_offsetA);
1715 }
1716
1717 bool __stdcall MakePropertyWritable(PVideoFrame* pvf)
1718 {
1719 return core->MakePropertyWritable(pvf);
1720 }
1721
1722 2 void __stdcall copyFrameProps(const PVideoFrame& src, PVideoFrame& dst)
1723 {
1724 2 core->copyFrameProps(src, dst);
1725 2 }
1726
1727 48 const AVSMap* __stdcall getFramePropsRO(const PVideoFrame& frame)
1728 {
1729 48 return core->getFramePropsRO(frame);
1730 }
1731 60 AVSMap* __stdcall getFramePropsRW(PVideoFrame& frame)
1732 {
1733 60 return core->getFramePropsRW(frame);
1734 }
1735 1 int __stdcall propNumKeys(const AVSMap* map)
1736 {
1737 1 return core->propNumKeys(map);
1738 }
1739 1 const char* __stdcall propGetKey(const AVSMap* map, int index)
1740 {
1741 1 return core->propGetKey(map, index);
1742 }
1743 77 int __stdcall propNumElements(const AVSMap* map, const char* key)
1744 {
1745 77 return core->propNumElements(map, key);
1746 }
1747 1 char __stdcall propGetType(const AVSMap* map, const char* key)
1748 {
1749 1 return core->propGetType(map, key);
1750 }
1751 34 int __stdcall propDeleteKey(AVSMap* map, const char* key)
1752 {
1753 34 return core->propDeleteKey(map, key);
1754 }
1755 21 int64_t __stdcall propGetInt(const AVSMap* map, const char* key, int index, int* error)
1756 {
1757 21 return core->propGetInt(map, key, index, error);
1758 }
1759 20 int __stdcall propGetIntSaturated(const AVSMap* map, const char* key, int index, int* error)
1760 {
1761 20 return core->propGetIntSaturated(map, key, index, error);
1762 }
1763 double __stdcall propGetFloat(const AVSMap* map, const char* key, int index, int* error)
1764 {
1765 return core->propGetFloat(map, key, index, error);
1766 }
1767 float __stdcall propGetFloatSaturated(const AVSMap* map, const char* key, int index, int* error)
1768 {
1769 return core->propGetFloatSaturated(map, key, index, error);
1770 }
1771 1 const char* __stdcall propGetData(const AVSMap* map, const char* key, int index, int* error)
1772 {
1773 1 return core->propGetData(map, key, index, error);
1774 }
1775 int __stdcall propGetDataSize(const AVSMap* map, const char* key, int index, int* error)
1776 {
1777 return core->propGetDataSize(map, key, index, error);
1778 }
1779 int __stdcall propGetDataTypeHint(const AVSMap* map, const char* key, int index, int* error)
1780 {
1781 return core->propGetDataTypeHint(map, key, index, error);
1782 }
1783 PClip __stdcall propGetClip(const AVSMap* map, const char* key, int index, int* error)
1784 {
1785 return core->propGetClip(map, key, index, error);
1786 }
1787 const PVideoFrame __stdcall propGetFrame(const AVSMap* map, const char* key, int index, int* error)
1788 {
1789 return core->propGetFrame(map, key, index, error);
1790 }
1791 80 int __stdcall propSetInt(AVSMap* map, const char* key, int64_t i, int append)
1792 {
1793 80 return core->propSetInt(map, key, i, append);
1794 }
1795 int __stdcall propSetFloat(AVSMap* map, const char* key, double d, int append)
1796 {
1797 return core->propSetFloat(map, key, d, append);
1798 }
1799 int __stdcall propSetData(AVSMap* map, const char* key, const char* d, int length, int append)
1800 {
1801 return core->propSetData(map, key, d, length, append);
1802 }
1803 2 int __stdcall propSetDataH(AVSMap* map, const char* key, const char* d, int length, int type, int append)
1804 {
1805 2 return core->propSetDataH(map, key, d, length, type, append);
1806 }
1807 int __stdcall propSetClip(AVSMap* map, const char* key, PClip& clip, int append)
1808 {
1809 return core->propSetClip(map, key, clip, append);
1810 }
1811 int __stdcall propSetFrame(AVSMap* map, const char* key, const PVideoFrame& frame, int append)
1812 {
1813 return core->propSetFrame(map, key, frame, append);
1814 }
1815
1816 const int64_t* __stdcall propGetIntArray(const AVSMap* map, const char* key, int* error)
1817 {
1818 return core->propGetIntArray(map, key, error);
1819 }
1820
1821 const double* __stdcall propGetFloatArray(const AVSMap* map, const char* key, int* error)
1822 {
1823 return core->propGetFloatArray(map, key, error);
1824 }
1825
1826 int __stdcall propSetIntArray(AVSMap* map, const char* key, const int64_t* i, int size)
1827 {
1828 return core->propSetIntArray(map, key, i, size);
1829 }
1830
1831 int __stdcall propSetFloatArray(AVSMap* map, const char* key, const double* d, int size)
1832 {
1833 return core->propSetFloatArray(map, key, d, size);
1834 }
1835
1836 15 AVSMap* __stdcall createMap()
1837 {
1838 15 return core->createMap();
1839 }
1840
1841 15 void __stdcall freeMap(AVSMap* map)
1842 {
1843 15 core->freeMap(map);
1844 15 }
1845
1846 void __stdcall clearMap(AVSMap* map)
1847 {
1848 core->clearMap(map);
1849 }
1850
1851 void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data = 0)
1852 {
1853 core->AddFunction(name, params, apply, user_data);
1854 }
1855
1856 void __stdcall AddFunction25(const char* name, const char* params, ApplyFunc apply, void* user_data = 0)
1857 {
1858 core->AddFunction25(name, params, apply, user_data);
1859 }
1860
1861 void __stdcall AddFunctionPreV11C(const char* name, const char* params, ApplyFunc apply, void* user_data = 0)
1862 {
1863 core->AddFunctionPreV11C(name, params, apply, user_data);
1864 }
1865
1866 bool __stdcall FunctionExists(const char* name)
1867 {
1868 return core->FunctionExists(name);
1869 }
1870
1871 7 bool IsRuntime() {
1872 // When invoked from GetFrame/GetAudio, skip all cache and mt mecanism
1873 7 bool is_runtime = true;
1874
1875 #ifdef XP_TLS
1876 ScriptEnvironmentTLS* g_TLS = (ScriptEnvironmentTLS*)(TlsGetValue(dwTlsIndex));
1877 #endif
1878
1/2
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 6 not taken.
7 if (g_TLS == nullptr) { // not called by thread
1879
1/2
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 6 not taken.
7 if (GetFrameRecursiveCount() == 0) { // not called by GetFrame
1880 7 is_runtime = false;
1881 }
1882 }
1883
1884 7 return is_runtime;
1885 }
1886
1887 // thrower Invoke, IScriptEnvironment
1888 7 AVSValue __stdcall Invoke(const char* name,
1889 const AVSValue args, const char* const* arg_names)
1890 {
1891 7 AVSValue result;
1892
4/8
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 15 not taken.
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 14 not taken.
✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 12 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 7 times.
7 if (!core->Invoke_(&result, AVSValue(), name, nullptr, args, arg_names, this, IsRuntime()))
1893 {
1894 throw NotFound();
1895 }
1896 7 return result;
1897 }
1898
1899 // thrower Invoke, IScriptEnvironment_Avs25
1900 AVSValue __stdcall Invoke25(const char* name,
1901 const AVSValue args, const char* const* arg_names)
1902 {
1903 AVSValue result;
1904 const bool success = core->Invoke_(&result, AVSValue(), name, nullptr, args, arg_names, this, IsRuntime());
1905
1906 ((AVSValue*)&args)->MarkArrayAsNonDeepCopy();
1907 // In Avisynth+, arrays are deep-copied and freed. The 'args' array elements are freed upon Invoke25's exit.
1908 // But this call comes from a filter using Avisynth 2.5 header, where the caller would free the elements again, causing a crash.
1909 // We assume memory freeing is handled by the cpp 2.5 plugin.
1910 // If 'args' is an array, we convert its type to 'void' to prevent AVSValue destructor from freeing the array elements.
1911 // Unlike Avisynth+, the passed AVSValue is not a smart deep-copied array; it was allocated by the client and filled manually.
1912
1913 if (!success)
1914 throw NotFound();
1915
1916 // 2.5 plugins don't know about long and double types.
1917 if (result.GetType() == AvsValueType::VALUE_TYPE_LONG) // real 64 bit integer
1918 result = result.AsInt();
1919 else if (result.GetType() == AvsValueType::VALUE_TYPE_DOUBLE) // real 64 bit double
1920 result = result.AsFloatf();
1921
1922 return result;
1923 }
1924
1925 // thrower Invoke, IScriptEnvironment_AvsPreV11C
1926 AVSValue __stdcall InvokePreV11C(const char* name,
1927 const AVSValue args, const char* const* arg_names)
1928 {
1929 AVSValue result;
1930
1931 const bool success = core->Invoke_(&result, AVSValue(), name, nullptr, args, arg_names, this, IsRuntime());
1932
1933 if (!success)
1934 throw NotFound();
1935
1936 // PreV11C plugins don't know about 'l'ong and 'd'ouble basic types.
1937 // Convert them to 'i'nt and 'f'loat respectively.
1938 // A preV11C interface plugin (no avisynth_c_plugin_init2) can call avs_invoke
1939 // which would (if unchanged) result in a 64 bit type, like Pi() returns double.
1940 // Example call:
1941 // AVS_Value ver = avs_invoke(Env, "Pi", avs_new_value_array(NULL, 0), NULL);
1942 // We convert only basic types, and not handling array type return values here,
1943 // to look into whether it contains 64 bit elements accidentally.
1944 // Neither is an old C plugin expected to handle array return values.
1945 // Anyway, proper dyn array support comes only from v11 on C API.
1946 if (result.GetType() == AvsValueType::VALUE_TYPE_LONG) // real 64 bit integer
1947 result = result.AsInt(); // to 32 bit integer
1948 else if (result.GetType() == AvsValueType::VALUE_TYPE_DOUBLE) // real 64 bit double
1949 result = result.AsFloatf(); // to 32 bit float
1950
1951 return result;
1952 }
1953
1954 // no-throw Invoke, IScriptEnvironment, Ex-IS2
1955 bool __stdcall InvokeTry(AVSValue* result,
1956 const char* name, const AVSValue& args, const char* const* arg_names)
1957 {
1958 return core->Invoke_(result, AVSValue(), name, nullptr, args, arg_names, this, IsRuntime());
1959 }
1960
1961 // thrower Invoke + implicit last, since IS V8
1962 // created to have throw and non-throw (xxxxTry) versions from all Invokes
1963 AVSValue __stdcall Invoke2(const AVSValue& implicit_last,
1964 const char* name, const AVSValue args, const char* const* arg_names)
1965 {
1966 AVSValue result;
1967 if (!core->Invoke_(&result, implicit_last,
1968 name, nullptr, args, arg_names, this, IsRuntime()))
1969 {
1970 throw NotFound();
1971 }
1972 return result;
1973 }
1974
1975 // no-throw Invoke + implicit last, Ex-INeo
1976 bool __stdcall Invoke2Try(AVSValue* result, const AVSValue& implicit_last,
1977 const char* name, const AVSValue args, const char* const* arg_names)
1978 {
1979 return core->Invoke_(result, implicit_last,
1980 name, nullptr, args, arg_names, this, IsRuntime());
1981 }
1982
1983 // thrower Invoke + implicit last + PFunction
1984 AVSValue __stdcall Invoke3(const AVSValue& implicit_last,
1985 const PFunction& func, const AVSValue args, const char* const* arg_names)
1986 {
1987 AVSValue result;
1988 if (!core->Invoke_(&result, implicit_last,
1989 func->GetLegacyName(), func->GetDefinition(), args, arg_names, this, IsRuntime()))
1990 {
1991 throw NotFound();
1992 }
1993 return result;
1994 }
1995
1996 // no-throw Invoke + implicit last + PFunction
1997 bool __stdcall Invoke3Try(AVSValue *result, const AVSValue& implicit_last,
1998 const PFunction& func, const AVSValue args, const char* const* arg_names)
1999 {
2000 return core->Invoke_(result, implicit_last,
2001 func->GetLegacyName(), func->GetDefinition(), args, arg_names, this, IsRuntime());
2002 }
2003
2004 // King of all Invoke versions: no-throw Invoke + implicit last + funtion name + function definition
2005 bool __stdcall Invoke_(AVSValue *result, const AVSValue& implicit_last,
2006 const char* name, const Function *f, const AVSValue& args, const char* const* arg_names)
2007 {
2008 return core->Invoke_(result, implicit_last, name, f, args, arg_names, this, IsRuntime());
2009 }
2010
2011 117 bool __stdcall MakeWritable(PVideoFrame* pvf)
2012 {
2013 117 return core->MakeWritable(pvf);
2014 }
2015
2016 172 void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height)
2017 {
2018 172 core->BitBlt(dstp, dst_pitch, srcp, src_pitch, row_size, height);
2019 172 }
2020
2021 8 void __stdcall AtExit(IScriptEnvironment::ShutdownFunc function, void* user_data)
2022 {
2023 8 core->AtExit(function, user_data);
2024 8 }
2025
2026 14 PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height)
2027 {
2028
2/4
✓ Branch 2 → 3 taken 14 times.
✗ Branch 2 → 10 not taken.
✓ Branch 3 → 4 taken 14 times.
✗ Branch 3 → 8 not taken.
14 return core->Subframe(src, rel_offset, new_pitch, new_row_size, new_height);
2029 }
2030
2031 int __stdcall SetMemoryMax(int mem)
2032 {
2033 return core->SetMemoryMax(mem);
2034 }
2035
2036 int __stdcall SetWorkingDir(const char* newdir)
2037 {
2038 return core->SetWorkingDir(newdir);
2039 }
2040
2041 bool __stdcall AcquireGlobalLock(const char* name) {
2042 return core->AcquireGlobalLock(name);
2043 }
2044
2045 void __stdcall ReleaseGlobalLock(const char* name) {
2046 core->ReleaseGlobalLock(name);
2047 }
2048
2049 89 void* __stdcall ManageCache(int key, void* data)
2050 {
2051
2/2
✓ Branch 2 → 3 taken 58 times.
✓ Branch 2 → 4 taken 31 times.
89 if (
2052
2/2
✓ Branch 3 → 4 taken 31 times.
✓ Branch 3 → 5 taken 27 times.
58 (MANAGE_CACHE_KEYS)key == MC_QueryAvs25 ||
2053 (MANAGE_CACHE_KEYS)key == MC_QueryAvsPreV11C
2054 )
2055 62 return (intptr_t*)0;
2056 27 return core->ManageCache(key, data);
2057 }
2058
2059 void* __stdcall ManageCache25(int key, void* data)
2060 {
2061 // We use a v2.5-special ManageCache call with special key to query if
2062 // env ptr is v2.5 even if casted to IScriptEnvironment
2063 if ((MANAGE_CACHE_KEYS)key == MC_QueryAvs25)
2064 return (intptr_t *)1;
2065 return ManageCache(key, data);
2066 }
2067
2068 void* __stdcall ManageCachePreV11C(int key, void* data)
2069 {
2070 // We use a preV11C-special ManageCache call with special key to query if
2071 // env ptr is preV11C even if casted to IScriptEnvironment
2072 if ((MANAGE_CACHE_KEYS)key == MC_QueryAvsPreV11C)
2073 return (intptr_t*)1;
2074 return ManageCache(key, data);
2075 }
2076
2077 5 bool __stdcall PlanarChromaAlignment(IScriptEnvironment::PlanarChromaAlignmentMode key)
2078 {
2079 5 return core->PlanarChromaAlignment(key);
2080 }
2081
2082 32 PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV)
2083 {
2084
2/4
✓ Branch 2 → 3 taken 32 times.
✗ Branch 2 → 10 not taken.
✓ Branch 3 → 4 taken 32 times.
✗ Branch 3 → 8 not taken.
32 return core->SubframePlanar(src, rel_offset, new_pitch, new_row_size, new_height, rel_offsetU, rel_offsetV, new_pitchUV);
2085 }
2086
2087 453 void __stdcall DeleteScriptEnvironment()
2088 {
2089 #ifdef XP_TLS
2090 ScriptEnvironmentTLS* g_TLS = (ScriptEnvironmentTLS*)(TlsGetValue(dwTlsIndex));
2091 #endif
2092
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 453 times.
453 if (g_TLS != nullptr) {
2093 ThrowError("Cannot delete environment from a TLS proxy.");
2094 }
2095 453 core->DeleteScriptEnvironment();
2096 453 }
2097
2098 1 void __stdcall ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, int textcolor, int halocolor, int bgcolor)
2099 {
2100 1 core->ApplyMessage(frame, vi, message, size, textcolor, halocolor, bgcolor);
2101 1 }
2102
2103 void __stdcall ApplyMessageEx(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, int textcolor, int halocolor, int bgcolor, bool utf8)
2104 {
2105 core->ApplyMessageEx(frame, vi, message, size, textcolor, halocolor, bgcolor, utf8);
2106 }
2107
2108 const AVS_Linkage* __stdcall GetAVSLinkage()
2109 {
2110 return core->GetAVSLinkage();
2111 }
2112
2113 /* IScriptEnvironment2 */
2114 bool __stdcall LoadPlugin(const char* filePath, bool throwOnError, AVSValue* result)
2115 {
2116 return core->LoadPlugin(filePath, throwOnError, result);
2117 }
2118
2119 void __stdcall AddAutoloadDir(const char* dirPath, bool toFront)
2120 {
2121 core->AddAutoloadDir(dirPath, toFront);
2122 }
2123
2124 void __stdcall ClearAutoloadDirs()
2125 {
2126 core->ClearAutoloadDirs();
2127 }
2128
2129 void __stdcall AutoloadPlugins()
2130 {
2131 core->AutoloadPlugins();
2132 }
2133
2134 void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data, const char* exportVar)
2135 {
2136 core->AddFunction(name, params, apply, user_data, exportVar);
2137 }
2138
2139 char* __stdcall ListAutoloadDirs()
2140 {
2141 return core->ListAutoloadDirs();
2142 }
2143
2144 void __stdcall SetFilterProp(const char* filter, const char* key, const AVSValue& value, int mode)
2145 {
2146 core->SetFilterProp(filter, key, value, mode);
2147 }
2148
2149 void __stdcall SetFilterPropConditional(const char* filter, const char* param_name, const AVSValue& param_match,
2150 const char* key, const AVSValue& value, int mode)
2151 {
2152 core->SetFilterPropConditional(filter, param_name, param_match, key, value, mode);
2153 }
2154
2155 const char* __stdcall GetFilterProps()
2156 {
2157 return core->GetFilterProps();
2158 }
2159
2160 void __stdcall SetFilterPropPassthrough(const char* filter)
2161 {
2162 core->SetFilterPropPassthrough(filter);
2163 }
2164
2165 int __stdcall IncrImportDepth()
2166 {
2167 return ++DISPATCH(ImportDepth);
2168 }
2169
2170 int __stdcall DecrImportDepth()
2171 {
2172 return --DISPATCH(ImportDepth);
2173 }
2174
2175 120 size_t __stdcall GetEnvProperty(AvsEnvProperty prop)
2176 {
2177
1/4
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 11 not taken.
✓ Branch 2 → 15 taken 120 times.
120 switch (prop)
2178 {
2179 case AEP_THREAD_ID:
2180 return DISPATCH(thread_id);
2181 case AEP_SUPPRESS_THREAD:
2182 return DISPATCH(suppressThreadCount);
2183 case AEP_GETFRAME_RECURSIVE:
2184 return DISPATCH(getFrameRecursiveCount);
2185 120 default:
2186 120 return core->GetEnvProperty(prop);
2187 }
2188 }
2189
2190 void __stdcall SetFilterMTMode(const char* filter, MtMode mode, bool force)
2191 {
2192 core->SetFilterMTMode(filter, mode, force);
2193 }
2194
2195 7 MtMode __stdcall GetFilterMTMode(const Function* filter, bool* is_forced) const
2196 {
2197 7 return core->GetFilterMTMode(filter, is_forced);
2198 }
2199
2200 1 bool __stdcall FilterHasMtMode(const Function* filter) const
2201 {
2202 1 return core->FilterHasMtMode(filter);
2203 }
2204
2205 IJobCompletion* __stdcall NewCompletion(size_t capacity)
2206 {
2207 return core->NewCompletion(capacity);
2208 }
2209
2210 void __stdcall ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion)
2211 {
2212 core->ParallelJob(jobFunc, jobData, completion, this);
2213 }
2214
2215 void __stdcall ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion, InternalEnvironment* env)
2216 {
2217 core->ParallelJob(jobFunc, jobData, completion, env);
2218 }
2219
2220 ClipDataStore* __stdcall ClipData(IClip* clip)
2221 {
2222 return core->ClipData(clip);
2223 }
2224
2225 MtMode __stdcall GetDefaultMtMode() const
2226 {
2227 return core->GetDefaultMtMode();
2228 }
2229
2230 void __stdcall SetLogParams(const char* target, int level)
2231 {
2232 core->SetLogParams(target, level);
2233 }
2234
2235 // stdcall calling convention is not supported on variadic function
2236 33 void LogMsg(int level, const char* fmt, ...)
2237 {
2238 va_list val;
2239 33 va_start(val, fmt);
2240
1/2
✓ Branch 2 → 3 taken 33 times.
✗ Branch 2 → 4 not taken.
33 core->LogMsg_valist(level, fmt, val);
2241 33 va_end(val);
2242 33 }
2243
2244 void __stdcall LogMsg_valist(int level, const char* fmt, va_list va)
2245 {
2246 core->LogMsg_valist(level, fmt, va);
2247 }
2248
2249 // stdcall calling convention is not supported on variadic function
2250 void LogMsgOnce(const OneTimeLogTicket& ticket, int level, const char* fmt, ...)
2251 {
2252 va_list val;
2253 va_start(val, fmt);
2254 core->LogMsgOnce_valist(ticket, level, fmt, val);
2255 va_end(val);
2256 }
2257
2258 void __stdcall LogMsgOnce_valist(const OneTimeLogTicket& ticket, int level, const char* fmt, va_list va)
2259 {
2260 core->LogMsgOnce_valist(ticket, level, fmt, va);
2261 }
2262
2263 void __stdcall SetMaxCPU(const char *features)
2264 {
2265 core->SetMaxCPU(features);
2266 }
2267
2268 void __stdcall SetGraphAnalysis(bool enable)
2269 {
2270 core->SetGraphAnalysis(enable);
2271 }
2272
2273 int __stdcall SetMemoryMax(AvsDeviceType type, int index, int mem)
2274 {
2275 return core->SetMemoryMax(type, index, mem);
2276 }
2277
2278 PDevice __stdcall GetDevice(AvsDeviceType device_type, int device_index) const
2279 {
2280 return core->GetDevice(device_type, device_index);
2281 }
2282
2283 PDevice __stdcall GetDevice() const
2284 {
2285 return DISPATCH(currentDevice);
2286 }
2287
2288 6 AvsDeviceType __stdcall GetDeviceType() const
2289 {
2290
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 6 times.
6 return DISPATCH(currentDevice)->device_type;
2291 }
2292
2293 int __stdcall GetDeviceId() const
2294 {
2295 return DISPATCH(currentDevice)->device_id;
2296 }
2297
2298 int __stdcall GetDeviceIndex() const
2299 {
2300 return DISPATCH(currentDevice)->device_index;
2301 }
2302
2303 void* __stdcall GetDeviceStream() const
2304 {
2305 return DISPATCH(currentDevice)->GetComputeStream();;
2306 }
2307
2308 PVideoFrame __stdcall NewVideoFrameOnDevice(const VideoInfo& vi, int align, Device* device)
2309 {
2310 return core->NewVideoFrameOnDevice(vi, align, device);
2311 }
2312
2313 // shortcut to the above
2314 PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi)
2315 {
2316 return NewVideoFrameOnDevice(vi, FRAME_ALIGN, DISPATCH(currentDevice));
2317 }
2318
2319 // shortcut to the above
2320 PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, const PDevice& device)
2321 {
2322 return NewVideoFrameOnDevice(vi, FRAME_ALIGN, (Device*)(void*)device);
2323 }
2324
2325 // variants with frame property source
2326 PVideoFrame __stdcall NewVideoFrameOnDevice(const VideoInfo& vi, int align, Device* device, const PVideoFrame *prop_src)
2327 {
2328 return core->NewVideoFrameOnDevice(vi, align, device, prop_src);
2329 }
2330
2331 // shortcut to the above
2332 PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, const PVideoFrame* prop_src)
2333 {
2334 return NewVideoFrameOnDevice(vi, FRAME_ALIGN, DISPATCH(currentDevice), prop_src);
2335 }
2336
2337 // shortcut to the above
2338 PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, const PDevice& device, const PVideoFrame* prop_src)
2339 {
2340 return NewVideoFrameOnDevice(vi, FRAME_ALIGN, (Device*)(void*)device, prop_src);
2341 }
2342
2343 PVideoFrame __stdcall GetOnDeviceFrame(const PVideoFrame& src, Device* device)
2344 {
2345 return core->GetOnDeviceFrame(src, device);
2346 }
2347
2348
2349 ThreadPool* __stdcall NewThreadPool(size_t nThreads)
2350 {
2351 return core->NewThreadPool(nThreads);
2352 }
2353
2354
2355 void __stdcall AddRef() {
2356 InterlockedIncrement(&DISPATCH(refcount));
2357 }
2358
2359 1811 void __stdcall Release() {
2360
3/4
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1811 times.
✓ Branch 5 → 6 taken 1810 times.
✓ Branch 5 → 8 taken 1 time.
1811 if (InterlockedDecrement(&DISPATCH(refcount)) == 0) {
2361
1/2
✓ Branch 6 → 7 taken 1811 times.
✗ Branch 6 → 8 not taken.
1810 delete this;
2362 }
2363 1756 }
2364
2365 void __stdcall IncEnvCount() {
2366 core->IncEnvCount();
2367 }
2368
2369 void __stdcall DecEnvCount() {
2370 core->DecEnvCount();
2371 }
2372
2373 2265 ConcurrentVarStringFrame* __stdcall GetTopFrame()
2374 {
2375 2265 return core->GetTopFrame();
2376 }
2377
2378 void __stdcall SetCacheMode(CacheMode mode)
2379 {
2380 core->SetCacheMode(mode);
2381 }
2382
2383 5 CacheMode __stdcall GetCacheMode()
2384 {
2385 5 return core->GetCacheMode();
2386 }
2387
2388 15 bool& __stdcall GetSupressCaching()
2389 {
2390
1/2
✓ Branch 2 → 3 taken 15 times.
✗ Branch 2 → 4 not taken.
15 return DISPATCH(supressCaching);
2391 }
2392
2393 size_t __stdcall GetInvokeStackSize()
2394 {
2395 return core->GetInvokeStackSize();
2396 }
2397
2398 void __stdcall SetDeviceOpt(DeviceOpt opt, int val)
2399 {
2400 core->SetDeviceOpt(opt, val);
2401 }
2402
2403 void __stdcall UpdateFunctionExports(const char* funcName, const char* funcParams, const char *exportVar)
2404 {
2405 if (GetThreadId() != 0 || GetFrameRecursiveCount() != 0) {
2406 // no need to export function at runtime
2407 return;
2408 }
2409 core->UpdateFunctionExports(funcName, funcParams, exportVar);
2410 }
2411
2412
2413 1812 InternalEnvironment* __stdcall NewThreadScriptEnvironment(int thread_id)
2414 {
2415
2/6
✓ Branch 3 → 4 taken 1810 times.
✗ Branch 3 → 9 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 1810 times.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
3622 return new ThreadScriptEnvironment(thread_id, core, coreTLS);
2416 }
2417
2418 5 int __stdcall GetThreadId() {
2419
1/2
✓ Branch 2 → 3 taken 5 times.
✗ Branch 2 → 4 not taken.
5 return DISPATCH(thread_id);
2420 }
2421
2422 12 int& __stdcall GetFrameRecursiveCount() {
2423
1/2
✓ Branch 2 → 3 taken 12 times.
✗ Branch 2 → 4 not taken.
12 return DISPATCH(getFrameRecursiveCount);
2424 }
2425
2426 7 int& __stdcall GetSuppressThreadCount() {
2427
1/2
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 4 not taken.
7 return DISPATCH(suppressThreadCount);
2428 }
2429
2430 903 FilterGraphNode*& GetCurrentGraphNode() {
2431
1/2
✓ Branch 2 → 3 taken 903 times.
✗ Branch 2 → 4 not taken.
903 return DISPATCH(currentGraphNode);
2432 }
2433
2434 #undef DISPATCH
2435 #undef IFNULL
2436 };
2437
2438 #ifdef AVS_POSIX
2439 906 static uint64_t posix_get_physical_memory() {
2440 uint64_t ullTotalPhys;
2441 #if defined(AVS_MACOS)
2442 size_t len;
2443 sysctlbyname("hw.memsize", nullptr, &len, nullptr, 0);
2444 int64_t memsize;
2445 sysctlbyname("hw.memsize", (void*)&memsize, &len, nullptr, 0);
2446 ullTotalPhys = memsize;
2447 #elif defined(AVS_BSD)
2448 size_t len;
2449 int64_t memsize;
2450 #if !defined(__OpenBSD__)
2451 // OpenBSD doesn't have sysctlbyname at all, so this needs to be
2452 // ported to plain sysctl.
2453 sysctlbyname("hw.physmem", nullptr, &len, nullptr, 0);
2454 sysctlbyname("hw.physmem", (void*)&memsize, &len, nullptr, 0);
2455 #else
2456 sysctl((const int *)HW_PHYSMEM, 4, nullptr, &len, nullptr, 0);
2457 sysctl((const int *)HW_PHYSMEM, 4, (void*)&memsize, &len, nullptr, 0);
2458 #endif
2459 ullTotalPhys = memsize;
2460 #elif defined(AVS_HAIKU)
2461 system_info sysinf;
2462 get_system_info(&sysinf);
2463 ullTotalPhys = PAGESIZE * sysinf.max_pages;
2464 #else
2465 // linux
2466 struct sysinfo info;
2467
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 taken 906 times.
906 if (sysinfo(&info) != 0) {
2468 throw AvisynthError("sysinfo: error reading system statistics");
2469 }
2470 906 ullTotalPhys = (uint64_t)info.totalram * info.mem_unit;
2471 #endif
2472 906 return ullTotalPhys;
2473 }
2474
2475 453 static int64_t posix_get_available_memory() {
2476 int64_t memory;
2477
2478 453 long nPageSize = sysconf(_SC_PAGE_SIZE);
2479 int64_t nAvailablePhysicalPages;
2480
2481 #if defined(AVS_MACOS)
2482 vm_statistics64_data_t vmstats;
2483 mach_msg_type_number_t vmstatsz = HOST_VM_INFO64_COUNT;
2484 host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info_t)&vmstats, &vmstatsz);
2485 nAvailablePhysicalPages = vmstats.free_count;
2486 #elif defined(AVS_BSD)
2487 #if !defined(__OpenBSD__)
2488 // OpenBSD does not have sysctlbyname
2489 size_t nAvailablePhysicalPagesLen = sizeof(nAvailablePhysicalPages);
2490 sysctlbyname("vm.stats.vm.v_free_count", &nAvailablePhysicalPages, &nAvailablePhysicalPagesLen, NULL, 0);
2491 #endif
2492 #elif defined(AVS_HAIKU)
2493 system_info sysinf;
2494 get_system_info(&sysinf);
2495 nAvailablePhysicalPages = sysinf.free_memory;
2496 #else // Linux
2497 453 nAvailablePhysicalPages = sysconf(_SC_AVPHYS_PAGES);
2498 #endif
2499
2500 453 memory = nPageSize * nAvailablePhysicalPages;
2501
2502 453 return memory;
2503 }
2504 #endif
2505
2506 453 static uint64_t ConstrainMemoryRequest(uint64_t requested)
2507 {
2508 // Get system memory information
2509 #ifdef AVS_WINDOWS // needs linux alternative
2510 MEMORYSTATUSEX memstatus;
2511 memstatus.dwLength = sizeof(memstatus);
2512 GlobalMemoryStatusEx(&memstatus);
2513
2514 // mem_limit is the largest amount of memory that makes sense to use.
2515 // We don't want to use more than the virtual address space,
2516 // and we also don't want to start paging to disk.
2517 uint64_t mem_limit = min(memstatus.ullTotalVirtual, memstatus.ullTotalPhys);
2518
2519 uint64_t mem_sysreserve = 0;
2520 if (memstatus.ullTotalPhys > memstatus.ullTotalVirtual)
2521 {
2522 // We are probably running on a 32bit OS system where the virtual space is capped to
2523 // much less than what the system can use, so it is enough to reserve only a small amount.
2524 mem_sysreserve = 128 * 1024 * 1024ull;
2525 }
2526 else
2527 {
2528 // We could probably use up all the RAM in our single application,
2529 // so reserve more to leave some RAM for other apps and the OS too.
2530 mem_sysreserve = 1024 * 1024 * 1024ull;
2531 }
2532
2533 // Cap memory_max to at most mem_sysreserve less than total, but at least to 64MB.
2534 return clamp(requested, (uint64_t)64 * 1024 * 1024, mem_limit - mem_sysreserve);
2535 #else
2536 // copied over from AvxSynth, check against current code!!!
2537
2538 // Check#1
2539 // AvxSynth returned simply the actual total_available memory
2540 // this part is trying to fine tune it, considering that
2541 // - total_available may contain swap area which we do not want to use FIXME: check it!
2542 // - leave some memory for other processes (1 GB for x64, 128MB for 32 bit)
2543
2544 453 uint64_t physical_memory = posix_get_physical_memory();
2545 453 uint64_t total_available = posix_get_available_memory();
2546 // We don't want to use more than the virtual address space,
2547 // and we also don't want to start paging to disk.
2548 453 uint64_t mem_limit = min(total_available, physical_memory);
2549
2550 // We could probably use up all the RAM in our single application,
2551 // so reserve more to leave some RAM for other apps and the OS too.
2552 453 const bool isX64 = sizeof(void*) == 8;
2553 453 uint64_t mem_sysreserve = isX64 ? (uint64_t)1024 * 1024 * 1024 : (uint64_t)128 * 1024 * 1024;
2554
2555 // Cap memory_max to at most mem_sysreserve less than total, but at least to 64MB.
2556 453 uint64_t allowed_memory = clamp(requested, (uint64_t)64 * 1024 * 1024, mem_limit - mem_sysreserve);
2557 #if 0
2558 const int DIV = 1024 * 1024;
2559 fprintf(stdout, "requested= %" PRIu64 " MB\r\n", requested / DIV);
2560 fprintf(stdout, "physical_memory= %" PRIu64 " MB\r\n", physical_memory / DIV);
2561 fprintf(stdout, "total_available= %" PRIu64 " MB\r\n", total_available / DIV);
2562 fprintf(stdout, "mem_limit= %" PRIu64 " MB\r\n", mem_limit / DIV);
2563 fprintf(stdout, "mem_sysreserve= %" PRIu64 " MB\r\n", mem_sysreserve / DIV);
2564 fprintf(stdout, "allowed_memory= %" PRIu64 " MB\r\n", allowed_memory / DIV);
2565 /*For a computer with 16GB RAM, 64 bit OS
2566 No SetMemoryMax, where default max request is 4GB on x64
2567 requested= 4072 MB
2568 physical_memory= 16291 MB
2569 total_available= 7640 MB
2570 mem_limit= 7640 MB
2571 mem_sysreserve= 1024 MB
2572 allowed_memory= 4072 MB
2573 Using SetmemoryMax(10000)
2574 requested= 10000 MB
2575 physical_memory= 16291 MB
2576 total_available= 7667 MB
2577 mem_limit= 7667 MB
2578 mem_sysreserve= 1024 MB
2579 allowed_memory= 6643 MB
2580 */
2581 #endif
2582 453 return allowed_memory;
2583 #endif
2584
2585 }
2586
2587 IJobCompletion* ScriptEnvironment::NewCompletion(size_t capacity)
2588 {
2589 return new JobCompletion(capacity);
2590 }
2591
2592 453 ScriptEnvironment::ScriptEnvironment()
2593 453 : threadEnv(),
2594 453 at_exit(),
2595 453 thread_pool(NULL),
2596 453 plugin_manager(NULL),
2597 453 EnvCount(0),
2598 453 PlanarChromaAlignmentState(true), // Change to "true" for 2.5.7
2599 453 hrfromcoinit(E_FAIL), coinitThreadId(0),
2600 453 Devices(),
2601 453 FrontCache(NULL),
2602 453 nTotalThreads(1),
2603 453 nMaxFilterInstances(1),
2604 453 LogLevel(LOGLEVEL_NONE),
2605 453 graphAnalysisEnable(false),
2606
2/4
✓ Branch 15 → 16 taken 453 times.
✗ Branch 15 → 306 not taken.
✓ Branch 20 → 21 taken 453 times.
✗ Branch 20 → 296 not taken.
1812 cacheMode(CACHE_DEFAULT)
2607 {
2608 #ifdef XP_TLS
2609 if(dwTlsIndex == 0)
2610 throw("ScriptEnvironment: TlsAlloc failed on DLL load");
2611 #endif
2612
2613
1/2
✓ Branch 23 → 24 taken 453 times.
✗ Branch 23 → 290 not taken.
453 cpuFlagsEx = ::GetCPUFlagsEx();
2614
1/2
✓ Branch 24 → 25 taken 453 times.
✗ Branch 24 → 290 not taken.
453 cache_size_L2 = ::GetL2CacheSize();
2615
2616 try {
2617 #ifdef AVS_WINDOWS
2618 // Make sure COM is initialised
2619 hrfromcoinit = CoInitialize(NULL);
2620 // If it was already init'd then decrement
2621 // the use count and leave it alone!
2622 if (hrfromcoinit == S_FALSE) {
2623 hrfromcoinit = E_FAIL;
2624 CoUninitialize();
2625 }
2626 // Remember our threadId.
2627 coinitThreadId = GetCurrentThreadId();
2628 #endif
2629
2630
3/8
✓ Branch 25 → 26 taken 453 times.
✗ Branch 25 → 178 not taken.
✓ Branch 26 → 27 taken 453 times.
✗ Branch 26 → 175 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 453 times.
✗ Branch 175 → 176 not taken.
✗ Branch 175 → 177 not taken.
453 threadEnv = std::unique_ptr<ThreadScriptEnvironment>(new ThreadScriptEnvironment(0, this, nullptr));
2631
3/8
✓ Branch 32 → 33 taken 453 times.
✗ Branch 32 → 182 not taken.
✓ Branch 34 → 35 taken 453 times.
✗ Branch 34 → 179 not taken.
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 453 times.
✗ Branch 179 → 180 not taken.
✗ Branch 179 → 181 not taken.
453 Devices = std::unique_ptr<DeviceManager>(new DeviceManager(threadEnv.get()));
2632
2633 // calc frame align
2634 453 frame_align = plane_align = FRAME_ALIGN;
2635 #ifdef ENABLE_CUDA
2636 for (int i = 0, end = Devices->GetNumDevices(DEV_TYPE_CUDA); i < end; ++i) {
2637 int align, pitchAlign;
2638 Devices->GetDevice(DEV_TYPE_CUDA, i)->GetAlignmentRequirement(&align, &pitchAlign);
2639 frame_align = max(frame_align, pitchAlign);
2640 plane_align = max(plane_align, align);
2641 }
2642 #endif
2643
2644
1/2
✓ Branch 41 → 42 taken 453 times.
✗ Branch 41 → 281 not taken.
453 auto cpuDevice = Devices->GetCPUDevice();
2645 453 threadEnv->GetTLS()->currentDevice = cpuDevice;
2646
2647 #ifdef AVS_WINDOWS
2648 MEMORYSTATUSEX memstatus;
2649 memstatus.dwLength = sizeof(memstatus);
2650 GlobalMemoryStatusEx(&memstatus);
2651 cpuDevice->memory_max = ConstrainMemoryRequest(memstatus.ullTotalPhys / 4);
2652 #endif
2653 #ifdef AVS_POSIX
2654
1/2
✓ Branch 44 → 45 taken 453 times.
✗ Branch 44 → 281 not taken.
453 uint64_t ullTotalPhys = posix_get_physical_memory();
2655
1/2
✓ Branch 45 → 46 taken 453 times.
✗ Branch 45 → 281 not taken.
453 cpuDevice->memory_max = ConstrainMemoryRequest(ullTotalPhys / 4);
2656 // fprintf(stdout, "Total physical memory= %" PRIu64 ", after constraint=%" PRIu64 "\r\n", ullTotalPhys, memory_max);
2657 // Total physical memory = 17083355136, after constraint = 7274700800
2658 #endif
2659 453 const bool isX64 = sizeof(void*) == 8;
2660 453 cpuDevice->memory_max = min(cpuDevice->memory_max, (uint64_t)((isX64 ? 4096 : 1024) * (1024 * 1024ull))); // at start, cap memory usage to 1GB(x86)/4GB (x64)
2661 453 cpuDevice->memory_used = 0ull;
2662
2663
2/4
✓ Branch 48 → 49 taken 453 times.
✗ Branch 48 → 185 not taken.
✓ Branch 49 → 50 taken 453 times.
✗ Branch 49 → 183 not taken.
453 top_frame.Set("true", true);
2664
2/4
✓ Branch 51 → 52 taken 453 times.
✗ Branch 51 → 188 not taken.
✓ Branch 52 → 53 taken 453 times.
✗ Branch 52 → 186 not taken.
453 top_frame.Set("false", false);
2665
2/4
✓ Branch 54 → 55 taken 453 times.
✗ Branch 54 → 191 not taken.
✓ Branch 55 → 56 taken 453 times.
✗ Branch 55 → 189 not taken.
453 top_frame.Set("yes", true);
2666
2/4
✓ Branch 57 → 58 taken 453 times.
✗ Branch 57 → 194 not taken.
✓ Branch 58 → 59 taken 453 times.
✗ Branch 58 → 192 not taken.
453 top_frame.Set("no", false);
2667
2/4
✓ Branch 60 → 61 taken 453 times.
✗ Branch 60 → 197 not taken.
✓ Branch 61 → 62 taken 453 times.
✗ Branch 61 → 195 not taken.
453 top_frame.Set("last", AVSValue());
2668
2669
2/4
✓ Branch 63 → 64 taken 453 times.
✗ Branch 63 → 200 not taken.
✓ Branch 64 → 65 taken 453 times.
✗ Branch 64 → 198 not taken.
453 top_frame.Set("$ScriptName$", AVSValue());
2670
2/4
✓ Branch 66 → 67 taken 453 times.
✗ Branch 66 → 203 not taken.
✓ Branch 67 → 68 taken 453 times.
✗ Branch 67 → 201 not taken.
453 top_frame.Set("$ScriptFile$", AVSValue());
2671
2/4
✓ Branch 69 → 70 taken 453 times.
✗ Branch 69 → 206 not taken.
✓ Branch 70 → 71 taken 453 times.
✗ Branch 70 → 204 not taken.
453 top_frame.Set("$ScriptDir$", AVSValue());
2672
2/4
✓ Branch 72 → 73 taken 453 times.
✗ Branch 72 → 209 not taken.
✓ Branch 73 → 74 taken 453 times.
✗ Branch 73 → 207 not taken.
453 top_frame.Set("$ScriptNameUtf8$", AVSValue());
2673
2/4
✓ Branch 75 → 76 taken 453 times.
✗ Branch 75 → 212 not taken.
✓ Branch 76 → 77 taken 453 times.
✗ Branch 76 → 210 not taken.
453 top_frame.Set("$ScriptFileUtf8$", AVSValue());
2674
2/4
✓ Branch 78 → 79 taken 453 times.
✗ Branch 78 → 215 not taken.
✓ Branch 79 → 80 taken 453 times.
✗ Branch 79 → 213 not taken.
453 top_frame.Set("$ScriptDirUtf8$", AVSValue());
2675
2676
3/8
✓ Branch 81 → 82 taken 453 times.
✗ Branch 81 → 281 not taken.
✓ Branch 83 → 84 taken 453 times.
✗ Branch 83 → 216 not taken.
✗ Branch 84 → 85 not taken.
✓ Branch 84 → 86 taken 453 times.
✗ Branch 216 → 217 not taken.
✗ Branch 216 → 218 not taken.
453 plugin_manager = new PluginManager(threadEnv.get());
2677 #ifdef AVS_WINDOWS
2678 plugin_manager->AddAutoloadDir("USER_PLUS_PLUGINS", false);
2679 plugin_manager->AddAutoloadDir("MACHINE_PLUS_PLUGINS", false);
2680 plugin_manager->AddAutoloadDir("USER_CLASSIC_PLUGINS", false);
2681 plugin_manager->AddAutoloadDir("MACHINE_CLASSIC_PLUGINS", false);
2682 #else
2683 // system_avs_plugindir relies on install path, it and user_avs_plugindir_configurable get
2684 // defined in avisynth_conf.h.in when configuring.
2685
2686
1/2
✓ Branch 87 → 88 taken 453 times.
✗ Branch 87 → 111 not taken.
453 if(std::getenv("HOME")) {
2687
1/2
✓ Branch 91 → 92 taken 453 times.
✗ Branch 91 → 219 not taken.
906 std::string user_avs_plugindir = std::getenv("HOME");
2688
1/2
✓ Branch 96 → 97 taken 453 times.
✗ Branch 96 → 222 not taken.
906 std::string user_avs_plugindir_local = std::getenv("HOME");
2689
1/2
✓ Branch 100 → 101 taken 453 times.
✗ Branch 100 → 225 not taken.
453 std::string user_avs_dirname = "/.avisynth";
2690
2691
1/2
✓ Branch 102 → 103 taken 453 times.
✗ Branch 102 → 228 not taken.
453 user_avs_plugindir.append(user_avs_dirname);
2692
2693
2/4
✓ Branch 103 → 104 taken 453 times.
✗ Branch 103 → 228 not taken.
✓ Branch 104 → 105 taken 453 times.
✗ Branch 104 → 228 not taken.
453 user_avs_plugindir_local.append("/").append(user_avs_plugindir_configurable);
2694
2695
1/2
✓ Branch 105 → 106 taken 453 times.
✗ Branch 105 → 228 not taken.
453 plugin_manager->AddAutoloadDir(user_avs_plugindir, false);
2696
1/2
✓ Branch 106 → 107 taken 453 times.
✗ Branch 106 → 228 not taken.
453 plugin_manager->AddAutoloadDir(user_avs_plugindir_local, false);
2697 453 }
2698
2699
1/2
✓ Branch 112 → 113 taken 453 times.
✗ Branch 112 → 128 not taken.
453 if (std::getenv("LD_LIBRARY_PATH")) {
2700 453 std::string ldrel_avs_plugindir, ldrel_stor;
2701
1/2
✓ Branch 116 → 117 taken 453 times.
✗ Branch 116 → 237 not taken.
453 ldrel_avs_plugindir.append(std::getenv("LD_LIBRARY_PATH"));
2702
2703
1/2
✓ Branch 117 → 118 taken 453 times.
✗ Branch 117 → 237 not taken.
453 std::istringstream ldrelin(ldrel_avs_plugindir);
2704
2705
4/6
✓ Branch 121 → 122 taken 906 times.
✗ Branch 121 → 235 not taken.
✓ Branch 122 → 123 taken 906 times.
✗ Branch 122 → 235 not taken.
✓ Branch 123 → 119 taken 453 times.
✓ Branch 123 → 124 taken 453 times.
906 while(getline(ldrelin, ldrel_stor, ':')) {
2706
1/2
✓ Branch 119 → 120 taken 453 times.
✗ Branch 119 → 235 not taken.
453 ldrel_stor.append("/avisynth");
2707
1/2
✓ Branch 120 → 121 taken 453 times.
✗ Branch 120 → 235 not taken.
453 plugin_manager->AddAutoloadDir(ldrel_stor, false);
2708 }
2709 453 }
2710
2711
2/4
✓ Branch 130 → 131 taken 453 times.
✗ Branch 130 → 244 not taken.
✓ Branch 131 → 132 taken 453 times.
✗ Branch 131 → 242 not taken.
906 plugin_manager->AddAutoloadDir(system_avs_plugindir, false);
2712 #endif
2713
2714
2/4
✓ Branch 134 → 135 taken 453 times.
✗ Branch 134 → 250 not taken.
✓ Branch 135 → 136 taken 453 times.
✗ Branch 135 → 248 not taken.
453 top_frame.Set("LOG_ERROR", (int)LOGLEVEL_ERROR);
2715
2/4
✓ Branch 137 → 138 taken 453 times.
✗ Branch 137 → 253 not taken.
✓ Branch 138 → 139 taken 453 times.
✗ Branch 138 → 251 not taken.
453 top_frame.Set("LOG_WARNING", (int)LOGLEVEL_WARNING);
2716
2/4
✓ Branch 140 → 141 taken 453 times.
✗ Branch 140 → 256 not taken.
✓ Branch 141 → 142 taken 453 times.
✗ Branch 141 → 254 not taken.
453 top_frame.Set("LOG_INFO", (int)LOGLEVEL_INFO);
2717
2/4
✓ Branch 143 → 144 taken 453 times.
✗ Branch 143 → 259 not taken.
✓ Branch 144 → 145 taken 453 times.
✗ Branch 144 → 257 not taken.
453 top_frame.Set("LOG_DEBUG", (int)LOGLEVEL_DEBUG);
2718
2719
2/4
✓ Branch 146 → 147 taken 453 times.
✗ Branch 146 → 262 not taken.
✓ Branch 147 → 148 taken 453 times.
✗ Branch 147 → 260 not taken.
453 top_frame.Set("DEV_TYPE_CPU", (int)DEV_TYPE_CPU);
2720
2/4
✓ Branch 149 → 150 taken 453 times.
✗ Branch 149 → 265 not taken.
✓ Branch 150 → 151 taken 453 times.
✗ Branch 150 → 263 not taken.
453 top_frame.Set("DEV_TYPE_CUDA", (int)DEV_TYPE_CUDA);
2721
2722
2/4
✓ Branch 152 → 153 taken 453 times.
✗ Branch 152 → 268 not taken.
✓ Branch 153 → 154 taken 453 times.
✗ Branch 153 → 266 not taken.
453 top_frame.Set("CACHE_FAST_START", (int)CACHE_FAST_START);
2723
2/4
✓ Branch 155 → 156 taken 453 times.
✗ Branch 155 → 271 not taken.
✓ Branch 156 → 157 taken 453 times.
✗ Branch 156 → 269 not taken.
453 top_frame.Set("CACHE_OPTIMAL_SIZE", (int)CACHE_OPTIMAL_SIZE);
2724
2/4
✓ Branch 158 → 159 taken 453 times.
✗ Branch 158 → 274 not taken.
✓ Branch 159 → 160 taken 453 times.
✗ Branch 159 → 272 not taken.
453 top_frame.Set("DEV_CUDA_PINNED_HOST", (int)DEV_CUDA_PINNED_HOST);
2725
2/4
✓ Branch 161 → 162 taken 453 times.
✗ Branch 161 → 277 not taken.
✓ Branch 162 → 163 taken 453 times.
✗ Branch 162 → 275 not taken.
453 top_frame.Set("DEV_FREE_THRESHOLD", (int)DEV_FREE_THRESHOLD);
2726
2727
1/2
✓ Branch 164 → 165 taken 453 times.
✗ Branch 164 → 281 not taken.
453 InitMT();
2728
3/8
✓ Branch 165 → 166 taken 453 times.
✗ Branch 165 → 281 not taken.
✓ Branch 168 → 169 taken 453 times.
✗ Branch 168 → 278 not taken.
✗ Branch 169 → 170 not taken.
✓ Branch 169 → 171 taken 453 times.
✗ Branch 278 → 279 not taken.
✗ Branch 278 → 280 not taken.
453 thread_pool = new ThreadPool(std::thread::hardware_concurrency(), 1, threadEnv.get());
2729
2730
1/2
✓ Branch 171 → 172 taken 453 times.
✗ Branch 171 → 281 not taken.
453 ExportBuiltinFilters();
2731
2732
1/2
✓ Branch 172 → 173 taken 453 times.
✗ Branch 172 → 281 not taken.
453 clip_data.max_load_factor(0.8f);
2733
1/2
✓ Branch 173 → 174 taken 453 times.
✗ Branch 173 → 281 not taken.
453 LogTickets.max_load_factor(0.8f);
2734 }
2735 catch (const AvisynthError& err) {
2736 #ifdef AVS_WINDOWS
2737 if (SUCCEEDED(hrfromcoinit)) {
2738 hrfromcoinit = E_FAIL;
2739 CoUninitialize();
2740 }
2741 #endif
2742 // Needs must, to not loose the text we
2743 // must leak a little memory.
2744 throw AvisynthError(_strdup(err.msg));
2745 }
2746 453 }
2747
2748 MtMode ScriptEnvironment::GetDefaultMtMode() const
2749 {
2750 return DefaultMtMode;
2751 }
2752
2753 453 void ScriptEnvironment::InitMT()
2754 {
2755
2/4
✓ Branch 2 → 3 taken 453 times.
✗ Branch 2 → 17 not taken.
✓ Branch 3 → 4 taken 453 times.
✗ Branch 3 → 15 not taken.
453 top_frame.Set("MT_NICE_FILTER", (int)MT_NICE_FILTER);
2756
2/4
✓ Branch 5 → 6 taken 453 times.
✗ Branch 5 → 20 not taken.
✓ Branch 6 → 7 taken 453 times.
✗ Branch 6 → 18 not taken.
453 top_frame.Set("MT_MULTI_INSTANCE", (int)MT_MULTI_INSTANCE);
2757
2/4
✓ Branch 8 → 9 taken 453 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 453 times.
✗ Branch 9 → 21 not taken.
453 top_frame.Set("MT_SERIALIZED", (int)MT_SERIALIZED);
2758
2/4
✓ Branch 11 → 12 taken 453 times.
✗ Branch 11 → 26 not taken.
✓ Branch 12 → 13 taken 453 times.
✗ Branch 12 → 24 not taken.
453 top_frame.Set("MT_SPECIAL_MT", (int)MT_SPECIAL_MT);
2759 453 }
2760
2761 453 ScriptEnvironment::~ScriptEnvironment() {
2762
2763 _RPT0(0, "~ScriptEnvironment() called.\n");
2764
2765 453 auto tls = threadEnv->GetTLS();
2766 453 tls->closing = true;
2767
2768 // Before we start to pull the world apart
2769 // give every one their last wish.
2770 453 at_exit.Execute(threadEnv.get());
2771
2772 453 GlobalLockManager::on_environment_exit(this); // V12
2773
2774
1/2
✓ Branch 7 → 8 taken 453 times.
✗ Branch 7 → 10 not taken.
453 delete thread_pool;
2775
2776 453 tls->var_table.Clear();
2777 453 top_frame.Clear();
2778
2779 // There can be a circular reference between the Prefetcher and the
2780 // TLS PopContext() variables of the threads started by it. Normally
2781 // this doesn't happen, but it can for example when somebody
2782 // sets 'last' in a TLS (see ScriptClip for a specific example).
2783 // This circular reference causes leaks, so we call
2784 // Destroy() on the prefetcher, which will in turn terminate all
2785 // its TLS stuff and break the chain.
2786
1/2
✗ Branch 27 → 14 not taken.
✓ Branch 27 → 28 taken 453 times.
906 for (auto& pool : ThreadPoolRegistry) {
2787 pool->Join();
2788 }
2789 453 ThreadPoolRegistry.clear();
2790
2791 // delete ThreadScriptEnvironment
2792 453 threadEnv = nullptr;
2793
2794 // check ThreadScriptEnvironment leaks
2795
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 453 times.
453 if (EnvCount > 0) {
2796 LogMsg(LOGLEVEL_WARNING, "ThreadScriptEnvironment leaks.");
2797 }
2798
2799 #if 0
2800 // check clip leaks DoDumpGraph
2801 if (std::find_if(GraphNodeRegistry.begin(), GraphNodeRegistry.end(),
2802 [](FilterGraphNode* node) { return node != nullptr; }) != GraphNodeRegistry.end())
2803 {
2804 // This is dangerous operation because thread's string is destroyed
2805 // and may be there are dangling string pointer which results in access violation.
2806 MinimumScriptEnvironment env(&top_frame);
2807 DoDumpGraph(GraphNodeRegistry, "clip_leaks.txt", &env);
2808 }
2809 #endif
2810
2811
2812 #ifdef _DEBUG
2813 // LogMsg(LOGLEVEL_DEBUG, "We are before FrameRegistryCleanup");
2814 // ListFrameRegistry(0,10000000000000ull, true, device); // list all
2815 #endif
2816 // and deleting the frame buffer from FrameRegistry2 as well
2817 453 bool somethingLeaks = false;
2818
2/2
✓ Branch 65 → 34 taken 373 times.
✓ Branch 65 → 66 taken 453 times.
826 for (auto &it: FrameRegistry2)
2819 {
2820
2/2
✓ Branch 62 → 37 taken 891 times.
✓ Branch 62 → 63 taken 373 times.
1264 for (auto &it2: it.second)
2821 {
2822 891 VFBStorage *vfb = static_cast<VFBStorage*>(it2.first);
2823
1/2
✓ Branch 38 → 39 taken 891 times.
✗ Branch 38 → 41 not taken.
891 delete vfb;
2824 // iterate through frames belonging to this vfb
2825
2/2
✓ Branch 59 → 43 taken 927 times.
✓ Branch 59 → 60 taken 891 times.
2709 for (auto &it3: it2.second)
2826 {
2827 927 VideoFrame *frame = it3.frame;
2828
2829 927 frame->vfb = 0;
2830
2831 //assert(0 == frame->refcount);
2832
1/2
✓ Branch 45 → 46 taken 927 times.
✗ Branch 45 → 49 not taken.
927 if (0 == frame->refcount)
2833 {
2834
1/2
✓ Branch 46 → 47 taken 927 times.
✗ Branch 46 → 50 not taken.
927 delete frame;
2835 }
2836 else
2837 {
2838 somethingLeaks = true;
2839 }
2840 } // it3
2841 } // it2
2842 } // it
2843
2844
1/2
✗ Branch 66 → 67 not taken.
✓ Branch 66 → 68 taken 453 times.
453 if (somethingLeaks) {
2845 LogMsg(LOGLEVEL_WARNING, "A plugin or the host application might be causing memory leaks.");
2846 }
2847
2848
1/2
✓ Branch 68 → 69 taken 453 times.
✗ Branch 68 → 71 not taken.
453 delete plugin_manager;
2849
2850 #ifdef AVS_WINDOWS // COM is Win32-specific
2851 // If we init'd COM and this is the right thread then release it
2852 // If it's the wrong threadId then tuff, nothing we can do.
2853 if (SUCCEEDED(hrfromcoinit) && (coinitThreadId == GetCurrentThreadId())) {
2854 hrfromcoinit = E_FAIL;
2855 CoUninitialize();
2856 }
2857 #endif
2858
2859 453 }
2860
2861 void ScriptEnvironment::SetLogParams(const char* target, int level)
2862 {
2863 if (nullptr == target) {
2864 target = "stderr";
2865 }
2866
2867 if (-1 == level) {
2868 level = LOGLEVEL_INFO;
2869 }
2870
2871 if (LogFileStream.is_open()) {
2872 LogFileStream.close();
2873 }
2874
2875 LogLevel = LOGLEVEL_NONE;
2876
2877 if (!streqi(target, "stderr") && !streqi(target, "stdout")) {
2878 LogFileStream.open(target, std::ofstream::out | std::ofstream::app);
2879 if (LogFileStream.fail()) {
2880 this->ThrowError("SetLogParams: Could not open file \"%s\" for writing.", target);
2881 return;
2882 }
2883 }
2884
2885 LogLevel = level;
2886 LogTarget = target;
2887 }
2888
2889 void ScriptEnvironment::LogMsg(int level, const char* fmt, ...)
2890 {
2891 va_list val;
2892 va_start(val, fmt);
2893 LogMsg_valist(level, fmt, val);
2894 va_end(val);
2895 }
2896
2897 34 void ScriptEnvironment::LogMsg_valist(int level, const char* fmt, va_list va)
2898 {
2899 // Don't output message if our logging level is not high enough
2900
1/2
✓ Branch 2 → 3 taken 34 times.
✗ Branch 2 → 4 not taken.
34 if (level > LogLevel) {
2901 34 return;
2902 }
2903
2904 // Setup string prefixes for output messages
2905 const char* levelStr = nullptr;
2906 uint16_t levelAttr;
2907 switch (level)
2908 {
2909 case LOGLEVEL_ERROR:
2910 levelStr = "ERROR: ";
2911 #ifdef AVS_WINDOWS // FOREGROUND_* is Windows-specific
2912 levelAttr = FOREGROUND_INTENSITY | FOREGROUND_RED;
2913 #endif
2914 break;
2915 case LOGLEVEL_WARNING:
2916 levelStr = "WARNING: ";
2917 #ifdef AVS_WINDOWS // FOREGROUND_* is Windows-specific
2918 levelAttr = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED;
2919 #endif
2920 break;
2921 case LOGLEVEL_INFO:
2922 levelStr = "INFO: ";
2923 #ifdef AVS_WINDOWS // FOREGROUND_* is Windows-specific
2924 levelAttr = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE;
2925 #endif
2926 break;
2927 case LOGLEVEL_DEBUG:
2928 levelStr = "DEBUG: ";
2929 #ifdef AVS_WINDOWS // FOREGROUND_* is Windows-specific
2930 levelAttr = FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_RED;
2931 #endif
2932 break;
2933 default:
2934 this->ThrowError("LogMsg: level argument must be between 1 and 4.");
2935 break;
2936 }
2937
2938 // Prepare message output target
2939 std::ostream* targetStream = nullptr;
2940 #ifdef AVS_WINDOWS
2941 void* hConsole = GetStdHandle(STD_ERROR_HANDLE);
2942 #else
2943 void* hConsole = stderr;
2944 #endif
2945
2946 if (streqi("stderr", LogTarget.c_str()))
2947 {
2948 #ifdef AVS_WINDOWS
2949 hConsole = GetStdHandle(STD_ERROR_HANDLE);
2950 #else
2951 hConsole = stderr;
2952 #endif
2953 targetStream = &std::cerr;
2954 }
2955 else if (streqi("stdout", LogTarget.c_str()))
2956 {
2957 #ifdef AVS_WINDOWS // linux alternative?
2958 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
2959 #else
2960 hConsole = stdout;
2961 #endif
2962 targetStream = &std::cout;
2963 }
2964 else if (LogFileStream.is_open())
2965 {
2966 targetStream = &LogFileStream;
2967 }
2968 else
2969 {
2970 // Logging not yet set up (SetLogParams() not yet called).
2971 // Do nothing.
2972 return;
2973 }
2974
2975 // Format our message string
2976 std::string msg = FormatString(fmt, va);
2977
2978 #ifdef AVS_WINDOWS
2979 // Save current console attributes so that we can restore them later
2980 CONSOLE_SCREEN_BUFFER_INFO Info;
2981 GetConsoleScreenBufferInfo(hConsole, &Info);
2982 #endif
2983
2984 // Do the output
2985 std::lock_guard<std::mutex> lock(string_mutex);
2986 *targetStream << "---------------------------------------------------------------------" << std::endl;
2987 #ifdef AVS_WINDOWS
2988 SetConsoleTextAttribute(hConsole, levelAttr);
2989 #endif
2990 *targetStream << levelStr;
2991 #ifdef AVS_WINDOWS
2992 SetConsoleTextAttribute(hConsole, Info.wAttributes);
2993 #endif
2994 *targetStream << msg << std::endl;
2995 targetStream->flush();
2996 }
2997
2998 1 void ScriptEnvironment::LogMsgOnce(const OneTimeLogTicket& ticket, int level, const char* fmt, ...)
2999 {
3000 va_list val;
3001 1 va_start(val, fmt);
3002
1/2
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 4 not taken.
1 LogMsgOnce_valist(ticket, level, fmt, val);
3003 1 va_end(val);
3004 1 }
3005
3006 1 void ScriptEnvironment::LogMsgOnce_valist(const OneTimeLogTicket& ticket, int level, const char* fmt, va_list va)
3007 {
3008
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 9 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 8 not taken.
1 if (LogTickets.end() == LogTickets.find(ticket))
3009 {
3010 1 LogMsg_valist(level, fmt, va);
3011 1 LogTickets.insert(ticket);
3012 }
3013 1 }
3014
3015 void ScriptEnvironment::SetMaxCPU(const char* features)
3016 {
3017 #if defined(ARM64)
3018 enum CPUlevel {
3019 CL_NONE,
3020 CL_NEON,
3021 CL_DOTPROD,
3022 CL_SVE2,
3023 CL_I8MM,
3024 CL_SVE2_1
3025 };
3026 #elif defined(X86_32) || defined(X86_64)
3027 enum CPUlevel {
3028 CL_NONE,
3029 CL_MMX,
3030 CL_SSE,
3031 CL_SSE2,
3032 CL_SSE3,
3033 CL_SSSE3,
3034 CL_SSE4_1,
3035 CL_SSE4_2,
3036 CL_AVX,
3037 CL_AVX2,
3038 CL_AVX512_BASE,
3039 CL_AVX512_FAST
3040 };
3041 #else
3042 enum CPUlevel {
3043 CL_NONE
3044 };
3045 #endif
3046
3047 std::string s;
3048 const int len = (int)strlen(features);
3049 s.resize(len);
3050 for (int i = 0; i < len; i++)
3051 s[i] = tolower(features[i]);
3052
3053 int64_t cpu_flags = GetCPUFlagsEx();
3054
3055 std::vector<std::string> tokens;
3056 std::size_t start = 0, end = 0;
3057 while ((end = s.find(',', start)) != std::string::npos) {
3058 if (end != start) {
3059 tokens.push_back(s.substr(start, end - start));
3060 }
3061 start = end + 1;
3062 }
3063 if (end != start) {
3064 tokens.push_back(s.substr(start));
3065 }
3066
3067 for (auto token : tokens)
3068 {
3069 token = trim(token);
3070 if (token.empty()) continue;
3071
3072 int mode = 0; // limit
3073
3074 char ch = token[token.size() - 1];
3075 if (ch == '-') mode = -1; // remove
3076 else if (ch == '+') mode = 1; // add
3077
3078 if (mode != 0)
3079 token.resize(token.size() - 1);
3080
3081 CPUlevel cpulevel = CL_NONE;
3082
3083 const char* t = token.c_str();
3084
3085 if (streqi(t, "") || streqi(t, "none")) cpulevel = CL_NONE;
3086 #if defined(ARM64)
3087 else if (streqi(t, "neon")) cpulevel = CL_NEON;
3088 else if (streqi(t, "dotprod")) cpulevel = CL_DOTPROD;
3089 else if (streqi(t, "sve2")) cpulevel = CL_SVE2;
3090 else if (streqi(t, "i8mm")) cpulevel = CL_I8MM;
3091 else if (streqi(t, "sve2.1")) cpulevel = CL_SVE2_1;
3092 // i8mm is just an optional level may not dependent on sve2 and vice versa
3093 else ThrowError("SetMaxCPU error: cpu level must be empty or none, neon, dotprod or sve2 (%s)", t);
3094 #elif defined(X86_32) || defined(X86_64)
3095 else if (streqi(t, "mmx")) cpulevel = CL_MMX;
3096 else if (streqi(t, "sse")) cpulevel = CL_SSE;
3097 else if (streqi(t, "sse2")) cpulevel = CL_SSE2;
3098 else if (streqi(t, "sse3")) cpulevel = CL_SSE3;
3099 else if (streqi(t, "ssse3")) cpulevel = CL_SSSE3;
3100 else if (streqi(t, "sse4") || streqi(t, "sse4.1")) cpulevel = CL_SSE4_1;
3101 else if (streqi(t, "sse4.2")) cpulevel = CL_SSE4_2;
3102 else if (streqi(t, "avx")) cpulevel = CL_AVX;
3103 else if (streqi(t, "avx2")) cpulevel = CL_AVX2;
3104 else if (streqi(t, "avx512base")) cpulevel = CL_AVX512_BASE;
3105 else if (streqi(t, "avx512fast")) cpulevel = CL_AVX512_FAST;
3106 else ThrowError("SetMaxCPU error: cpu level must be empty or none, mmx, sse, sse2, sse3, ssse3, sse4 or sse4.1, sse4.2, avx, avx2, avx512base or avx512fast (%s)", t);
3107 #else
3108 else ThrowError("SetMaxCPU error: cpu level must be empty or none (%s)", t);
3109 #endif
3110
3111 if (0 == mode) { // limit
3112 // always switch off the more advanced features compared to previous check if limiting
3113 #if defined(ARM64)
3114 if (cpulevel <= CL_SVE2_1) {
3115 // already max level, nothing to do
3116 }
3117 if (cpulevel <= CL_SVE2) {
3118 cpu_flags &= ~CPUF_ARM_SVE2_1;
3119 }
3120 if (cpulevel <= CL_DOTPROD) {
3121 cpu_flags &= ~CPUF_ARM_SVE2;
3122 }
3123 if (cpulevel <= CL_NEON) {
3124 cpu_flags &= ~CPUF_ARM_DOTPROD;
3125 }
3126 if (cpulevel <= CL_NONE) {
3127 cpu_flags &= ~CPUF_ARM_NEON; // switch off the most minimal feature
3128 }
3129
3130 #elif defined(X86_32) || defined(X86_64)
3131 // group feature
3132 if (cpulevel <= CL_AVX512_FAST) {
3133 // disable all avx512, re-enable the whole mask up to "fast"
3134 cpu_flags &= ~CPUF_AVX512_MASK;
3135 cpu_flags |= CPUF_AVX512_FAST_ALL;
3136 }
3137 // group feature
3138 if (cpulevel <= CL_AVX512_BASE) {
3139 // disable all avx512, re-enable the whole mask up to "base"
3140 cpu_flags &= ~CPUF_AVX512_MASK;
3141 cpu_flags |= CPUF_AVX512_BASE_ALL;
3142 }
3143 // individual features
3144 if (cpulevel <= CL_AVX2) // just disable all avx512
3145 cpu_flags &= ~CPUF_AVX512_MASK;
3146 if (cpulevel <= CL_AVX)
3147 cpu_flags &= ~(CPUF_AVX2 | CPUF_FMA3 | CPUF_FMA4 | CPUF_F16C);
3148 if (cpulevel <= CL_SSE4_2)
3149 cpu_flags &= ~(CPUF_AVX); // switch off AVX, when max is sse4.2, other features already off
3150 if (cpulevel <= CL_SSE4_1)
3151 cpu_flags &= ~(CPUF_SSE4_2);
3152 if (cpulevel <= CL_SSSE3)
3153 cpu_flags &= ~(CPUF_SSE4_1);
3154 if (cpulevel <= CL_SSE3)
3155 cpu_flags &= ~(CPUF_SSSE3);
3156 if (cpulevel <= CL_SSE2)
3157 cpu_flags &= ~(CPUF_SSE3);
3158 if (cpulevel <= CL_SSE)
3159 cpu_flags &= ~(CPUF_SSE2);
3160 if (cpulevel <= CL_MMX)
3161 cpu_flags &= ~(CPUF_SSE | CPUF_INTEGER_SSE); // ?
3162 if (cpulevel <= CL_NONE)
3163 cpu_flags &= ~(CPUF_MMX);
3164 #else
3165 // nothing to do, only "none" exists
3166 #endif
3167 }
3168 else {
3169 int64_t current_flag;
3170 switch (cpulevel) {
3171 #if defined(ARM64)
3172 case CL_SVE2_1: current_flag = CPUF_ARM_SVE2_1; break;
3173 case CL_I8MM: current_flag = CPUF_ARM_I8MM; break;
3174 case CL_SVE2: current_flag = CPUF_ARM_SVE2; break;
3175 case CL_DOTPROD: current_flag = CPUF_ARM_DOTPROD; break;
3176 case CL_NEON: current_flag = CPUF_ARM_NEON; break;
3177 #elif defined(X86_32) || defined(X86_64)
3178 case CL_AVX512_FAST: current_flag = CPUF_AVX512_FAST_ALL; break;
3179 case CL_AVX512_BASE: current_flag = CPUF_AVX512_BASE_ALL; break;
3180 case CL_AVX2: current_flag = CPUF_AVX2 | CPUF_FMA3; break;
3181 case CL_AVX: current_flag = CPUF_AVX; break;
3182 case CL_SSE4_2: current_flag = CPUF_SSE4_2; break;
3183 case CL_SSE4_1: current_flag = CPUF_SSE4_1; break;
3184 case CL_SSSE3: current_flag = CPUF_SSSE3; break;
3185 case CL_SSE3: current_flag = CPUF_SSE3; break;
3186 case CL_SSE2: current_flag = CPUF_SSE2; break;
3187 case CL_SSE: current_flag = CPUF_SSE; break;
3188 case CL_MMX: current_flag = CPUF_MMX; break;
3189 #else
3190 // nothing to do, only "none" exists
3191 #endif
3192 default:
3193 current_flag = 0;
3194 }
3195 if (mode < 0) {
3196 if (0 != current_flag)
3197 cpu_flags &= ~current_flag; // sse2-: removes sse2
3198 }
3199 else
3200 cpu_flags |= current_flag; // avx2+: adds avx2 and fma3
3201 // limit to sse2 and avx2: "sse2,avx2+"
3202 }
3203 }
3204
3205 cpuFlagsEx = cpu_flags;
3206 }
3207
3208 13 ClipDataStore* ScriptEnvironment::ClipData(IClip *clip)
3209 {
3210 #if ( !defined(_MSC_VER) || (_MSC_VER < 1900) )
3211
1/2
✓ Branch 2 → 3 taken 13 times.
✗ Branch 2 → 7 not taken.
13 return &(clip_data.emplace(clip, clip).first->second);
3212 #else
3213 return &(clip_data.try_emplace(clip, clip).first->second);
3214 #endif
3215 }
3216
3217 508 void ScriptEnvironment::AdjustMemoryConsumption(size_t amount, bool minus)
3218 {
3219
2/2
✓ Branch 2 → 3 taken 254 times.
✓ Branch 2 → 6 taken 254 times.
508 if (minus)
3220 254 Devices->GetCPUDevice()->memory_used -= amount;
3221 else
3222 254 Devices->GetCPUDevice()->memory_used += amount;
3223 508 }
3224
3225 void ScriptEnvironment::ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion)
3226 {
3227 thread_pool->QueueJob(jobFunc, jobData, threadEnv.get(), static_cast<JobCompletion*>(completion));
3228 }
3229
3230 void ScriptEnvironment::ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion, InternalEnvironment* env)
3231 {
3232 thread_pool->QueueJob(jobFunc, jobData, env, static_cast<JobCompletion*>(completion));
3233 }
3234
3235 void ScriptEnvironment::SetFilterMTMode(const char* filter, MtMode mode, bool force)
3236 {
3237 this->SetFilterMTMode(filter, mode, force ? MtWeight::MT_WEIGHT_2_USERFORCE : MtWeight::MT_WEIGHT_1_USERSPEC);
3238 }
3239
3240 void ScriptEnvironment::SetFilterMTMode(const char* filter, MtMode mode, MtWeight weight)
3241 {
3242 assert(NULL != filter);
3243 assert(strcmp("", filter) != 0);
3244
3245 if (((int)mode <= (int)MT_INVALID)
3246 || ((int)mode >= (int)MT_MODE_COUNT))
3247 {
3248 throw AvisynthError("Invalid MT mode specified.");
3249 }
3250
3251 if (streqi(filter, DEFAULT_MODE_SPECIFIER.c_str()))
3252 {
3253 DefaultMtMode = mode;
3254 return;
3255 }
3256
3257 std::string name_to_register;
3258 std::string loading;
3259 {
3260 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3261 loading = plugin_manager->PluginLoading();
3262 }
3263 if (loading.empty())
3264 name_to_register = filter;
3265 else
3266 name_to_register = loading.append("_").append(filter);
3267
3268 name_to_register = NormalizeString(name_to_register);
3269
3270 auto it = MtMap.find(name_to_register);
3271 if (it != MtMap.end())
3272 {
3273 if ((int)weight >= (int)(it->second.second))
3274 {
3275 it->second.first = mode;
3276 it->second.second = weight;
3277 }
3278 }
3279 else
3280 {
3281 MtMap.emplace(name_to_register, std::make_pair(mode, weight));
3282 }
3283 }
3284
3285 7 bool ScriptEnvironment::FilterHasMtMode(const Function* filter) const
3286 {
3287
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 7 times.
7 if (filter->name == nullptr) { // no named function
3288 return false;
3289 }
3290 7 const auto& end = MtMap.end();
3291
5/16
✓ Branch 7 → 8 taken 7 times.
✗ Branch 7 → 39 not taken.
✓ Branch 8 → 9 taken 7 times.
✗ Branch 8 → 39 not taken.
✓ Branch 9 → 10 taken 7 times.
✗ Branch 9 → 39 not taken.
✓ Branch 31 → 32 taken 7 times.
✗ Branch 31 → 33 not taken.
✓ Branch 33 → 34 taken 7 times.
✗ Branch 33 → 36 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 60 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 65 not taken.
21 return (end != MtMap.find(NormalizeString(filter->canon_name)))
3292
9/24
✓ Branch 11 → 12 taken 7 times.
✗ Branch 11 → 19 not taken.
✓ Branch 14 → 15 taken 7 times.
✗ Branch 14 → 39 not taken.
✓ Branch 15 → 16 taken 7 times.
✗ Branch 15 → 39 not taken.
✓ Branch 16 → 17 taken 7 times.
✗ Branch 16 → 39 not taken.
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 7 times.
✓ Branch 22 → 23 taken 7 times.
✗ Branch 22 → 24 not taken.
✓ Branch 24 → 25 taken 7 times.
✗ Branch 24 → 26 not taken.
✓ Branch 26 → 27 taken 7 times.
✗ Branch 26 → 29 not taken.
✓ Branch 29 → 30 taken 7 times.
✗ Branch 29 → 31 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 51 not taken.
28 || (end != MtMap.find(NormalizeString(filter->name)));
3293 }
3294
3295 static bool isValidVSMapKey(const std::string& s); // forward
3296
3297 void ScriptEnvironment::SetFilterProp(const char* filter, const char* key, const AVSValue& value, int mode)
3298 {
3299 assert(filter != nullptr && *filter != '\0');
3300 assert(key != nullptr);
3301
3302 if (!isValidVSMapKey(std::string(key)))
3303 ThrowError("SetFilterProp: invalid property key '%s'. Must start with a letter or underscore, "
3304 "followed only by letters, digits and underscores.", key);
3305
3306 if (mode != AVSPropAppendMode::PROPAPPENDMODE_REPLACE
3307 && mode != AVSPropAppendMode::PROPAPPENDMODE_APPEND
3308 && mode != AVSPropAppendMode::PROPAPPENDMODE_TOUCH)
3309 ThrowError("SetFilterProp: invalid mode %d for filter '%s', key '%s'.", mode, filter, key);
3310
3311 std::string name_to_register;
3312 std::string loading;
3313 {
3314 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3315 loading = plugin_manager->PluginLoading();
3316 }
3317 if (loading.empty())
3318 name_to_register = filter;
3319 else
3320 name_to_register = loading + "_" + filter;
3321
3322 name_to_register = NormalizeString(name_to_register);
3323
3324 FilterPropMap[name_to_register].push_back(PropEntry{ std::string(key), value, mode, {}, AVSValue() });
3325 }
3326
3327 void ScriptEnvironment::SetFilterPropConditional(const char* filter, const char* param_name,
3328 const AVSValue& param_match, const char* key, const AVSValue& value, int mode)
3329 {
3330 assert(filter != nullptr && *filter != '\0');
3331 assert(param_name != nullptr && *param_name != '\0');
3332 assert(key != nullptr);
3333
3334 if (!isValidVSMapKey(std::string(key)))
3335 ThrowError("SetFilterProp: invalid property key '%s'. Must start with a letter or underscore, "
3336 "followed only by letters, digits and underscores.", key);
3337
3338 if (mode != AVSPropAppendMode::PROPAPPENDMODE_REPLACE
3339 && mode != AVSPropAppendMode::PROPAPPENDMODE_APPEND
3340 && mode != AVSPropAppendMode::PROPAPPENDMODE_TOUCH)
3341 ThrowError("SetFilterProp: invalid mode %d for filter '%s', key '%s'.", mode, filter, key);
3342
3343 std::string name_to_register;
3344 std::string loading;
3345 {
3346 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3347 loading = plugin_manager->PluginLoading();
3348 }
3349 if (loading.empty())
3350 name_to_register = filter;
3351 else
3352 name_to_register = loading + "_" + filter;
3353
3354 name_to_register = NormalizeString(name_to_register);
3355
3356 FilterPropMap[name_to_register].push_back(PropEntry{ std::string(key), value, mode,
3357 std::string(param_name), param_match });
3358 }
3359
3360 void ScriptEnvironment::SetFilterPropPassthrough(const char* filter)
3361 {
3362 assert(filter != nullptr && *filter != '\0');
3363
3364 std::string name_to_register;
3365 std::string loading;
3366 {
3367 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3368 loading = plugin_manager->PluginLoading();
3369 }
3370 if (loading.empty())
3371 name_to_register = filter;
3372 else
3373 name_to_register = loading + "_" + filter;
3374
3375 FilterPropPassthroughSet.insert(NormalizeString(name_to_register));
3376 }
3377
3378 const char* ScriptEnvironment::GetFilterProps()
3379 {
3380 std::string json = "[\n";
3381 bool first = true;
3382 for (const auto& kv : FilterPropMap) {
3383 for (const auto& entry : kv.second) {
3384 if (!first) json += ",\n";
3385 first = false;
3386 json += " {\"filter\":\"" + kv.first + "\",\"key\":\"" + entry.key + "\"";
3387 if (!entry.param_name.empty()) {
3388 json += ",\"when_param\":\"" + entry.param_name + "\",\"when_value\":";
3389 // param_match can be a scalar or an alias array
3390 auto serializeScalar = [&](const AVSValue& v) -> std::string {
3391 if (v.IsString()) return "\"" + std::string(v.AsString()) + "\"";
3392 if (v.IsInt()) return std::to_string(v.AsLong());
3393 if (v.IsFloat()) return double_to_string(v.AsFloat());
3394 if (v.IsBool()) return v.AsBool() ? "true" : "false";
3395 return "null";
3396 };
3397 if (entry.param_match.IsArray()) {
3398 json += "[";
3399 for (int j = 0; j < entry.param_match.ArraySize(); ++j) {
3400 if (j > 0) json += ",";
3401 json += serializeScalar(entry.param_match[j]);
3402 }
3403 json += "]";
3404 } else {
3405 json += serializeScalar(entry.param_match);
3406 }
3407 }
3408 json += ",";
3409 if (entry.value.IsFunction()) {
3410 json += "\"type\":\"function\",\"value\":null";
3411 } else if (entry.value.IsInt()) {
3412 json += "\"type\":\"int\",\"value\":" + std::to_string(entry.value.AsLong());
3413 } else if (entry.value.IsBool()) {
3414 // bool values are converted to int at registration; this branch is a safety net
3415 json += "\"type\":\"int\",\"value\":" + std::to_string(entry.value.AsBool() ? 1 : 0);
3416 } else if (entry.value.IsFloat()) {
3417 json += "\"type\":\"float\",\"value\":" + double_to_string(entry.value.AsFloat());
3418 } else if (entry.value.IsString()) {
3419 std::string escaped;
3420 for (unsigned char c : std::string(entry.value.AsString())) {
3421 if (c == '"') escaped += "\\\"";
3422 else if (c == '\\') escaped += "\\\\";
3423 else if (c == '\n') escaped += "\\n";
3424 else if (c == '\r') escaped += "\\r";
3425 else escaped += c;
3426 }
3427 json += "\"type\":\"string\",\"value\":\"" + escaped + "\"";
3428 } else if (!entry.value.Defined()) {
3429 json += "\"type\":\"capture\",\"value\":null"; // undefined(): capture from named call arg
3430 } else {
3431 json += "\"type\":\"unknown\",\"value\":null";
3432 }
3433 json += ",\"mode\":" + std::to_string(entry.mode) + "}";
3434 }
3435 }
3436 json += "\n]";
3437 return threadEnv->SaveString(json.c_str(), (int)json.size());
3438 }
3439
3440 21 MtMode ScriptEnvironment::GetFilterMTMode(const Function* filter, bool* is_forced) const
3441 {
3442
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 21 times.
21 assert(NULL != filter);
3443
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 21 times.
21 if (filter->name == nullptr) { // no named function
3444 *is_forced = false;
3445 return DefaultMtMode;
3446 }
3447
3448
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 21 times.
21 assert(NULL != filter->name);
3449
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 21 times.
21 assert(NULL != filter->canon_name);
3450
3451
3/6
✓ Branch 12 → 13 taken 21 times.
✗ Branch 12 → 45 not taken.
✓ Branch 13 → 14 taken 21 times.
✗ Branch 13 → 43 not taken.
✓ Branch 14 → 15 taken 21 times.
✗ Branch 14 → 41 not taken.
42 auto it = MtMap.find(NormalizeString(filter->canon_name));
3452
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 24 taken 21 times.
21 if (it != MtMap.end())
3453 {
3454 *is_forced = it->second.second == MtWeight::MT_WEIGHT_2_USERFORCE;
3455 return it->second.first;
3456 }
3457
3458
3/6
✓ Branch 26 → 27 taken 21 times.
✗ Branch 26 → 54 not taken.
✓ Branch 27 → 28 taken 21 times.
✗ Branch 27 → 52 not taken.
✓ Branch 28 → 29 taken 21 times.
✗ Branch 28 → 50 not taken.
42 it = MtMap.find(NormalizeString(filter->name));
3459
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 38 taken 21 times.
21 if (it != MtMap.end())
3460 {
3461 *is_forced = it->second.second == MtWeight::MT_WEIGHT_2_USERFORCE;
3462 return it->second.first;
3463 }
3464
3465 21 *is_forced = false;
3466 21 return DefaultMtMode;
3467 }
3468
3469 /* This function adds information about builtin functions into global variables.
3470 * External utilities (like AvsPmod) can parse these variables and use them
3471 * to learn about supported functions and their syntax.
3472 */
3473 453 void ScriptEnvironment::ExportBuiltinFilters()
3474 {
3475 453 std::string FunctionList;
3476
1/2
✓ Branch 3 → 4 taken 453 times.
✗ Branch 3 → 46 not taken.
453 FunctionList.reserve(512);
3477 453 const size_t NumFunctionArrays = sizeof(builtin_functions) / sizeof(builtin_functions[0]);
3478
2/2
✓ Branch 26 → 5 taken 14496 times.
✓ Branch 26 → 27 taken 453 times.
14949 for (size_t i = 0; i < NumFunctionArrays; ++i)
3479 {
3480
3/4
✓ Branch 23 → 24 taken 270441 times.
✗ Branch 23 → 46 not taken.
✓ Branch 24 → 6 taken 255945 times.
✓ Branch 24 → 25 taken 14496 times.
270441 for (const AVSFunction* f = builtin_functions[i]; !f->empty(); ++f)
3481 {
3482 // This builds the $InternalFunctions$ variable, which is a list of space-delimited
3483 // function names. Utilities can learn the names of the builtin function from this.
3484
1/2
✓ Branch 6 → 7 taken 255945 times.
✗ Branch 6 → 42 not taken.
255945 FunctionList.append(f->name);
3485
1/2
✓ Branch 7 → 8 taken 255945 times.
✗ Branch 7 → 42 not taken.
255945 FunctionList.push_back(' ');
3486
3487 // For each supported function, a global variable is added with <param_var_name> as the name,
3488 // and the list of parameters to that function as the value.
3489 255945 std::string param_var_name;
3490
1/2
✓ Branch 9 → 10 taken 255945 times.
✗ Branch 9 → 40 not taken.
255945 param_var_name.reserve(128);
3491
1/2
✓ Branch 10 → 11 taken 255945 times.
✗ Branch 10 → 40 not taken.
255945 param_var_name.append("$Plugin!");
3492
1/2
✓ Branch 11 → 12 taken 255945 times.
✗ Branch 11 → 40 not taken.
255945 param_var_name.append(f->name);
3493
1/2
✓ Branch 12 → 13 taken 255945 times.
✗ Branch 12 → 40 not taken.
255945 param_var_name.append("!Param$");
3494
3/6
✓ Branch 14 → 15 taken 255945 times.
✗ Branch 14 → 39 not taken.
✓ Branch 18 → 19 taken 255945 times.
✗ Branch 18 → 37 not taken.
✓ Branch 19 → 20 taken 255945 times.
✗ Branch 19 → 37 not taken.
255945 threadEnv->SetGlobalVar(threadEnv->SaveString(param_var_name.c_str(), (int)param_var_name.size()), AVSValue(f->param_types));
3495 255945 }
3496 }
3497
3498 // Save $InternalFunctions$
3499
3/6
✓ Branch 31 → 32 taken 453 times.
✗ Branch 31 → 45 not taken.
✓ Branch 32 → 33 taken 453 times.
✗ Branch 32 → 45 not taken.
✓ Branch 33 → 34 taken 453 times.
✗ Branch 33 → 43 not taken.
453 threadEnv->SetGlobalVar("$InternalFunctions$", AVSValue(threadEnv->SaveString(FunctionList.c_str(), (int)FunctionList.size())));
3500 453 }
3501
3502 120 size_t ScriptEnvironment::GetEnvProperty(AvsEnvProperty prop)
3503 {
3504
1/14
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 6 not taken.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 8 not taken.
✗ Branch 2 → 9 not taken.
✓ Branch 2 → 11 taken 120 times.
✗ Branch 2 → 12 not taken.
✗ Branch 2 → 14 not taken.
✗ Branch 2 → 15 not taken.
✗ Branch 2 → 17 not taken.
✗ Branch 2 → 18 not taken.
✗ Branch 2 → 19 not taken.
✗ Branch 2 → 20 not taken.
✗ Branch 2 → 21 not taken.
120 switch (prop)
3505 {
3506 case AEP_NUM_DEVICES:
3507 return Devices->GetNumDevices();
3508 case AEP_FRAME_ALIGN:
3509 return frame_align;
3510 case AEP_PLANE_ALIGN:
3511 return plane_align;
3512 case AEP_FILTERCHAIN_THREADS:
3513 return nMaxFilterInstances;
3514 case AEP_PHYSICAL_CPUS:
3515 return GetNumPhysicalCPUs();
3516 120 case AEP_CACHESIZE_L2:
3517 120 return cache_size_L2;
3518 case AEP_LOGICAL_CPUS:
3519 return std::thread::hardware_concurrency();
3520 case AEP_THREAD_ID:
3521 return 0;
3522 case AEP_THREADPOOL_THREADS:
3523 return thread_pool->NumThreads();
3524 case AEP_VERSION:
3525 #ifdef RELEASE_TARBALL
3526 return 0;
3527 #else
3528 return AVS_SEQREV;
3529 #endif
3530 case AEP_HOST_SYSTEM_ENDIANNESS:
3531 return (uintptr_t)AVS_ENDIANNESS;
3532 case AEP_INTERFACE_VERSION:
3533 return AVISYNTH_INTERFACE_VERSION;
3534 case AEP_INTERFACE_BUGFIX:
3535 return AVISYNTHPLUS_INTERFACE_BUGFIX_VERSION;
3536 default:
3537 this->ThrowError("Invalid property request.");
3538 return std::numeric_limits<size_t>::max();
3539 }
3540
3541 assert(0);
3542 }
3543
3544 bool ScriptEnvironment::LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result)
3545 {
3546 // Autoload needed to ensure that manual LoadPlugin() calls always override autoloaded plugins.
3547 // For that, autoloading must happen before any LoadPlugin(), so we force an
3548 // autoload operation before any LoadPlugin().
3549 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3550
3551 this->AutoloadPlugins();
3552 return plugin_manager->LoadPlugin(filePath, throwOnError, result);
3553 }
3554
3555 void ScriptEnvironment::AddAutoloadDir(const char* dirPath, bool toFront)
3556 {
3557 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3558 plugin_manager->AddAutoloadDir(dirPath, toFront);
3559 }
3560
3561 void ScriptEnvironment::ClearAutoloadDirs()
3562 {
3563 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3564 plugin_manager->ClearAutoloadDirs();
3565 }
3566
3567 char* ScriptEnvironment::ListAutoloadDirs()
3568 {
3569 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3570
3571 std::string str = plugin_manager->ListAutoloadDirs();
3572 return threadEnv->SaveString(str.c_str(), (int)str.size());
3573 }
3574
3575 void ScriptEnvironment::AutoloadPlugins()
3576 {
3577 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3578 plugin_manager->AutoloadPlugins();
3579 }
3580
3581 int ScriptEnvironment::SetMemoryMax(int mem) {
3582
3583 Device* cpuDevice = Devices->GetCPUDevice();
3584 if (mem > 0) /* If mem is zero, we should just return current setting */
3585 cpuDevice->memory_max = ConstrainMemoryRequest(mem * 1048576ull);
3586
3587 return (int)(cpuDevice->memory_max / 1048576ull);
3588 }
3589
3590 int ScriptEnvironment::SetWorkingDir(const char* newdir) {
3591 return SetCurrentDirectory(newdir) ? 0 : 1;
3592 }
3593
3594 void ScriptEnvironment::CheckVersion(int version) {
3595 if (version > AVISYNTH_INTERFACE_VERSION)
3596 ThrowError("Plugin was designed for a later version of Avisynth (%d)", version);
3597 }
3598
3599 260 int ScriptEnvironment::GetCPUFlags() { return cpuFlagsEx & 0xFFFFFFFF; }
3600 3 int64_t ScriptEnvironment::GetCPUFlagsEx() { return cpuFlagsEx; }
3601
3602 void ScriptEnvironment::AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) {
3603 this->AddFunction(name, params, apply, user_data, NULL);
3604 }
3605
3606 // called from IScriptEnvironment_Avs25
3607 void ScriptEnvironment::AddFunction25(const char* name, const char* params, ApplyFunc apply, void* user_data) {
3608 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3609 plugin_manager->AddFunction(name, params, apply, user_data, NULL, true, false);
3610 }
3611
3612 // called from IScriptEnvironment_AvsPreV11C
3613 void ScriptEnvironment::AddFunctionPreV11C(const char* name, const char* params, ApplyFunc apply, void* user_data) {
3614 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3615 plugin_manager->AddFunction(name, params, apply, user_data, NULL, false, true);
3616 }
3617
3618 void ScriptEnvironment::AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data, const char *exportVar) {
3619 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
3620 plugin_manager->AddFunction(name, params, apply, user_data, exportVar, false, false);
3621 }
3622
3623 891 VideoFrame* ScriptEnvironment::AllocateFrame(size_t vfb_size, size_t margin, Device* device)
3624 {
3625
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 9 taken 891 times.
891 if (vfb_size > (size_t)std::numeric_limits<int>::max())
3626 {
3627 throw AvisynthError(threadEnv->Sprintf("Requested buffer size of %zu is too large", vfb_size));
3628 }
3629
3630 891 VFBStorage* vfb = NULL;
3631 try
3632 {
3633
3/8
✓ Branch 9 → 10 taken 891 times.
✗ Branch 9 → 36 not taken.
✓ Branch 10 → 11 taken 891 times.
✗ Branch 10 → 33 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 891 times.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
891 vfb = new VFBStorage((int)vfb_size, (int)margin, device);
3634 }
3635 catch(const std::bad_alloc&)
3636 {
3637 return NULL;
3638 }
3639
3640 891 VideoFrame *new_frame = NULL;
3641 try
3642 {
3643
5/14
✓ Branch 13 → 14 taken 891 times.
✗ Branch 13 → 46 not taken.
✓ Branch 14 → 15 taken 891 times.
✗ Branch 14 → 43 not taken.
✓ Branch 15 → 16 taken 891 times.
✗ Branch 15 → 40 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 891 times.
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 891 times.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
891 new_frame = new VideoFrame(vfb, new AVSMap(), 0, 0, 0, 0, VideoInfo::CS_UNKNOWN);
3644 }
3645 catch(const std::bad_alloc&)
3646 {
3647 delete vfb;
3648 return NULL;
3649 }
3650
3651 891 device->memory_used += vfb_size;
3652 891 vfb->Attach(threadEnv->GetCurrentGraphNode());
3653
3654 // automatically inserts keys if they not exist!
3655 // no locking here, calling method have done it already
3656
2/4
✓ Branch 26 → 27 taken 891 times.
✗ Branch 26 → 54 not taken.
✓ Branch 28 → 29 taken 891 times.
✗ Branch 28 → 53 not taken.
891 FrameRegistry2[vfb_size][vfb].push_back(DebugTimestampedFrame(new_frame));
3657
3658 //_RPT1(0, "ScriptEnvironment::AllocateFrame %zu frame=%p vfb=%p %" PRIu64 "\n", vfb_size, newFrame, newFrame->vfb, memory_used);
3659
3660 891 return new_frame;
3661 }
3662
3663 #ifdef _DEBUG
3664
3665 static void DebugOut(char* s)
3666 {
3667 #ifdef AVS_POSIX
3668 LogMsg(LOGLEVEL_DEBUG, s);
3669 #else
3670 _RPT0(0, s);
3671 #endif
3672 }
3673
3674 void ScriptEnvironment::ListFrameRegistry(size_t min_size, size_t max_size, bool someframes)
3675 {
3676 char buf[1024];
3677 //#define FULL_LIST_OF_VFBs
3678 //#define LIST_ALSO_SOME_FRAMES
3679 int size1 = 0;
3680 int size2 = 0;
3681 int size3 = 0;
3682 snprintf(buf, 1023, "******** %p <= FrameRegistry2 Address. Buffer list for size between %7zu and %7zu\n", &FrameRegistry2, min_size, max_size);
3683 DebugOut(buf);
3684 snprintf(buf, 1023, ">> IterateLevel #1: Different vfb sizes: FrameRegistry2.size=%zu \n", FrameRegistry2.size());
3685 DebugOut(buf);
3686 size_t total_vfb_size = 0;
3687 auto t_end = std::chrono::high_resolution_clock::now();
3688
3689 // list to debugview: all frames up-to vfb_size size
3690 for (FrameRegistryType2::iterator it = FrameRegistry2.lower_bound(min_size), end_it = FrameRegistry2.upper_bound(max_size);
3691 it != end_it;
3692 ++it)
3693 {
3694 size1++;
3695 _RPT3(0, ">>>> IterateLevel #2 [%3d]: Vfb count for size %7zu is %7zu\n", size1, it->first, it->second.size());
3696 for (auto &it2: it->second)
3697 {
3698 size2++;
3699 VFBStorage* vfb = static_cast<VFBStorage*>(it2.first);
3700 total_vfb_size += vfb->GetDataSize();
3701 size_t inner_frame_count_size = it2.second.size();
3702 #ifdef ALTERNATIVE_VFB_TIMESTAMP
3703 snprintf(buf, 1023, ">>>> IterateLevel #3 %5zu frames in [%3d,%5d] --> vfb=%p vfb_refcount=%3ld vfb_timestamp=%" PRId64 " seqNum=%d\n", inner_frame_count_size, size1, size2, vfb, vfb->refcount, vfb->last_freed_timestamp.load(), vfb->GetSequenceNumber());
3704 #else
3705 snprintf(buf, 1023, ">>>> IterateLevel #3 %5zu frames in [%3d,%5d] --> vfb=%p vfb_refcount=%3ld seqNum=%d\n", inner_frame_count_size, size1, size2, vfb, vfb->refcount, vfb->GetSequenceNumber());
3706 #endif
3707 DebugOut(buf);
3708 // iterate the frame list of this vfb
3709 int inner_frame_count = 0;
3710 int inner_frame_count_for_frame_refcount_nonzero = 0;
3711 for (auto &it3: it2.second)
3712 {
3713 size3++;
3714 inner_frame_count++;
3715 #ifdef _DEBUG
3716 VideoFrame* frame = it3.frame;
3717 std::chrono::time_point<std::chrono::high_resolution_clock> frame_entry_timestamp = it3.timestamp;
3718 #else
3719 VideoFrame* frame = it3;
3720 #endif
3721 if (0 != frame->refcount)
3722 inner_frame_count_for_frame_refcount_nonzero++;
3723 if (someframes)
3724 {
3725 std::chrono::duration<double> elapsed_seconds = t_end - frame_entry_timestamp;
3726 if (inner_frame_count <= 4) // list only the first 2. There can be even many thousand of frames!
3727 {
3728 // log only if frame creation timestamp is too old!
3729 // e.g. 100 secs, it must be a stuck frame (but can also be a valid static frame from ColorBars)
3730 // if (elapsed_seconds.count() > 100.0f && frame->refcount > 0)
3731 //if (frame->refcount > 0)
3732 {
3733 snprintf(buf, 1023, " >> Frame#%6d: vfb=%p frame=%p frame_refcount=%3ld T=%f ago\n", inner_frame_count, vfb, frame, frame->refcount, elapsed_seconds.count());
3734 DebugOut(buf);
3735 }
3736 }
3737 else if (inner_frame_count == inner_frame_count_size - 1)
3738 {
3739 // log the last one
3740 if (frame->refcount > 0)
3741 {
3742 snprintf(buf, 1023, " ...Frame#%6d: vfb=%p frame=%p frame_refcount=%3ld \n", inner_frame_count, vfb, frame, frame->refcount);
3743 DebugOut(buf);
3744 }
3745 _RPT2(0, " == TOTAL of %d frames. Number of nonzero refcount=%d \n", inner_frame_count, inner_frame_count_for_frame_refcount_nonzero);
3746 }
3747 if (0 == vfb->refcount && 0 != frame->refcount)
3748 {
3749 snprintf(buf, 1023, " ########## VFB=0 FRAME!=0 ####### VFB: %p Frame:%p frame_refcount=%3ld \n", vfb, frame, frame->refcount);
3750 DebugOut(buf);
3751 }
3752 }
3753 }
3754 // After the inner loop, always print summary:
3755 snprintf(buf, 1023, " == TOTAL of %d frames. nonzero refcount=%d \n",
3756 inner_frame_count, inner_frame_count_for_frame_refcount_nonzero);
3757 DebugOut(buf);
3758 }
3759 }
3760 snprintf(buf, 1023, ">> >> >> array sizes %d %d %d Total VFB size=%zu\n", size1, size2, size3, total_vfb_size);
3761 DebugOut(buf);
3762 snprintf(buf, 1023, " ----------------------------\n");
3763 DebugOut(buf);
3764 }
3765 #endif
3766
3767 #ifndef ALTERNATIVE_VFB_TIMESTAMP
3768 903 VideoFrame* ScriptEnvironment::GetFrameFromRegistry(size_t vfb_size, Device* device)
3769 {
3770 #ifdef _DEBUG
3771 std::chrono::time_point<std::chrono::high_resolution_clock> t_start, t_end; // std::chrono::time_point<std::chrono::system_clock> t_start, t_end;
3772 t_start = std::chrono::high_resolution_clock::now();
3773 #endif
3774
3775 // FrameRegistry2 is like: map<key1, map<key2, vector<VideoFrame *>> >
3776 // typedef std::vector<VideoFrame*> VideoFrameArrayType;
3777 // typedef std::map<VideoFrameBuffer *, VideoFrameArrayType> FrameBufferRegistryType;
3778 // typedef std::map<size_t, FrameBufferRegistryType> FrameRegistryType2;
3779 // [vfb_size = 10000][vfb = 0x111111111] [frame = 0x129837192(,timestamp=xxx)]
3780 // [frame = 0x012312122(,timestamp=xxx)]
3781 // [frame = 0x232323232(,timestamp=xxx)]
3782 // [vfb = 0x222222222] [frame = 0x333333333(,timestamp=xxx)]
3783 // [frame = 0x444444444(,timestamp=xxx)]
3784 // Which is better?
3785 // - found exact vfb_size or
3786 // - allow reusing existing vfb's with size up to size_to_find*1.5 THIS ONE!
3787 // - allow to occupy any buffer that is bigger than the requested size
3788 //for (FrameRegistryType2::iterator it = FrameRegistry2.lower_bound(vfb_size), end_it = FrameRegistry2.upper_bound(vfb_size); // exact! no-go. special service clips can fragment it
3789 //for (FrameRegistryType2::iterator it = FrameRegistry2.lower_bound(vfb_size), end_it = FrameRegistry2.end(); // vfb_size or bigger, so a 100K size would claim a 1.5M space.
3790
2/4
✓ Branch 2 → 3 taken 903 times.
✗ Branch 2 → 74 not taken.
✓ Branch 3 → 4 taken 903 times.
✗ Branch 3 → 67 not taken.
903 for (FrameRegistryType2::iterator it = FrameRegistry2.lower_bound(vfb_size), end_it = FrameRegistry2.upper_bound(vfb_size * 3 / 2); // vfb_size or at most 1.5* bigger
3791
2/2
✓ Branch 64 → 5 taken 530 times.
✓ Branch 64 → 65 taken 891 times.
1421 it != end_it;
3792 518 ++it)
3793 {
3794
2/2
✓ Branch 60 → 8 taken 1399 times.
✓ Branch 60 → 61 taken 518 times.
1917 for (auto &it2: it->second)
3795 {
3796 1399 VFBStorage *vfb = static_cast<VFBStorage*>(it2.first); // same for all map content, the key is vfb pointer
3797
5/6
✓ Branch 9 → 10 taken 1399 times.
✗ Branch 9 → 12 not taken.
✓ Branch 10 → 11 taken 12 times.
✓ Branch 10 → 12 taken 1387 times.
✓ Branch 13 → 14 taken 12 times.
✓ Branch 13 → 58 taken 1387 times.
1399 if (device == vfb->device && 0 == vfb->refcount) // vfb device and refcount check
3798 {
3799 12 size_t videoFrameListSize = it2.second.size();
3800 // size is more than one if SubFrame was used to create a new frame
3801 VideoFrame *frame_found;
3802 12 bool found = false;
3803 12 for (VideoFrameArrayType::iterator it3 = it2.second.begin(), end_it3 = it2.second.end();
3804
1/2
✓ Branch 51 → 17 taken 12 times.
✗ Branch 51 → 52 not taken.
12 it3 != end_it3;
3805 /* ++it3 not here, because of the delete */)
3806 {
3807 12 VideoFrame *frame = it3->frame;
3808
3809 // sanity check if its refcount is zero
3810 // because when a vfb is free (refcount==0) then all its parent frames should also be free
3811
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 12 times.
12 assert(0 == frame->refcount);
3812
3813 // Note: Release() does not free 'properties'; FrameRegistry is the sole owner.
3814 // An Avisynth 2.5 filter ("baked code" in ancient avisynth.h) can set refcount
3815 // to zero without freeing extra frame data, so nullptr is not guaranteed here.
3816 // Clear content now to promptly free any large strings/arrays.
3817
1/2
✓ Branch 21 → 22 taken 12 times.
✗ Branch 21 → 23 not taken.
12 if (frame->properties != nullptr)
3818
1/2
✓ Branch 22 → 23 taken 12 times.
✗ Branch 22 → 71 not taken.
12 frame->properties->clear();
3819
3820
1/2
✓ Branch 23 → 24 taken 12 times.
✗ Branch 23 → 38 not taken.
12 if (!found)
3821 {
3822 12 InterlockedIncrement(&(frame->vfb->refcount)); // same as &(vfb->refcount)
3823 12 vfb->free_count = 0; // reset free count
3824
2/4
✓ Branch 25 → 26 taken 12 times.
✗ Branch 25 → 71 not taken.
✓ Branch 26 → 27 taken 12 times.
✗ Branch 26 → 71 not taken.
12 vfb->Attach(threadEnv->GetCurrentGraphNode());
3825 #ifdef _DEBUG
3826 char buf[256];
3827 t_end = std::chrono::high_resolution_clock::now();
3828 std::chrono::duration<double> elapsed_seconds = t_end - t_start;
3829 snprintf(buf, 255, "ScriptEnvironment::GetNewFrame NEW METHOD EXACT hit! VideoFrameListSize=%7zu GotSize=%7zu FrReg.Size=%6zu vfb=%p frame=%p SeekTime:%f\n", videoFrameListSize, vfb_size, FrameRegistry2.size(), vfb, frame, elapsed_seconds.count());
3830 _RPT0(0, buf);
3831 _RPT5(0, " frame %p RowSize=%d Height=%d Pitch=%d Offset=%d\n", frame, frame->GetRowSize(), frame->GetHeight(), frame->GetPitch(), frame->GetOffset());
3832 #endif
3833 // properties was cleared above; create the AVSMap shell only if somehow null
3834
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 33 taken 12 times.
12 if (frame->properties == nullptr)
3835 frame->properties = new AVSMap();
3836 // only 1 frame in list -> no delete
3837
1/2
✓ Branch 33 → 34 taken 12 times.
✗ Branch 33 → 35 not taken.
12 if (videoFrameListSize <= 1)
3838 {
3839 _RPT1(0, "ScriptEnvironment::GetNewFrame returning frame. VideoFrameListSize was 1\n", videoFrameListSize);
3840 #ifdef _DEBUG
3841 it3->timestamp = std::chrono::high_resolution_clock::now(); // refresh timestamp!
3842 #endif
3843 12 return frame; // return immediately
3844 }
3845 // more than X: just registered the frame found, and erase all other frames from list plus delete frame objects also
3846 frame_found = frame;
3847 found = true;
3848 ++it3;
3849 }
3850 else {
3851 // if the first frame to this vfb was already found, then we free all others and delete it from the list
3852 // Benefit: no 4-5k frame list count per a single vfb.
3853 //_RPT4(0, "ScriptEnvironment::GetNewFrame Delete one frame %p RowSize=%d Height=%d Pitch=%d Offset=%d\n", frame, frame->GetRowSize(), frame->GetHeight(), frame->GetPitch(), frame->GetOffset());
3854 delete frame;
3855 ++it3;
3856 }
3857 } // for it3
3858 if (found)
3859 {
3860 _RPT1(0, "ScriptEnvironment::GetNewFrame returning frame_found. clearing frames. List count: it2->second.size(): %7zu \n", it2.second.size());
3861 it2.second.clear();
3862 it2.second.reserve(16); // initial capacity set to 16, avoid reallocation when 1st, 2nd, etc.. elements pushed later (possible speedup)
3863 it2.second.push_back(DebugTimestampedFrame(frame_found)); // keep only the first
3864 #ifdef _DEBUG
3865 //ListFrameRegistry(vfb_size, vfb_size, true); // for chasing stuck frames
3866 //ListFrameRegistry(0, vfb_size, true); // for chasing stuck frames
3867 #endif
3868 return frame_found;
3869 }
3870 }
3871 } // for it2
3872 } // for it
3873 _RPT3(0, "ScriptEnvironment::GetNewFrame, no free entry in FrameRegistry. Requested vfb size=%zu memused=%" PRIu64 " memmax=%" PRIu64 "\n", vfb_size, device->memory_used.load(), device->memory_max);
3874
3875 #ifdef _DEBUG
3876 //ListFrameRegistry(vfb_size, vfb_size, true); // for chasing stuck frames
3877 //ListFrameRegistry(0, vfb_size, true); // for chasing stuck frames
3878 #endif
3879
3880 891 return NULL;
3881 }
3882 #else
3883
3884 // A newer method which returns the most recently freed vfb
3885 VideoFrame* ScriptEnvironment::GetFrameFromRegistry(size_t vfb_size, Device* device)
3886 {
3887 #ifdef _DEBUG
3888 std::chrono::time_point<std::chrono::high_resolution_clock> t_start, t_end;
3889 t_start = std::chrono::high_resolution_clock::now();
3890 #endif
3891
3892 for (FrameRegistryType2::iterator it = FrameRegistry2.lower_bound(vfb_size), end_it = FrameRegistry2.upper_bound(vfb_size * 3 / 2);
3893 it != end_it;
3894 ++it)
3895 {
3896 // Find VFB with _highest_ last_freed_timestamp (most recently freed)
3897 VFBStorage* best_vfb = nullptr;
3898 int64_t best_timestamp = -1;
3899 FrameBufferRegistryType::iterator best_it2 = it->second.end();
3900
3901 // FIXME: size of frame registry? - Here we do always full scan.
3902 // Unlike the old method which returns first free.
3903 for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
3904 {
3905 VFBStorage* vfb = static_cast<VFBStorage*>(it2->first);
3906 if (device == vfb->device && 0 == vfb->refcount)
3907 {
3908 int64_t timestamp = vfb->last_freed_timestamp.load();
3909 if (timestamp > best_timestamp)
3910 {
3911 best_timestamp = timestamp;
3912 best_vfb = vfb;
3913 best_it2 = it2;
3914 }
3915 }
3916
3917 }
3918
3919 if (best_vfb != nullptr)
3920 {
3921 auto& it2 = best_it2;
3922 VFBStorage* vfb = best_vfb;
3923
3924 // Process the most recently freed VFB (LRU strategy for better cache locality)
3925 // This new version is likely better because it prefers recently-used memory
3926 // (better cache performance), while the old version just grabs the first available match.
3927 size_t videoFrameListSize = it2->second.size();
3928 VideoFrame* frame_found = nullptr;
3929 bool found = false;
3930
3931 for (VideoFrameArrayType::iterator it3 = it2->second.begin(), end_it3 = it2->second.end();
3932 it3 != end_it3;
3933 /* ++it3 not here, because of the delete */)
3934 {
3935 VideoFrame* frame = it3->frame;
3936 assert(0 == frame->refcount);
3937
3938 // Clear content now to promptly free any large strings/arrays.
3939 if (frame->properties != nullptr)
3940 frame->properties->clear();
3941
3942 if (!found)
3943 {
3944 InterlockedIncrement(&(frame->vfb->refcount));
3945 vfb->free_count = 0;
3946 vfb->Attach(threadEnv->GetCurrentGraphNode());
3947
3948 #ifdef _DEBUG
3949 char buf[256];
3950 t_end = std::chrono::high_resolution_clock::now();
3951 std::chrono::duration<double> elapsed_seconds = t_end - t_start;
3952 snprintf(buf, 255, "ScriptEnvironment::GetNewFrame NEW METHOD EXACT hit! VideoFrameListSize=%7zu GotSize=%7zu FrReg.Size=%6zu vfb=%p frame=%p timestamp=%" PRId64 " SeqNum=%d SeekTime:%f\n", videoFrameListSize, vfb_size, FrameRegistry2.size(), vfb, frame, vfb->last_freed_timestamp.load(), vfb->GetSequenceNumber(), elapsed_seconds.count());
3953 _RPT0(0, buf);
3954 #endif
3955
3956 // properties was cleared above; create the AVSMap shell only if somehow null
3957 if (frame->properties == nullptr)
3958 frame->properties = new AVSMap();
3959 frame_found = frame;
3960 found = true;
3961
3962 #ifdef _DEBUG
3963 it3->timestamp = std::chrono::high_resolution_clock::now();
3964 #endif
3965 ++it3;
3966 }
3967 else {
3968 delete frame; // ~VideoFrame()/DESTRUCTOR() deletes frame->properties
3969 ++it3;
3970 }
3971 } // for it3
3972
3973 if (found)
3974 {
3975 _RPT2(0, "ScriptEnvironment::GetNewFrame returning frame_found. clearing frames. List count: %7zu timestamp=%" PRId64 " SeqNum=%d\n", it2->second.size(), vfb->last_freed_timestamp.load(), vfb->GetSequenceNumber());
3976 it2->second.clear();
3977 it2->second.reserve(16);
3978 it2->second.push_back(DebugTimestampedFrame(frame_found));
3979
3980 #ifdef _DEBUG
3981 ListFrameRegistry(vfb_size, vfb_size, true);
3982 ListFrameRegistry(0, vfb_size, true);
3983 #endif
3984 return frame_found;
3985 }
3986 }
3987 } // for it
3988
3989 _RPT3(0, "ScriptEnvironment::GetNewFrame, no free entry in FrameRegistry. Requested vfb size=%zu memused=%" PRIu64 " memmax=%" PRIu64 "\n", vfb_size, device->memory_used.load(), device->memory_max);
3990 #ifdef _DEBUG
3991 ListFrameRegistry(vfb_size, vfb_size, true);
3992 ListFrameRegistry(0, vfb_size, true);
3993 #endif
3994 return NULL;
3995 }
3996 #endif
3997
3998 903 VideoFrame* ScriptEnvironment::GetNewFrame(size_t vfb_size, size_t margin, Device* device)
3999 {
4000
1/2
✓ Branch 2 → 3 taken 903 times.
✗ Branch 2 → 104 not taken.
903 std::unique_lock<std::recursive_mutex> env_lock(memory_mutex);
4001
4002 // prevent fragmentation of vfb buffer list many different small-sized vfb's
4003
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 903 times.
903 if (vfb_size < 64) vfb_size = 64;
4004
2/2
✓ Branch 5 → 6 taken 153 times.
✓ Branch 5 → 7 taken 750 times.
903 else if (vfb_size < 256) vfb_size = 256;
4005
2/2
✓ Branch 7 → 8 taken 189 times.
✓ Branch 7 → 9 taken 561 times.
750 else if (vfb_size < 512) vfb_size = 512;
4006
2/2
✓ Branch 9 → 10 taken 480 times.
✓ Branch 9 → 11 taken 81 times.
561 else if (vfb_size < 1024) vfb_size = 1024;
4007
2/2
✓ Branch 11 → 12 taken 72 times.
✓ Branch 11 → 13 taken 9 times.
81 else if (vfb_size < 2048) vfb_size = 2048;
4008
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 8 times.
9 else if (vfb_size < 4096) vfb_size = 4096;
4009
4010 /* -----------------------------------------------------------
4011 * Try to return an unused but already allocated instance
4012 * -----------------------------------------------------------
4013 */
4014
1/2
✓ Branch 15 → 16 taken 903 times.
✗ Branch 15 → 102 not taken.
903 VideoFrame* frame = GetFrameFromRegistry(vfb_size, device);
4015
2/2
✓ Branch 16 → 17 taken 12 times.
✓ Branch 16 → 18 taken 891 times.
903 if (frame != NULL)
4016 12 return frame;
4017
4018 /* -----------------------------------------------------------
4019 * No unused instance was found, try to allocate a new one
4020 * -----------------------------------------------------------
4021 */
4022 // We reserve 15% for unaccounted stuff
4023
1/2
✓ Branch 19 → 20 taken 891 times.
✗ Branch 19 → 22 not taken.
891 if (device->memory_used + vfb_size < device->memory_max * 0.85f) {
4024
1/2
✓ Branch 20 → 21 taken 891 times.
✗ Branch 20 → 102 not taken.
891 frame = AllocateFrame(vfb_size, margin, device);
4025 }
4026
1/2
✓ Branch 22 → 23 taken 891 times.
✗ Branch 22 → 24 not taken.
891 if (frame != NULL)
4027 891 return frame;
4028
4029 #ifdef _DEBUG
4030 // #define LIST_CACHES
4031 // list all cache_entries
4032 #ifdef LIST_CACHES
4033 int cache_counter = 0;
4034 for (auto &cit: CacheRegistry)
4035 {
4036 cache_counter++;
4037 AvsCache* cache = cit;
4038 int cache_size = cache->SetCacheHints(CACHE_GET_SIZE, 0);
4039 _RPT4(0, " cache#%d cache_ptr=%p cache_size=%d \n", cache_counter, (void*)cache, cache_size); // let's see what's in the cache
4040 }
4041 #endif
4042 #endif
4043
4044 /* -----------------------------------------------------------
4045 * Couldn't allocate, shrink cache and get more unused frames
4046 * -----------------------------------------------------------
4047 */
4048 ShrinkCache(device);
4049
4050 /* -----------------------------------------------------------
4051 * Try to return an unused frame again
4052 * -----------------------------------------------------------
4053 */
4054 frame = GetFrameFromRegistry(vfb_size, device);
4055 if (frame != NULL)
4056 return frame;
4057
4058 /* -----------------------------------------------------------
4059 * Try to allocate again
4060 * -----------------------------------------------------------
4061 */
4062 frame = AllocateFrame(vfb_size, margin, device);
4063 if (frame != NULL)
4064 return frame;
4065
4066 OneTimeLogTicket ticket(LOGTICKET_W1100);
4067 LogMsgOnce(ticket, LOGLEVEL_WARNING, "Memory reallocation occurs. This will probably degrade performance. You can try increasing the limit using SetMemoryMax().");
4068
4069 /* -----------------------------------------------------------
4070 * No frame found, free all the unused frames!!!
4071 * -----------------------------------------------------------
4072 */
4073 _RPT1(0, "Allocate failed. GC start memory_used=%" PRIu64 "\n", device->memory_used.load());
4074 // unfortunately if we reach here, only 0 or 1 vfbs or frames can be freed, from lower vfb sizes
4075 // usually it's not enough
4076 // yet it is true that it's meaningful only to free up smaller vfb sizes here
4077 for (FrameRegistryType2::iterator it = FrameRegistry2.begin(), end_it = FrameRegistry2.upper_bound(vfb_size);
4078 it != end_it;
4079 ++it)
4080 {
4081 for (FrameBufferRegistryType::iterator it2 = (it->second).begin(), end_it2 = (it->second).end();
4082 it2 != end_it2;
4083 /*++it2: not here: may delete iterator position */)
4084 {
4085 VFBStorage *vfb = static_cast<VFBStorage*>(it2->first);
4086 if (device == vfb->device && 0 == vfb->refcount) // vfb refcount check
4087 {
4088 vfb->device->memory_used -= vfb->GetDataSize(); // frame->vfb->GetDataSize();
4089 delete vfb;
4090 for (auto &it3: it2->second)
4091 {
4092 VideoFrame *currentframe = it3.frame;
4093 assert(0 == currentframe->refcount);
4094 delete currentframe;
4095 }
4096 // delete array belonging to this vfb in one step
4097 it2->second.clear(); // clear frame list
4098 it2 = (it->second).erase(it2); // clear current vfb
4099 }
4100 else ++it2;
4101 }
4102 }
4103 _RPT1(0, "End of garbage collection A memused=%" PRIu64 "\n", device->memory_used.load());
4104 #if 0
4105 static int counter = 0;
4106 char buf[200]; sprintf(buf, "Re allocation %d\r\n", counter++);
4107 OutputDebugStringA(buf);
4108 #endif
4109 /* -----------------------------------------------------------
4110 * Try to allocate again
4111 * -----------------------------------------------------------
4112 */
4113 frame = AllocateFrame(vfb_size, margin, device);
4114 if ( frame != NULL)
4115 return frame;
4116
4117
4118 /* -----------------------------------------------------------
4119 * Oh boy...
4120 * -----------------------------------------------------------
4121 */
4122
4123 // See if we could benefit from 64-bit Avisynth
4124 if (sizeof(void*) == 4 && device == Devices->GetCPUDevice())
4125 {
4126 #ifdef AVS_WINDOWS
4127 // Get system memory information
4128 MEMORYSTATUSEX memstatus;
4129 memstatus.dwLength = sizeof(memstatus);
4130 GlobalMemoryStatusEx(&memstatus);
4131
4132 BOOL using_wow64;
4133 if (IsWow64Process(GetCurrentProcess(), &using_wow64) // if running 32-bits on a 64-bit OS
4134 && (memstatus.ullAvailPhys > 1024ull * 1024 * 1024)) // if at least 1GB of system memory is still free
4135 {
4136 OneTimeLogTicket ticket(LOGTICKET_W1007);
4137 LogMsgOnce(ticket, LOGLEVEL_INFO, "We have run out of memory, but your system still has some free RAM left. You might benefit from a 64-bit build of Avisynth+.");
4138 }
4139 #endif
4140 }
4141
4142 ThrowError("Could not allocate video frame. Out of memory. memory_max = %" PRIu64 ", memory_used = %" PRIu64 " Request=%zu", device->memory_max, device->memory_used.load(), vfb_size);
4143 return NULL;
4144 903 }
4145
4146 void ScriptEnvironment::ShrinkCache(Device *device)
4147 {
4148 /* -----------------------------------------------------------
4149 * Shrink cache to keep memory limit
4150 * -----------------------------------------------------------
4151 */
4152 int shrinkcount = 0;
4153
4154 for (auto &cit: CacheRegistry)
4155 {
4156 // Oh darn. We'd need more memory than we are allowed to use.
4157 // Let's reduce the amount of caching.
4158
4159 // We try to shrink least recently used caches first.
4160
4161 AvsCache* cache = cit;
4162 if (cache->GetDevice() != device) {
4163 continue;
4164 }
4165 int cache_size = cache->SetCacheHints(CACHE_GET_SIZE, 0);
4166 if (cache_size != 0)
4167 {
4168 _RPT2(0, "ScriptEnvironment::EnsureMemoryLimit shrink cache. cache=%p new size=%d\n", (void*)cache, cache_size - 1);
4169 cache->SetCacheHints(CACHE_SET_MAX_CAPACITY, cache_size - 1);
4170 shrinkcount++;
4171 } // if
4172 } // for cit
4173
4174 if (shrinkcount != 0)
4175 {
4176 OneTimeLogTicket ticket(LOGTICKET_W1003);
4177 LogMsgOnce(ticket, LOGLEVEL_WARNING, "Caches have been shrunk due to low memory limit. This will probably degrade performance. You can try increasing the limit using SetMemoryMax().");
4178 }
4179
4180 /* -----------------------------------------------------------
4181 * Count up free_count and free if it exceeds the threshold
4182 * -----------------------------------------------------------
4183 */
4184 // Free up in one pass in FrameRegistry2
4185 if (shrinkcount)
4186 {
4187 _RPT1(0, "EnsureMemoryLimit GC start: memused=%" PRIu64 "\n", device->memory_used.load());
4188 [[maybe_unused]] int freed_vfb_count = 0;
4189 [[maybe_unused]] int freed_frame_count = 0;
4190 [[maybe_unused]] int unfreed_frame_count = 0;
4191 for (auto &it: FrameRegistry2)
4192 {
4193 for (FrameBufferRegistryType::iterator it2 = (it.second).begin(), end_it2 = (it.second).end();
4194 it2 != end_it2;
4195 /*++it2: not here: may delete iterator position */)
4196 {
4197 VFBStorage *vfb = static_cast<VFBStorage*>(it2->first);
4198 // vfb device and refcount check and free count exceeds the threshold
4199 if (device == vfb->device && 0 == vfb->refcount && vfb->free_count++ >= device->free_thresh)
4200 {
4201 #if 0
4202 static int counter = 0;
4203 char buf[200]; sprintf(buf, "Free frame !!! %d\r\n", counter++);
4204 OutputDebugStringA(buf);
4205 #endif
4206 device->memory_used -= vfb->GetDataSize();
4207 #ifdef _DEBUG
4208 VFBStorage *_vfb = vfb;
4209 #endif
4210 delete vfb;
4211 ++freed_vfb_count;
4212 for (auto &it3: it2->second)
4213 {
4214 VideoFrame *frame = it3.frame;
4215 assert(0 == frame->refcount);
4216 if (0 == frame->refcount)
4217 {
4218 delete frame;
4219 ++freed_frame_count;
4220 }
4221 else {
4222 // there should not be such case: vfb.refcount=0 and frame.refcount!=0
4223 ++unfreed_frame_count;
4224 #ifdef _DEBUG
4225 _RPT3(0, " ?????? frame refcount error!!! _vfb=%p frame=%p framerefcount=%d \n", _vfb, frame, frame->refcount);
4226 #endif
4227 }
4228 }
4229 // delete array belonging to this vfb in one step
4230 it2->second.clear(); // clear frame list
4231 it2 = (it.second).erase(it2); // clear vfb entry
4232 }
4233 else ++it2;
4234 }
4235 }
4236 _RPT4(0, "End of garbage collection B: freed_vfb=%d frame=%d unfreed=%d memused=%" PRIu64 "\n", freed_vfb_count, freed_frame_count, unfreed_frame_count, device->memory_used.load());
4237 }
4238 }
4239
4240 // no alpha
4241 PVideoFrame ScriptEnvironment::NewPlanarVideoFrame(int row_size, int height, int row_sizeUV, int heightUV, int align, bool U_first, int pixel_type, Device* device)
4242 {
4243 return NewPlanarVideoFrame(row_size, height, row_sizeUV, heightUV, align, U_first, false /* no alpha */, pixel_type, device);
4244 }
4245
4246 // with alpha support
4247 672 PVideoFrame ScriptEnvironment::NewPlanarVideoFrame(int row_size, int height, int row_sizeUV, int heightUV, int align, bool U_first, bool alpha, int pixel_type, Device* device)
4248 {
4249
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 7 taken 672 times.
672 if (align < 0)
4250 {
4251 align = -align;
4252 OneTimeLogTicket ticket(LOGTICKET_W1009);
4253 LogMsgOnce(ticket, LOGLEVEL_WARNING, "A filter is using forced frame alignment, a feature that is deprecated and disabled. The filter will likely behave erroneously.");
4254 }
4255 672 align = max(align, frame_align);
4256
4257 int pitchUV;
4258 672 const int pitchY = AlignNumber(row_size, align);
4259
1/6
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 taken 672 times.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 13 not taken.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 13 not taken.
672 if (!PlanarChromaAlignmentState && (row_size == row_sizeUV*2) && (height == heightUV*2)) { // Meet old 2.5 series API expectations for YV12
4260 // Legacy alignment - pack Y as specified, pack UV half that
4261 pitchUV = (pitchY+1)>>1; // UV plane, width = 1/2 byte per pixel - don't align UV planes seperately.
4262 }
4263 else {
4264 // Align planes separately
4265 672 pitchUV = AlignNumber(row_sizeUV, align);
4266 }
4267
4268 672 size_t sizeY = AlignNumber(pitchY * height, plane_align);
4269 672 size_t sizeUV = AlignNumber(pitchUV * heightUV, plane_align);
4270
2/2
✓ Branch 16 → 17 taken 116 times.
✓ Branch 16 → 18 taken 556 times.
672 size_t size = sizeY + 2 * sizeUV + (alpha ? sizeY : 0);
4271
4272 672 VideoFrame *res = GetNewFrame(size, align - 1, device);
4273
4274 int offsetU, offsetV, offsetA;
4275 672 const int offsetY = (int)(AlignPointer(res->vfb->GetWritePtr(), align) - res->vfb->GetWritePtr()); // first line offset for proper alignment
4276
2/2
✓ Branch 23 → 24 taken 91 times.
✓ Branch 23 → 27 taken 581 times.
672 if (U_first) {
4277 91 offsetU = offsetY + (int)sizeY;
4278 91 offsetV = offsetY + (int)sizeY + (int)sizeUV;
4279
2/2
✓ Branch 24 → 25 taken 20 times.
✓ Branch 24 → 26 taken 71 times.
91 offsetA = alpha ? offsetV + (int)sizeUV : 0;
4280 } else {
4281 581 offsetV = offsetY + (int)sizeY;
4282 581 offsetU = offsetY + (int)sizeY + (int)sizeUV;
4283
2/2
✓ Branch 27 → 28 taken 96 times.
✓ Branch 27 → 29 taken 485 times.
581 offsetA = alpha ? offsetU + (int)sizeUV : 0;
4284 }
4285
4286 672 res->offset = offsetY;
4287 672 res->pitch = pitchY;
4288 672 res->row_size = row_size;
4289 672 res->height = height;
4290
4291 672 res->offsetU = offsetU;
4292 672 res->offsetV = offsetV;
4293 672 res->pitchUV = pitchUV;
4294 672 res->row_sizeUV = row_sizeUV;
4295 672 res->heightUV = heightUV;
4296
4297 // alpha support
4298 672 res->offsetA = offsetA;
4299
2/2
✓ Branch 30 → 31 taken 116 times.
✓ Branch 30 → 32 taken 556 times.
672 res->row_sizeA = alpha ? row_size : 0;
4300
2/2
✓ Branch 33 → 34 taken 116 times.
✓ Branch 33 → 35 taken 556 times.
672 res->pitchA = alpha ? pitchY : 0;
4301
4302 672 res->pixel_type = pixel_type;
4303
4304 672 return PVideoFrame(res);
4305 }
4306
4307 // Variant #1.no frame property source
4308 231 PVideoFrame ScriptEnvironment::NewVideoFrameOnDevice(int row_size, int height, int align, int pixel_type, Device* device)
4309 {
4310
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 7 taken 231 times.
231 if (align < 0)
4311 {
4312 align = -align;
4313 OneTimeLogTicket ticket(LOGTICKET_W1009);
4314 this->LogMsgOnce(ticket, LOGLEVEL_WARNING, "A filter is using forced frame alignment, a feature that is deprecated and disabled. The filter will likely behave erroneously.");
4315 }
4316 231 align = max(align, frame_align);
4317
4318 231 const int pitch = AlignNumber(row_size, align);
4319 231 size_t size = pitch * height;
4320
4321 231 VideoFrame *res = GetNewFrame(size, align - 1, device);
4322
4323 231 const int offset = (int)(AlignPointer(res->vfb->GetWritePtr(), align) - res->vfb->GetWritePtr()); // first line offset for proper alignment
4324
4325 231 res->offset = offset;
4326 231 res->pitch = pitch;
4327 231 res->row_size = row_size;
4328 231 res->height = height;
4329
4330 231 res->offsetU = offset;
4331 231 res->offsetV = offset;
4332 231 res->pitchUV = 0;
4333 231 res->row_sizeUV = 0;
4334 231 res->heightUV = 0;
4335
4336 // alpha support
4337 231 res->offsetA = 0;
4338 231 res->row_sizeA = 0;
4339 231 res->pitchA = 0;
4340
4341 231 res->pixel_type = pixel_type;
4342
4343 231 return PVideoFrame(res);
4344 }
4345
4346 // Variant #1. with frame property source
4347 PVideoFrame ScriptEnvironment::NewVideoFrameOnDevice(int row_size, int height, int align, int pixel_type, Device* device, const PVideoFrame* prop_src)
4348 {
4349 PVideoFrame result = NewVideoFrameOnDevice(row_size, height, align, pixel_type, device);
4350
4351 if (prop_src)
4352 copyFrameProps(*prop_src, result);
4353
4354 return result;
4355 }
4356
4357 // Variant #2. without frame property source
4358 PVideoFrame ScriptEnvironment::NewVideoFrame(const VideoInfo& vi, const PDevice& device) {
4359 return NewVideoFrameOnDevice(vi, frame_align, (Device*)(void*)device);
4360 }
4361
4362 // Variant #2. with frame property source
4363 PVideoFrame ScriptEnvironment::NewVideoFrame(const VideoInfo& vi, const PDevice& device, const PVideoFrame* prop_src)
4364 {
4365 PVideoFrame result = NewVideoFrame(vi, device);
4366
4367 if (prop_src)
4368 copyFrameProps(*prop_src, result);
4369
4370 return result;
4371 }
4372
4373 // Variant #3. without frame property source
4374 792 PVideoFrame ScriptEnvironment::NewVideoFrameOnDevice(const VideoInfo& vi, int align, Device* device) {
4375 // todo: high bit-depth: we have too many types now. Do we need really check?
4376 // Check requested pixel_type:
4377
1/2
✓ Branch 2 → 3 taken 792 times.
✗ Branch 2 → 4 not taken.
792 switch (vi.pixel_type) {
4378 792 case VideoInfo::CS_BGR24:
4379 case VideoInfo::CS_BGR32:
4380 case VideoInfo::CS_YUY2:
4381 case VideoInfo::CS_Y8:
4382 case VideoInfo::CS_YV12:
4383 case VideoInfo::CS_YV16:
4384 case VideoInfo::CS_YV24:
4385 case VideoInfo::CS_YV411:
4386 case VideoInfo::CS_I420:
4387 // AVS16 do not reject when a filter requests it
4388 // planar YUV 10-32 bit
4389 case VideoInfo::CS_YUV420P10:
4390 case VideoInfo::CS_YUV422P10:
4391 case VideoInfo::CS_YUV444P10:
4392 case VideoInfo::CS_Y10:
4393 case VideoInfo::CS_YUV420P12:
4394 case VideoInfo::CS_YUV422P12:
4395 case VideoInfo::CS_YUV444P12:
4396 case VideoInfo::CS_Y12:
4397 case VideoInfo::CS_YUV420P14:
4398 case VideoInfo::CS_YUV422P14:
4399 case VideoInfo::CS_YUV444P14:
4400 case VideoInfo::CS_Y14:
4401 case VideoInfo::CS_YUV420P16:
4402 case VideoInfo::CS_YUV422P16:
4403 case VideoInfo::CS_YUV444P16:
4404 case VideoInfo::CS_Y16:
4405 case VideoInfo::CS_YUV420PS:
4406 case VideoInfo::CS_YUV422PS:
4407 case VideoInfo::CS_YUV444PS:
4408 case VideoInfo::CS_Y32:
4409 // 16 bit/sample packed RGB
4410 case VideoInfo::CS_BGR48:
4411 case VideoInfo::CS_BGR64:
4412 // planar RGB
4413 case VideoInfo::CS_RGBP:
4414 case VideoInfo::CS_RGBP10:
4415 case VideoInfo::CS_RGBP12:
4416 case VideoInfo::CS_RGBP14:
4417 case VideoInfo::CS_RGBP16:
4418 case VideoInfo::CS_RGBPS:
4419 // planar RGBA
4420 case VideoInfo::CS_RGBAP:
4421 case VideoInfo::CS_RGBAP10:
4422 case VideoInfo::CS_RGBAP12:
4423 case VideoInfo::CS_RGBAP14:
4424 case VideoInfo::CS_RGBAP16:
4425 case VideoInfo::CS_RGBAPS:
4426 // planar YUVA 8-32 bit
4427 case VideoInfo::CS_YUVA420:
4428 case VideoInfo::CS_YUVA422:
4429 case VideoInfo::CS_YUVA444:
4430 case VideoInfo::CS_YUVA420P10:
4431 case VideoInfo::CS_YUVA422P10:
4432 case VideoInfo::CS_YUVA444P10:
4433 case VideoInfo::CS_YUVA420P12:
4434 case VideoInfo::CS_YUVA422P12:
4435 case VideoInfo::CS_YUVA444P12:
4436 case VideoInfo::CS_YUVA420P14:
4437 case VideoInfo::CS_YUVA422P14:
4438 case VideoInfo::CS_YUVA444P14:
4439 case VideoInfo::CS_YUVA420P16:
4440 case VideoInfo::CS_YUVA422P16:
4441 case VideoInfo::CS_YUVA444P16:
4442 case VideoInfo::CS_YUVA420PS:
4443 case VideoInfo::CS_YUVA422PS:
4444 case VideoInfo::CS_YUVA444PS:
4445 792 break;
4446 default:
4447 ThrowError("Filter Error: Filter attempted to create VideoFrame with invalid pixel_type.");
4448 }
4449
4450 792 PVideoFrame retval;
4451
4452
8/10
✓ Branch 6 → 7 taken 792 times.
✗ Branch 6 → 66 not taken.
✓ Branch 7 → 8 taken 690 times.
✓ Branch 7 → 11 taken 102 times.
✓ Branch 8 → 9 taken 690 times.
✗ Branch 8 → 66 not taken.
✓ Branch 9 → 10 taken 580 times.
✓ Branch 9 → 11 taken 110 times.
✓ Branch 12 → 13 taken 580 times.
✓ Branch 12 → 43 taken 212 times.
792 if (vi.IsPlanar() && (vi.NumComponents() > 1)) {
4453
8/10
✓ Branch 13 → 14 taken 580 times.
✗ Branch 13 → 66 not taken.
✓ Branch 14 → 15 taken 172 times.
✓ Branch 14 → 17 taken 408 times.
✓ Branch 15 → 16 taken 172 times.
✗ Branch 15 → 66 not taken.
✓ Branch 16 → 17 taken 81 times.
✓ Branch 16 → 18 taken 91 times.
✓ Branch 19 → 20 taken 489 times.
✓ Branch 19 → 35 taken 91 times.
580 if (vi.IsYUV() || vi.IsYUVA()) {
4454 // Planar requires different math ;)
4455
1/2
✓ Branch 20 → 21 taken 489 times.
✗ Branch 20 → 66 not taken.
489 const int xmod = 1 << vi.GetPlaneWidthSubsampling(PLANAR_U);
4456 489 const int xmask = xmod - 1;
4457
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 489 times.
489 if (vi.width & xmask)
4458 ThrowError("Filter Error: Attempted to request a planar frame that wasn't mod%d in width!", xmod);
4459
4460
1/2
✓ Branch 23 → 24 taken 489 times.
✗ Branch 23 → 66 not taken.
489 const int ymod = 1 << vi.GetPlaneHeightSubsampling(PLANAR_U);
4461 489 const int ymask = ymod - 1;
4462
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 489 times.
489 if (vi.height & ymask)
4463 ThrowError("Filter Error: Attempted to request a planar frame that wasn't mod%d in height!", ymod);
4464
4465
1/2
✓ Branch 26 → 27 taken 489 times.
✗ Branch 26 → 66 not taken.
489 const int heightUV = vi.height >> vi.GetPlaneHeightSubsampling(PLANAR_U);
4466
4467
6/12
✓ Branch 27 → 28 taken 489 times.
✗ Branch 27 → 59 not taken.
✓ Branch 28 → 29 taken 489 times.
✗ Branch 28 → 59 not taken.
✓ Branch 29 → 30 taken 489 times.
✗ Branch 29 → 59 not taken.
✓ Branch 30 → 31 taken 489 times.
✗ Branch 30 → 59 not taken.
✓ Branch 31 → 32 taken 489 times.
✗ Branch 31 → 59 not taken.
✓ Branch 32 → 33 taken 489 times.
✗ Branch 32 → 57 not taken.
489 retval = NewPlanarVideoFrame(vi.RowSize(PLANAR_Y), vi.height, vi.RowSize(PLANAR_U), heightUV, align, !vi.IsVPlaneFirst(), vi.IsYUVA(), vi.pixel_type, device);
4468 } else {
4469 // plane order: G,B,R
4470
6/12
✓ Branch 35 → 36 taken 91 times.
✗ Branch 35 → 62 not taken.
✓ Branch 36 → 37 taken 91 times.
✗ Branch 36 → 62 not taken.
✓ Branch 37 → 38 taken 91 times.
✗ Branch 37 → 62 not taken.
✓ Branch 38 → 39 taken 91 times.
✗ Branch 38 → 62 not taken.
✓ Branch 39 → 40 taken 91 times.
✗ Branch 39 → 62 not taken.
✓ Branch 40 → 41 taken 91 times.
✗ Branch 40 → 60 not taken.
91 retval = NewPlanarVideoFrame(vi.RowSize(PLANAR_G), vi.height, vi.RowSize(PLANAR_G), vi.height, align, !vi.IsVPlaneFirst(), vi.IsPlanarRGBA(), vi.pixel_type, device);
4471 }
4472 }
4473 else {
4474
5/8
✓ Branch 43 → 44 taken 95 times.
✓ Branch 43 → 47 taken 117 times.
✓ Branch 44 → 45 taken 95 times.
✗ Branch 44 → 66 not taken.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 47 taken 95 times.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 212 times.
212 if ((vi.width&1)&&(vi.IsYUY2()))
4475 ThrowError("Filter Error: Attempted to request an YUY2 frame that wasn't mod2 in width.");
4476
4477
3/6
✓ Branch 50 → 51 taken 212 times.
✗ Branch 50 → 65 not taken.
✓ Branch 51 → 52 taken 212 times.
✗ Branch 51 → 65 not taken.
✓ Branch 52 → 53 taken 212 times.
✗ Branch 52 → 63 not taken.
212 retval= NewVideoFrameOnDevice(vi.RowSize(), vi.height, align, vi.pixel_type, device);
4478 }
4479
4480 792 return retval;
4481 }
4482
4483
4484 // Variant #3. with frame property source
4485 163 PVideoFrame ScriptEnvironment::NewVideoFrameOnDevice(const VideoInfo& vi, int align, Device* device, const PVideoFrame* prop_src)
4486 {
4487 163 PVideoFrame result = NewVideoFrameOnDevice(vi, align, device);
4488
4489
1/2
✓ Branch 3 → 4 taken 163 times.
✗ Branch 3 → 5 not taken.
163 if (prop_src)
4490
1/2
✓ Branch 4 → 5 taken 163 times.
✗ Branch 4 → 7 not taken.
163 copyFrameProps(*prop_src, result);
4491
4492 163 return result;
4493 }
4494
4495
4496 117 bool ScriptEnvironment::MakeWritable(PVideoFrame* pvf) {
4497 117 const PVideoFrame& vf = *pvf;
4498
4499 // If the frame is already writable, do nothing.
4500
3/4
✓ Branch 3 → 4 taken 117 times.
✗ Branch 3 → 94 not taken.
✓ Branch 4 → 5 taken 6 times.
✓ Branch 4 → 6 taken 111 times.
117 if (vf->IsWritable())
4501 6 return false;
4502
4503 // Otherwise, allocate a new frame (using NewVideoFrame) and
4504 // copy the data into it. Then modify the passed PVideoFrame
4505 // to point to the new buffer.
4506
1/2
✓ Branch 7 → 8 taken 111 times.
✗ Branch 7 → 94 not taken.
111 Device* device = vf->GetFrameBuffer()->device;
4507
1/2
✓ Branch 8 → 9 taken 111 times.
✗ Branch 8 → 94 not taken.
111 PVideoFrame dst;
4508
4509 #ifdef ENABLE_CUDA
4510 if (device->device_type == DEV_TYPE_CUDA) {
4511 // copy whole frame
4512 dst = GetOnDeviceFrame(vf, device);
4513 CopyCUDAFrame(dst, vf, threadEnv.get());
4514 }
4515 else
4516 #endif
4517 {
4518
1/2
✓ Branch 10 → 11 taken 111 times.
✗ Branch 10 → 92 not taken.
111 const int row_size = vf->GetRowSize();
4519
1/2
✓ Branch 12 → 13 taken 111 times.
✗ Branch 12 → 92 not taken.
111 const int height = vf->GetHeight();
4520
4521
1/2
✓ Branch 14 → 15 taken 111 times.
✗ Branch 14 → 92 not taken.
111 bool alpha = 0 != vf->GetPitch(PLANAR_A);
4522
3/4
✓ Branch 16 → 17 taken 111 times.
✗ Branch 16 → 92 not taken.
✓ Branch 17 → 18 taken 92 times.
✓ Branch 17 → 27 taken 19 times.
111 if (vf->GetPitch(PLANAR_U)) { // we have no videoinfo, so we assume that it is Planar if it has a U plane.
4523
1/2
✓ Branch 19 → 20 taken 92 times.
✗ Branch 19 → 92 not taken.
92 const int row_sizeUV = vf->GetRowSize(PLANAR_U); // for Planar RGB this returns row_sizeUV which is the same for all planes
4524
1/2
✓ Branch 21 → 22 taken 92 times.
✗ Branch 21 → 92 not taken.
92 const int heightUV = vf->GetHeight(PLANAR_U);
4525
2/4
✓ Branch 23 → 24 taken 92 times.
✗ Branch 23 → 88 not taken.
✓ Branch 24 → 25 taken 92 times.
✗ Branch 24 → 86 not taken.
92 dst = NewPlanarVideoFrame(row_size, height, row_sizeUV, heightUV, frame_align, false /* always V first on internal images */, alpha, vf->pixel_type, device);
4526 }
4527 else {
4528
2/4
✓ Branch 28 → 29 taken 19 times.
✗ Branch 28 → 91 not taken.
✓ Branch 29 → 30 taken 19 times.
✗ Branch 29 → 89 not taken.
19 dst = NewVideoFrameOnDevice(row_size, height, frame_align, vf->pixel_type, device);
4529 }
4530
4531
5/10
✓ Branch 33 → 34 taken 111 times.
✗ Branch 33 → 92 not taken.
✓ Branch 35 → 36 taken 111 times.
✗ Branch 35 → 92 not taken.
✓ Branch 37 → 38 taken 111 times.
✗ Branch 37 → 92 not taken.
✓ Branch 39 → 40 taken 111 times.
✗ Branch 39 → 92 not taken.
✓ Branch 40 → 41 taken 111 times.
✗ Branch 40 → 92 not taken.
111 BitBlt(dst->GetWritePtr(), dst->GetPitch(), vf->GetReadPtr(), vf->GetPitch(), row_size, height);
4532 // Blit More planes (pitch, rowsize and height should be 0, if none is present)
4533
7/14
✓ Branch 42 → 43 taken 111 times.
✗ Branch 42 → 92 not taken.
✓ Branch 44 → 45 taken 111 times.
✗ Branch 44 → 92 not taken.
✓ Branch 46 → 47 taken 111 times.
✗ Branch 46 → 92 not taken.
✓ Branch 48 → 49 taken 111 times.
✗ Branch 48 → 92 not taken.
✓ Branch 50 → 51 taken 111 times.
✗ Branch 50 → 92 not taken.
✓ Branch 52 → 53 taken 111 times.
✗ Branch 52 → 92 not taken.
✓ Branch 53 → 54 taken 111 times.
✗ Branch 53 → 92 not taken.
111 BitBlt(dst->GetWritePtr(PLANAR_V), dst->GetPitch(PLANAR_V), vf->GetReadPtr(PLANAR_V),
4534 vf->GetPitch(PLANAR_V), vf->GetRowSize(PLANAR_V), vf->GetHeight(PLANAR_V));
4535
7/14
✓ Branch 55 → 56 taken 111 times.
✗ Branch 55 → 92 not taken.
✓ Branch 57 → 58 taken 111 times.
✗ Branch 57 → 92 not taken.
✓ Branch 59 → 60 taken 111 times.
✗ Branch 59 → 92 not taken.
✓ Branch 61 → 62 taken 111 times.
✗ Branch 61 → 92 not taken.
✓ Branch 63 → 64 taken 111 times.
✗ Branch 63 → 92 not taken.
✓ Branch 65 → 66 taken 111 times.
✗ Branch 65 → 92 not taken.
✓ Branch 66 → 67 taken 111 times.
✗ Branch 66 → 92 not taken.
111 BitBlt(dst->GetWritePtr(PLANAR_U), dst->GetPitch(PLANAR_U), vf->GetReadPtr(PLANAR_U),
4536 vf->GetPitch(PLANAR_U), vf->GetRowSize(PLANAR_U), vf->GetHeight(PLANAR_U));
4537
2/2
✓ Branch 67 → 68 taken 15 times.
✓ Branch 67 → 81 taken 96 times.
111 if (alpha)
4538
7/14
✓ Branch 69 → 70 taken 15 times.
✗ Branch 69 → 92 not taken.
✓ Branch 71 → 72 taken 15 times.
✗ Branch 71 → 92 not taken.
✓ Branch 73 → 74 taken 15 times.
✗ Branch 73 → 92 not taken.
✓ Branch 75 → 76 taken 15 times.
✗ Branch 75 → 92 not taken.
✓ Branch 77 → 78 taken 15 times.
✗ Branch 77 → 92 not taken.
✓ Branch 79 → 80 taken 15 times.
✗ Branch 79 → 92 not taken.
✓ Branch 80 → 81 taken 15 times.
✗ Branch 80 → 92 not taken.
15 BitBlt(dst->GetWritePtr(PLANAR_A), dst->GetPitch(PLANAR_A), vf->GetReadPtr(PLANAR_A),
4539 vf->GetPitch(PLANAR_A), vf->GetRowSize(PLANAR_A), vf->GetHeight(PLANAR_A));
4540 }
4541
4542
1/2
✓ Branch 81 → 82 taken 111 times.
✗ Branch 81 → 92 not taken.
111 copyFrameProps(vf, dst);
4543
4544
1/2
✓ Branch 82 → 83 taken 111 times.
✗ Branch 82 → 92 not taken.
111 *pvf = dst;
4545 111 return true;
4546 111 }
4547
4548
4549 8 void ScriptEnvironment::AtExit(IScriptEnvironment::ShutdownFunc function, void* user_data) {
4550 8 at_exit.Add(function, user_data);
4551 8 }
4552
4553 // Registers a new VideoFrame (which shares an existing VFB via Subframe) into
4554 // FrameRegistry2, with eager pruning of dead entries.
4555 //
4556 // Why subframe registration is needed:
4557 // Even though the new subframe shares the source VFB, it must be registered so
4558 // that GetFrameFromRegistry cannot wrongly reassign the VFB while any subframe
4559 // still references it. The VFB refcount guards against reuse, but the registry
4560 // is the mechanism by which VideoFrame objects are tracked and eventually recycled.
4561 //
4562 // Why pruning is needed:
4563 // The naive approach (bare push_back) causes a memory leak when the source VFB
4564 // belongs to a static-frame clip such as ColorBars() or BlankClip(). Those clips
4565 // serve the same VFB for every GetFrame() call; its refcount never reaches 0.
4566 // GetFrameFromRegistry only cleans up dead VideoFrame objects when it finds
4567 // vfb->refcount == 0, so it never visits the static VFB's bucket. Each call to
4568 // Subframe/SubframePlanar/MakePropertyWritable pushes a new entry; the subframe
4569 // is used briefly, released (refcount drops to 0), but the dead VideoFrame object
4570 // is never deleted. Over a long sequence (e.g. 107000 frames) this accumulates
4571 // ~10 MB of dead VideoFrame objects.
4572 //
4573 // The fix:
4574 // Before pushing, scan the bucket and delete all entries with refcount == 0,
4575 // mirroring what GetFrameFromRegistry does during normal VFB recycling, but
4576 // eagerly, for buckets it will never visit.
4577 // For static-clip VFBs the vector stabilizes at exactly 2 entries:
4578 // [0] the original live frame (refcount >= 2, untouched)
4579 // [1] the single current subframe (refcount == 0 by next call, pruned then)
4580 // For normal (non-static) clips the VFB is recycled normally by
4581 // GetFrameFromRegistry; the prune loop finds nothing and is a no-op.
4582 //
4583 // NOTE 1: AllocateFrame is intentionally excluded. It always creates a brand-new
4584 // VFB with a fresh, empty bucket, so there are never stale entries to prune there.
4585 //
4586 // NOTE 2: Caller must hold memory_mutex before calling this function.
4587 // All four call sites (AllocateFrame excluded) acquire memory_mutex
4588 // via std::unique_lock<std::recursive_mutex> env_lock(memory_mutex)
4589 // before reaching this point.
4590 49 void ScriptEnvironment::RegisterSubFrameInRegistry(size_t vfb_size, VideoFrameBuffer* vfb, VideoFrame* new_frame)
4591 {
4592 // caller must already hold memory_mutex
4593 49 auto& vec = FrameRegistry2[vfb_size][vfb];
4594
2/2
✓ Branch 27 → 5 taken 68 times.
✓ Branch 27 → 28 taken 49 times.
234 for (auto it = vec.begin(); it != vec.end(); ) {
4595 68 VideoFrame* f = it->frame;
4596
2/2
✓ Branch 7 → 8 taken 13 times.
✓ Branch 7 → 16 taken 55 times.
68 if (f->refcount == 0) {
4597
1/2
✓ Branch 8 → 9 taken 13 times.
✗ Branch 8 → 11 not taken.
13 delete f; // ~VideoFrame()/DESTRUCTOR() deletes f->properties
4598
1/2
✓ Branch 14 → 15 taken 13 times.
✗ Branch 14 → 31 not taken.
13 it = vec.erase(it);
4599 }
4600 else {
4601 ++it;
4602 }
4603 }
4604
1/2
✓ Branch 29 → 30 taken 49 times.
✗ Branch 29 → 33 not taken.
49 vec.push_back(DebugTimestampedFrame(new_frame));
4605 #ifdef _DEBUG
4606 // ListFrameRegistry(vfb_size, vfb_size, true);
4607 #endif
4608
4609 49 }
4610
4611 14 PVideoFrame ScriptEnvironment::Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) {
4612
4613
2/4
✓ Branch 3 → 4 taken 14 times.
✗ Branch 3 → 30 not taken.
✓ Branch 4 → 5 taken 14 times.
✗ Branch 4 → 7 not taken.
14 if (src->GetFrameBuffer()->device->device_type == DEV_TYPE_CPU)
4614
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 14 times.
14 if ((new_pitch | rel_offset) & (frame_align - 1))
4615 ThrowError("Filter Error: Filter attempted to break alignment of VideoFrame.");
4616
4617
1/2
✓ Branch 8 → 9 taken 14 times.
✗ Branch 8 → 30 not taken.
14 VideoFrame* subframe = src->Subframe(rel_offset, new_pitch, new_row_size, new_height);
4618
4619
1/2
✓ Branch 10 → 11 taken 14 times.
✗ Branch 10 → 30 not taken.
14 const AVSMap &avsmap = src->getConstProperties();
4620
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 14 times.
14 if (propNumKeys(&avsmap) > 0)
4621 subframe->setProperties(avsmap);
4622
4623
2/4
✓ Branch 15 → 16 taken 14 times.
✗ Branch 15 → 30 not taken.
✓ Branch 16 → 17 taken 14 times.
✗ Branch 16 → 30 not taken.
14 size_t vfb_size = src->GetFrameBuffer()->GetDataSize();
4624
4625 // vector and maps needs locking
4626
1/2
✓ Branch 17 → 18 taken 14 times.
✗ Branch 17 → 30 not taken.
14 std::unique_lock<std::recursive_mutex> env_lock(memory_mutex);
4627
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 14 times.
14 assert(NULL != subframe);
4628
4629
2/4
✓ Branch 21 → 22 taken 14 times.
✗ Branch 21 → 28 not taken.
✓ Branch 22 → 23 taken 14 times.
✗ Branch 22 → 28 not taken.
14 RegisterSubFrameInRegistry(vfb_size, src->GetFrameBuffer(), subframe);
4630
4631
1/2
✓ Branch 23 → 24 taken 14 times.
✗ Branch 23 → 28 not taken.
28 return subframe;
4632 14 }
4633
4634 32 PVideoFrame ScriptEnvironment::SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size,
4635 int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) {
4636
2/4
✓ Branch 3 → 4 taken 32 times.
✗ Branch 3 → 30 not taken.
✓ Branch 4 → 5 taken 32 times.
✗ Branch 4 → 7 not taken.
32 if(src->GetFrameBuffer()->device->device_type == DEV_TYPE_CPU)
4637
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 32 times.
32 if ((rel_offset | new_pitch | rel_offsetU | rel_offsetV | new_pitchUV) & (frame_align - 1))
4638 ThrowError("Filter Error: Filter attempted to break alignment of VideoFrame.");
4639
4640
1/2
✓ Branch 8 → 9 taken 32 times.
✗ Branch 8 → 30 not taken.
32 VideoFrame *subframe = src->Subframe(rel_offset, new_pitch, new_row_size, new_height, rel_offsetU, rel_offsetV, new_pitchUV);
4641
4642
1/2
✓ Branch 10 → 11 taken 32 times.
✗ Branch 10 → 30 not taken.
32 const AVSMap& avsmap = src->getConstProperties();
4643
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 32 times.
32 if (propNumKeys(&avsmap) > 0)
4644 subframe->setProperties(avsmap);
4645
4646
2/4
✓ Branch 15 → 16 taken 32 times.
✗ Branch 15 → 30 not taken.
✓ Branch 16 → 17 taken 32 times.
✗ Branch 16 → 30 not taken.
32 size_t vfb_size = src->GetFrameBuffer()->GetDataSize();
4647
4648 // vector and maps needs locking
4649
1/2
✓ Branch 17 → 18 taken 32 times.
✗ Branch 17 → 30 not taken.
32 std::unique_lock<std::recursive_mutex> env_lock(memory_mutex); // vector needs locking!
4650
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 32 times.
32 assert(subframe != NULL);
4651
4652
2/4
✓ Branch 21 → 22 taken 32 times.
✗ Branch 21 → 28 not taken.
✓ Branch 22 → 23 taken 32 times.
✗ Branch 22 → 28 not taken.
32 RegisterSubFrameInRegistry(vfb_size, src->GetFrameBuffer(), subframe);
4653
4654
1/2
✓ Branch 23 → 24 taken 32 times.
✗ Branch 23 → 28 not taken.
64 return subframe;
4655 32 }
4656
4657 // alpha aware version
4658 3 PVideoFrame ScriptEnvironment::SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size,
4659 int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA) {
4660
2/4
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 30 not taken.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 7 not taken.
3 if (src->GetFrameBuffer()->device->device_type == DEV_TYPE_CPU)
4661
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 3 times.
3 if ((rel_offset | new_pitch | rel_offsetU | rel_offsetV | new_pitchUV | rel_offsetA) & (frame_align - 1))
4662 ThrowError("Filter Error: Filter attempted to break alignment of VideoFrame.");
4663 VideoFrame* subframe;
4664
1/2
✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 30 not taken.
3 subframe = src->Subframe(rel_offset, new_pitch, new_row_size, new_height, rel_offsetU, rel_offsetV, new_pitchUV, rel_offsetA);
4665
4666
1/2
✓ Branch 10 → 11 taken 3 times.
✗ Branch 10 → 30 not taken.
3 const AVSMap& avsmap = src->getConstProperties();
4667
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 3 times.
3 if (propNumKeys(&avsmap) > 0)
4668 subframe->setProperties(avsmap);
4669
4670
2/4
✓ Branch 15 → 16 taken 3 times.
✗ Branch 15 → 30 not taken.
✓ Branch 16 → 17 taken 3 times.
✗ Branch 16 → 30 not taken.
3 size_t vfb_size = src->GetFrameBuffer()->GetDataSize();
4671
4672 // vector and maps needs locking
4673
1/2
✓ Branch 17 → 18 taken 3 times.
✗ Branch 17 → 30 not taken.
3 std::unique_lock<std::recursive_mutex> env_lock(memory_mutex);
4674
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 3 times.
3 assert(subframe != NULL);
4675
4676
2/4
✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 28 not taken.
✓ Branch 22 → 23 taken 3 times.
✗ Branch 22 → 28 not taken.
3 RegisterSubFrameInRegistry(vfb_size, src->GetFrameBuffer(), subframe);
4677
4678
1/2
✓ Branch 23 → 24 taken 3 times.
✗ Branch 23 → 28 not taken.
6 return subframe;
4679 3 }
4680
4681 27 void* ScriptEnvironment::ManageCache(int key, void* data) {
4682 // An extensible interface for providing system or user access to the
4683 // ScriptEnvironment class without extending the IScriptEnvironment
4684 // definition.
4685
4686
1/2
✓ Branch 2 → 3 taken 27 times.
✗ Branch 2 → 99 not taken.
27 std::lock_guard<std::recursive_mutex> env_lock(memory_mutex);
4687
5/10
✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 7 taken 5 times.
✗ Branch 3 → 12 not taken.
✓ Branch 3 → 42 taken 5 times.
✓ Branch 3 → 46 taken 6 times.
✓ Branch 3 → 49 taken 6 times.
✗ Branch 3 → 66 not taken.
✗ Branch 3 → 69 not taken.
✗ Branch 3 → 86 not taken.
✗ Branch 3 → 87 not taken.
27 switch ((MANAGE_CACHE_KEYS)key)
4688 {
4689 // Called by Cache instances upon creation
4690 5 case MC_RegisterCache:
4691 {
4692 5 AvsCache* cache = reinterpret_cast<AvsCache*>(data);
4693
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 5 times.
5 if (FrontCache != NULL)
4694 CacheRegistry.push_back(FrontCache);
4695 5 FrontCache = cache;
4696 5 break;
4697 }
4698 // Called by Cache instances upon destruction
4699 5 case MC_UnRegisterCache:
4700 {
4701 5 AvsCache* cache = reinterpret_cast<AvsCache*>(data);
4702
1/2
✓ Branch 7 → 8 taken 5 times.
✗ Branch 7 → 9 not taken.
5 if (FrontCache == cache)
4703 5 FrontCache = NULL;
4704 else
4705 CacheRegistry.remove(cache);
4706 5 break;
4707 }
4708 // Called by Cache instances when they want to expand their limit
4709 case MC_NodAndExpandCache:
4710 {
4711 AvsCache* cache = reinterpret_cast<AvsCache*>(data);
4712
4713 // Nod
4714 if (cache != FrontCache)
4715 {
4716 CacheRegistry.move_to_back(cache);
4717 }
4718
4719 // Given that we are within our memory limits,
4720 // try to expand the limit of those caches that
4721 // need it.
4722 // We try to expand most recently used caches first.
4723
4724 int cache_cap = cache->SetCacheHints(CACHE_GET_CAPACITY, 0);
4725 int cache_reqcap = cache->SetCacheHints(CACHE_GET_REQUESTED_CAP, 0);
4726 if (cache_reqcap <= cache_cap)
4727 return 0;
4728
4729 Device* device = cache->GetDevice();
4730 if ((device->memory_used > device->memory_max) || (device->memory_max - device->memory_used < device->memory_max*0.1f))
4731 {
4732 // If we don't have enough free reserves, take away a cache slot from
4733 // a cache instance that hasn't been used since long.
4734
4735 for (AvsCache* old_cache : CacheRegistry)
4736 {
4737 if (old_cache->GetDevice() != device) {
4738 continue;
4739 }
4740 int osize = cache->SetCacheHints(CACHE_GET_SIZE, 0);
4741 if (osize != 0)
4742 {
4743 old_cache->SetCacheHints(CACHE_SET_MAX_CAPACITY, osize - 1);
4744 break;
4745 }
4746 } // for cit
4747 }
4748 #ifdef _DEBUG
4749 _RPT2(0, "ScriptEnvironment::ManageCache increase capacity to %d cache_id=%s\n", cache_cap + 1, cache->FuncName.c_str());
4750 #endif
4751 cache->SetCacheHints(CACHE_SET_MAX_CAPACITY, cache_cap + 1);
4752
4753 break;
4754 }
4755 // Called by Cache instances when they are accessed
4756 5 case MC_NodCache:
4757 {
4758 5 AvsCache* cache = reinterpret_cast<AvsCache*>(data);
4759
1/2
✓ Branch 42 → 43 taken 5 times.
✗ Branch 42 → 44 not taken.
5 if (cache == FrontCache) {
4760 5 return 0;
4761 }
4762
4763 CacheRegistry.move_to_back(cache);
4764 break;
4765 } // case
4766 6 case MC_RegisterMTGuard:
4767 {
4768 6 MTGuard* guard = reinterpret_cast<MTGuard*>(data);
4769
4770
1/2
✓ Branch 46 → 47 taken 6 times.
✗ Branch 46 → 95 not taken.
6 MTGuardRegistry.push_back(guard);
4771
4772 6 break;
4773 }
4774 6 case MC_UnRegisterMTGuard:
4775 {
4776 6 MTGuard* guard = reinterpret_cast<MTGuard*>(data);
4777
1/2
✓ Branch 64 → 51 taken 6 times.
✗ Branch 64 → 65 not taken.
12 for (auto& item : MTGuardRegistry)
4778 {
4779
1/2
✓ Branch 53 → 54 taken 6 times.
✗ Branch 53 → 55 not taken.
6 if (item == guard)
4780 {
4781 6 item = NULL;
4782 6 break;
4783 }
4784 }
4785 6 break;
4786 }
4787 case MC_RegisterGraphNode:
4788 {
4789 FilterGraphNode* node = reinterpret_cast<FilterGraphNode*>(data);
4790 GraphNodeRegistry.push_back(node);
4791 break;
4792 }
4793 case MC_UnRegisterGraphNode:
4794 {
4795 FilterGraphNode* node = reinterpret_cast<FilterGraphNode*>(data);
4796 for (auto& item : GraphNodeRegistry)
4797 {
4798 if (item == node)
4799 {
4800 item = NULL;
4801 break;
4802 }
4803 }
4804 break;
4805 }
4806 case MC_QueryAvs25:
4807 case MC_QueryAvsPreV11C:
4808 {
4809 break; // not cache related
4810 }
4811 } // switch
4812 22 return 0;
4813 27 }
4814
4815
4816 5 bool ScriptEnvironment::PlanarChromaAlignment(IScriptEnvironment::PlanarChromaAlignmentMode key){
4817 5 bool oldPlanarChromaAlignmentState = PlanarChromaAlignmentState;
4818
4819
3/3
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 3 times.
5 switch (key)
4820 {
4821 1 case IScriptEnvironment::PlanarChromaAlignmentOff:
4822 {
4823 1 PlanarChromaAlignmentState = false;
4824 1 break;
4825 }
4826 1 case IScriptEnvironment::PlanarChromaAlignmentOn:
4827 {
4828 1 PlanarChromaAlignmentState = true;
4829 1 break;
4830 }
4831 3 default:
4832 3 break;
4833 }
4834 5 return oldPlanarChromaAlignmentState;
4835 }
4836
4837 /* A helper for Invoke.
4838 Copy a nested array of 'src' into a flat array 'dst'.
4839 Returns the number of elements that have been written to 'dst'.
4840 If 'dst' is NULL, will still return the number of elements
4841 that would have been written to 'dst', but will not actually write to 'dst'.
4842 */
4843 44 static size_t Flatten(const AVSValue& src, AVSValue* dst, size_t index, int level, const char* const* arg_names = NULL) {
4844 // level is starting from zero
4845 44 if (src.IsArray()
4846
5/6
✓ Branch 3 → 4 taken 12 times.
✓ Branch 3 → 6 taken 32 times.
✓ Branch 4 → 5 taken 12 times.
✗ Branch 4 → 6 not taken.
✓ Branch 7 → 8 taken 12 times.
✓ Branch 7 → 17 taken 32 times.
44 && level == 0
4847 ) { // flatten for the first arg level
4848 12 const int array_size = src.ArraySize();
4849
2/2
✓ Branch 16 → 10 taken 30 times.
✓ Branch 16 → 20 taken 12 times.
42 for (int i=0; i<array_size; ++i) {
4850
1/4
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 30 times.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 15 not taken.
30 if (!arg_names || arg_names[i] == 0)
4851 30 index = Flatten(src[i], dst, index, level+1);
4852 }
4853 } else {
4854
2/2
✓ Branch 17 → 18 taken 16 times.
✓ Branch 17 → 19 taken 16 times.
32 if (dst != NULL)
4855 16 dst[index] = src;
4856 32 ++index;
4857 }
4858 44 return index;
4859 }
4860
4861 7 const Function* ScriptEnvironment::Lookup(const char* search_name, const AVSValue* args, size_t num_args,
4862 bool& pstrict, size_t args_names_count, const char* const* arg_names, IScriptEnvironment2* ctx)
4863 {
4864
1/2
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 78 not taken.
7 AVSValue avsv;
4865
3/10
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 76 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 7 times.
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 76 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 34 taken 7 times.
7 if (ctx->GetVarTry(search_name, &avsv) && avsv.IsFunction()) {
4866 //auto& funcv = avsv.AsFunction(); // c++ strict conformance: cannot Convert PFunction to PFunction&
4867 const PFunction& funcv = avsv.AsFunction();
4868 const char* name = funcv->GetLegacyName();
4869 const Function* func = funcv->GetDefinition();
4870 if (name != nullptr) {
4871 // wrapped function
4872 search_name = name;
4873 }
4874 else if (AVSFunction::TypeMatch(func->param_types, args, num_args, false, threadEnv.get()) &&
4875 AVSFunction::ArgNameMatch(func->param_types, args_names_count, arg_names))
4876 {
4877 pstrict = AVSFunction::TypeMatch(func->param_types, args, num_args, true, threadEnv.get());
4878 return func;
4879 }
4880 }
4881
4882
1/2
✓ Branch 34 → 35 taken 7 times.
✗ Branch 34 → 76 not taken.
7 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
4883
4884 7 const Function *result = NULL;
4885
4886 7 auto orig_args_names_count = args_names_count;
4887
4888 size_t oanc;
4889 do {
4890 // if args_names_count>0 then a 2x2 loop, strict yes/no, with or without args name matching
4891 // if =0, 2x loop strict yes/no,without args name matching
4892
1/2
✓ Branch 58 → 37 taken 7 times.
✗ Branch 58 → 59 not taken.
7 for (int strict = 1; strict >= 0; --strict) {
4893 7 pstrict = strict & 1;
4894 // first, look in loaded plugins or user defined functions
4895
1/2
✓ Branch 37 → 38 taken 7 times.
✗ Branch 37 → 74 not taken.
7 result = plugin_manager->Lookup(search_name, args, num_args, pstrict, args_names_count, arg_names);
4896
1/2
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 40 taken 7 times.
7 if (result)
4897 return result;
4898
4899 // then, look for a built-in function
4900
1/2
✓ Branch 56 → 41 taken 68 times.
✗ Branch 56 → 57 not taken.
68 for (int i = 0; i < sizeof(builtin_functions)/sizeof(builtin_functions[0]); ++i)
4901
3/4
✓ Branch 53 → 54 taken 1361 times.
✗ Branch 53 → 74 not taken.
✓ Branch 54 → 42 taken 1300 times.
✓ Branch 54 → 55 taken 61 times.
1361 for (const AVSFunction* j = builtin_functions[i]; !j->empty(); ++j)
4902 {
4903
3/4
✓ Branch 42 → 43 taken 1300 times.
✗ Branch 42 → 74 not taken.
✓ Branch 43 → 44 taken 7 times.
✓ Branch 43 → 52 taken 1293 times.
1300 if (streqi(j->name, search_name)) {
4904
3/6
✓ Branch 44 → 45 taken 7 times.
✗ Branch 44 → 74 not taken.
✓ Branch 45 → 46 taken 7 times.
✗ Branch 45 → 49 not taken.
✓ Branch 50 → 51 taken 7 times.
✗ Branch 50 → 52 not taken.
14 if (AVSFunction::TypeMatch(j->param_types, args, num_args, pstrict, ctx) &&
4905
2/4
✓ Branch 46 → 47 taken 7 times.
✗ Branch 46 → 74 not taken.
✓ Branch 47 → 48 taken 7 times.
✗ Branch 47 → 49 not taken.
7 AVSFunction::ArgNameMatch(j->param_types, args_names_count, arg_names))
4906 7 return j;
4907 }
4908 }
4909 }
4910 // Try again without arg name matching
4911 oanc = args_names_count;
4912 args_names_count = 0;
4913 } while (oanc);
4914
4915 // If we got here it means the function has not been found.
4916 // If we haven't done so yet, load the plugins in the autoload folders
4917 // and try again.
4918 if (!plugin_manager->HasAutoloadExecuted())
4919 {
4920 plugin_manager->AutoloadPlugins();
4921 args_names_count = orig_args_names_count;
4922 return Lookup(search_name, args, num_args, pstrict, args_names_count, arg_names, ctx);
4923 }
4924
4925 return NULL;
4926 7 }
4927
4928 bool ScriptEnvironment::CheckArguments(const Function* func, const AVSValue* args, size_t num_args,
4929 bool &pstrict, size_t args_names_count, const char* const* arg_names)
4930 {
4931 if (AVSFunction::TypeMatch(func->param_types, args, num_args, false, threadEnv.get()) &&
4932 AVSFunction::ArgNameMatch(func->param_types, args_names_count, arg_names))
4933 {
4934 pstrict = AVSFunction::TypeMatch(func->param_types, args, num_args, true, threadEnv.get());
4935 return true;
4936 }
4937 return false;
4938 }
4939
4940 #ifdef LISTARGUMENTS
4941 // debug
4942 static void ListArguments(const char *name, const AVSValue& args, int &level, bool flattened) {
4943 if (!strcmp(name, "Import"))
4944 return;
4945 if (!strcmp(name, "Eval"))
4946 return;
4947 if (level == 0)
4948 fprintf(stdout, "------- %s (%s)\r\n", name, flattened ? "flattened" : "orig");
4949 level++;
4950 if (args.IsArray()) {
4951 const int as = args.ArraySize();
4952 fprintf(stdout, "Array, size=%d {\r\n", as);
4953 for (int i = 0; i < as; i++) {
4954 fprintf(stdout, "Element#%d\r\n", i);
4955 ListArguments(name, args[i], level, flattened);
4956 }
4957 fprintf(stdout, "}\r\n");
4958 }
4959 else {
4960 if (!args.Defined())
4961 fprintf(stdout, "Undefined\r\n");
4962 else if (args.IsBool())
4963 fprintf(stdout, "Bool %s\r\n", args.AsBool() ? "true" : "false");
4964 else if (args.GetType() == AvsValueType::VALUE_TYPE_LONG) // before IsInt() !
4965 fprintf(stdout, "Long %" PRId64 "\r\n", args.AsLong());
4966 else if (args.IsInt())
4967 fprintf(stdout, "Int %d\r\n", args.AsInt());
4968 else if (args.IsString())
4969 fprintf(stdout, "String %s\r\n", args.AsString());
4970 else if (args.GetType() == AvsValueType::VALUE_TYPE_FLOAT) // before IsFloat() !
4971 fprintf(stdout, "Float %f\r\n", args.AsFloatf());
4972 else if (args.IsFloat())
4973 fprintf(stdout, "Float/Double %lf\r\n", args.AsFloat());
4974 else if (args.IsFunction())
4975 fprintf(stdout, "Function\r\n");
4976 else if (args.IsClip())
4977 fprintf(stdout, "Clip\r\n");
4978 else
4979 fprintf(stdout, "Unknown type\r\n");
4980 }
4981 level--;
4982 }
4983
4984 static void ListArguments2(const char* name, const AVSValue* args, int& level, bool flattened, int len) {
4985 if (!strcmp(name, "Import"))
4986 return;
4987 if (!strcmp(name, "Eval"))
4988 return;
4989 fprintf(stdout, "------- %s (%s)\r\n", name, flattened ? "flattened" : "orig");
4990 level++;
4991 for (int i = 0; i < len; i++) {
4992 ListArguments(name, *(args +i), level, flattened);
4993 }
4994 }
4995 #endif
4996
4997 7 bool ScriptEnvironment::Invoke_(AVSValue *result, const AVSValue& implicit_last,
4998 const char* name, const Function *f, const AVSValue& args, const char* const* arg_names,
4999 InternalEnvironment* env_thread, bool is_runtime)
5000 {
5001
5002
1/8
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 7 taken 7 times.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 1175 not taken.
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 7 not taken.
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 1175 not taken.
7 const int args_names_count = (arg_names && args.IsArray()) ? args.ArraySize() : 0;
5003
5004
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 7 times.
7 if (name == nullptr) {
5005 // for debug printing
5006 name = "<anonymous function>";
5007 }
5008
5009 // Step #1: Flattening
5010 // arrays received in the place of unnamed parameters are flattened back
5011 // to have them as a list again, in order to be compatible with Avisynth's "array elements as comma
5012 // delimited parameters" definition style.
5013
5014 // get how many args we will need to store
5015
1/2
✓ Branch 10 → 11 taken 7 times.
✗ Branch 10 → 1175 not taken.
7 size_t args2_count = Flatten(args, NULL, 0, 0, arg_names);
5016
1/2
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 7 times.
7 if (args2_count > ScriptParser::max_args)
5017 ThrowError("Too many arguments passed to function (max. is %d)", ScriptParser::max_args);
5018
5019 // flatten unnamed args
5020
2/4
✓ Branch 15 → 16 taken 7 times.
✗ Branch 15 → 871 not taken.
✓ Branch 16 → 17 taken 7 times.
✗ Branch 16 → 869 not taken.
7 std::vector<AVSValue> args2(args2_count + 1, AVSValue());
5021
1/2
✓ Branch 20 → 21 taken 7 times.
✗ Branch 20 → 1173 not taken.
7 args2[0] = implicit_last;
5022
1/2
✓ Branch 22 → 23 taken 7 times.
✗ Branch 22 → 1173 not taken.
7 Flatten(args, args2.data() + 1, 0, 0, arg_names);
5023
5024 #ifdef LISTARGUMENTS
5025 // debug list of Invoke arguments before-after flattening
5026 int level = 0;
5027 ListArguments(name, args, level, false); // unflattened remark
5028 ListArguments2(name, args2.data()+1, level, true, args2_count); // flattened remark
5029 #endif
5030
5031 // Step #2: Arguments check and function detection by matching signature.
5032
5033 7 bool strict = false;
5034 7 int argbase = 1;
5035
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 37 taken 7 times.
7 if (f != nullptr) {
5036 // check arguments
5037 if (!this->CheckArguments(f, args2.data() + 1, args2_count, strict, args_names_count, arg_names)) {
5038 if (!implicit_last.Defined() ||
5039 !this->CheckArguments(f, args2.data(), args2_count + 1, strict, args_names_count, arg_names))
5040 return false;
5041 argbase = 0;
5042 args2_count += 1;
5043 }
5044 }
5045 else {
5046 // Because only one level is flattened, for 2+ Dimension arrays result are
5047 // at least two parameters which are still arrays)
5048 // [3,4,5] is flattened as 3,4,5
5049 // clip, [[2,3], [3,4,5]] is flattened as clip, [2,3], [3,4,5]
5050
5051 // find matching function
5052
1/2
✓ Branch 38 → 39 taken 7 times.
✗ Branch 38 → 1173 not taken.
7 f = this->Lookup(name, args2.data() + 1, args2_count, strict, args_names_count, arg_names, env_thread);
5053
1/2
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 48 taken 7 times.
7 if (!f)
5054 {
5055 if (!implicit_last.Defined())
5056 return false;
5057 // search function definitions with implicite "last" given
5058 f = this->Lookup(name, args2.data(), args2_count + 1, strict, args_names_count, arg_names, env_thread);
5059 if (!f)
5060 return false;
5061 argbase = 0;
5062 args2_count += 1;
5063 }
5064 }
5065
5066 // Problem: Animate has parameter signature both "iis.*" and "ciis.*"
5067 // ColorBars()
5068 // Animate(0, 100, "blur", 0.1, 1.5)
5069 // Here we find "iis.*" but it turns out that its given function parameter "Blur" requires a clip
5070 // Thus we got an exception later during the filter instantiation (really it is "Blur" who throws the exception)
5071 // (see comment Issue20200818 later).
5072 // Expression evaluator would catch NotFound and reissue _Invoke with a forced implicit_last in args.
5073
5074 // Step #3: Unnamed arguments
5075
5076 // combine unnamed args into arrays
5077 7 size_t src_index = 0;
5078 7 const char* p = f->param_types;
5079
5080 7 std::vector<AVSValue> args3;
5081 7 std::vector<bool> args3_really_filled;
5082
5083 7 bool last_was_named = false;
5084
2/2
✓ Branch 134 → 51 taken 66 times.
✓ Branch 134 → 135 taken 7 times.
73 while (*p) {
5085
2/2
✓ Branch 51 → 52 taken 29 times.
✓ Branch 51 → 55 taken 37 times.
66 if (*p == '[') {
5086 // named parameter check: between brackets (no name validity check, empty is valid as well)
5087 29 const char* pstart = p + 1;
5088 29 p = strchr(pstart, ']');
5089
1/2
✗ Branch 52 → 53 not taken.
✓ Branch 52 → 54 taken 29 times.
29 if (!p) break;
5090 29 last_was_named = true;
5091 29 p++; // skip closing bracket
5092 }
5093
2/4
✓ Branch 55 → 56 taken 37 times.
✗ Branch 55 → 57 not taken.
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 121 taken 37 times.
37 else if (((p[1] == '*') || (p[1] == '+'))) {
5094 // Case of unnamed arrays or named arrays
5095 // Special named array: [] with no real name
5096 // Pre 3.6: filling up named arrays with names from script level was not possible.
5097 // Memo: array function signature types: * means "zero or more". + means "one or more"
5098 // Example fake-named array: the first clip array parameter of BlankClip "[]c*[length]i etc.
5099 // Some functions with array signatures:
5100 // Select i.+
5101 // SelectEvery: cii*
5102 // Format: s.*
5103 // MPP_SharedMemoryServer: csi[aux_clips]c*[max_cache_frames]i
5104 // BlankClip []c*[length]i (fake named array)
5105 // ArrayGet .i+
5106 // ArraySize .
5107 // Array/ArrayCreate .*: [[2,3,4], [1,2]] -> [2,3,4], [1,2]
5108 // user_script_function(clip, int_array) c[]i*
5109 // Pre v3.6 script-level array definition: autodetect comma separated list elements
5110 // The parser detects the end of array: the next argument type is different.
5111 // Since the end of free-typed (".+" or ".*") arrays cannot be recognized, they are allowed to appear
5112 // only at the very end of the parameter signature
5113 // Option since 3.6: bracket-style array definition syntax
5114 size_t start = src_index;
5115 const char arg_type = *p;
5116
5117
5118 if (src_index < args2_count &&
5119 args2[argbase + src_index].IsArray()
5120 // After flattening this only happens when an explicite array was passed.
5121 // typed array recognition is easy and unambigous
5122 && arg_type != '.' // to avoid .+ case when one can pass array of arrays like [[1,2,3],[4,5]] flattened to [1,2,3],[4,5]
5123 )
5124 {
5125 if (arg_type != '.') {
5126 if (!AVSFunction::SingleTypeMatchArray(arg_type, args2[argbase + src_index], strict))
5127 ThrowError("Array elements do not match with the specified type in function %s", name);
5128 }
5129
5130 if(p[1] == '+' && args2[argbase + src_index].ArraySize() == 0) // one or more
5131 ThrowError("An array with zero element size was given to function %s which expects a 'one-or-more' style array argument", name);
5132 args3.push_back(args2[argbase + src_index]); // can't delete args2 early because of this
5133 args3_really_filled.push_back(true); // valid array content
5134 src_index++;
5135 }
5136 else
5137 {
5138 // Collect consecutive similary-typed parameters into an array by automatic detection of its end:
5139 // The end of a simple-typed array:
5140 // - when the comma separated parameter list is changing type
5141 // - no more parameters (list ends)
5142 // E.g. collect values into an integer array (i*) until values in the list are still integers.
5143 // The array collection in parameter sequence 1, 2, 3, "hello1", "hello2" will stop before "hello1"
5144 // because type is changed from i to s.
5145 // This is why the end of an 'any-type' array (.*) cannot be detected from a comma delimited list:
5146 // no stopping condition (no type), only the end of list can stop collecting.
5147 while ((src_index < args2_count)) {
5148 const bool match = AVSFunction::SingleTypeMatch(arg_type, args2[argbase + src_index], strict);
5149 if (!match)
5150 break;
5151 src_index++;
5152 }
5153 size_t size = src_index - start; // so we have size number of items detected. Put them back into an array.
5154 assert(args2_count >= size);
5155
5156 // Even if the AVSValue below is an array of zero size, we can't skip adding it to args3,
5157 // because filters like BlankClip []c* or MPP_SharedMemoryServer might still be expecting it.
5158 // 3.7.1.: This statement is no longer true. BlankClip was modified to handle Undefined AVSValue instead of array[] properly
5159
5160 /*
5161 Considerations on resolving parameter handling for "array of anything" parameter when array(s) would be passed directly.
5162 Memo:
5163 - Avisynth signature: .* or .+
5164 - Script function specifier val_array or val_array_nz
5165
5166 When parameter signature is array of anything (.* or .+) and the
5167 parameter is passed unnamed (even if it is a named parameter) then
5168 there is an ambiguos situation which
5169
5170 Giving a parameter list of 1,2,3 will be detected as [1,2,3] (compatibility: list is grouped together into an array internally).
5171 E.g. 1 will be detected as [1]
5172 Passing nothing from an Avisynth script in the place of the parameter will be detected as [] (array size is zero, value is defined)
5173 initially, then later in the named parameter processing procedure it can be overridden directly by a named value.
5174
5175 This rule means that a directly given script array e.g. [1,2,3] will be detected as [[1,2,3]], because unnamed and untyped parameters are
5176 put together into an array, which has the size of the list. This is a list of 1 element which happens to be an array.
5177 Avisynth cannot 'guess' whether we want to define a single array directly or this array is the only one part of the list.
5178 So an unnamedly passed [1,2,3] will appear as [ [1,2,3] ] in the unnamed "array of anything" parameter value.
5179
5180 Syntax hint:
5181 When someone would like to pass a directly specified array (e.g. [1,2,3] instead of 1,2,3) to a .+ or .* parameter
5182 the parameter must be passed by name to avoid ambiguity!
5183
5184 Example script function definition:
5185 function foo(val_array "n")
5186
5187 Value seen inside the function body:
5188 Call Value of n
5189 foo() Undefined
5190 foo(1) [1] (*)
5191 foo(1,2,3) [1,2,3] (*)
5192 foo([1,2,3]) ! [[1,2,3]] (*)
5193 foo([1,2,3],[4,5]) ! [[1,2,3],[4,5]] (*)
5194 foo(n=[1,2,3]) [1,2,3]
5195 foo(n=[[1,2,3],[4,5]]) [[1,2,3],[4,5]]
5196 foo(n=[]) []
5197 foo(n=1) [1] (**)
5198 foo(n="hello") ["hello"] (**)
5199
5200 * compatible Avisynth way: comma delimited consecutive values form an array
5201 ** simple-type value passed to a named array parameter will be created as a 1-element real array
5202
5203 // unnamed signature
5204 function foo(val_array n)
5205 Call n
5206 foo() [] (defined and array size is zero) Avisynth compatible behaviour
5207 */
5208
5209 if (size == 0 && p[1] == '+') // '+': one or more
5210 {
5211 if(!last_was_named)
5212 ThrowError("A zero-sized array (or empty parameter list) appeared in the place of an unnamed parameter to function %s which expects a 'one-or-more' style array argument", name);
5213 args3.push_back(AVSValue()); // push undefined
5214 args3_really_filled.push_back(false); // zero sized (not found) array can be specified later with argname
5215 }
5216 else {
5217 if (size == 0) {
5218 if (last_was_named)
5219 args3.push_back(AVSValue());
5220 // Named array is left Undefined here. In a later section it can be overridden with named argument
5221 // This ensures that passing empty [] is different than not passing anything (Defined() vs. Undefined())
5222 // because of this (undefined instead of zero-sized array) BlankClip was changed to handle to allow the parameter as undefined.
5223 else {
5224 args3.push_back(AVSValue(NULL, 0));
5225 // Unnamed parameter: array of zero size.
5226 // Fixme cosmetics: Maybe this one even cannot be reached, because an unnamed compulsory parameter cannot be 'zero or more' array
5227 }
5228 }
5229 else
5230 // create a proper array from the list of elements
5231 args3.push_back(AVSValue(args2.data() + argbase + start, (int)size)); // can't delete args2 early because of this
5232
5233 if (last_was_named && size > 0)
5234 args3_really_filled.push_back(true); // valid array content
5235 else
5236 args3_really_filled.push_back(false); // zero sized (not found) array can be specified later with argname
5237 }
5238 }
5239
5240 p += 2; // skip type-char and '*' or '+'
5241 last_was_named = false;
5242 }
5243 else {
5244
2/2
✓ Branch 121 → 122 taken 16 times.
✓ Branch 121 → 125 taken 21 times.
37 if (src_index < args2_count) {
5245 // At this point we could still have an array in AVSValue because a directly given (e.g. [1,2,3]) array is accepted at the place of a "." (any) type
5246 // The signature checker recognizes and reports a signature match in AVSFunction::TypeMatch
5247 // Example call: fn([1,2,3]) where fn has a signature of "c[n]."
5248 // Allowing it does not do any harm
5249 // FIXME, When removed, we'd better remove the limitation in (look for: QWERTZUIOP)
5250 #ifdef DISABLE_ARRAYS_WHEN_DOT_ALONE
5251 if (args2[argbase + src_index].IsArray() && p[0] != 'a') {
5252 ThrowError((std::string("An array is passed to a non-array parameter type in function ") + std::string(name)).c_str());
5253 }
5254 else
5255 #endif
5256 {
5257
1/2
✓ Branch 123 → 124 taken 16 times.
✗ Branch 123 → 1169 not taken.
16 args3.push_back(args2[argbase + src_index]);
5258 }
5259
1/2
✓ Branch 124 → 129 taken 16 times.
✗ Branch 124 → 1169 not taken.
16 args3_really_filled.push_back(true);
5260 }
5261 else {
5262
2/4
✓ Branch 125 → 126 taken 21 times.
✗ Branch 125 → 889 not taken.
✓ Branch 126 → 127 taken 21 times.
✗ Branch 126 → 887 not taken.
21 args3.push_back(AVSValue());
5263
1/2
✓ Branch 128 → 129 taken 21 times.
✗ Branch 128 → 1169 not taken.
21 args3_really_filled.push_back(false);
5264 }
5265 37 src_index++;
5266 37 p++;
5267 // Skip possible array marker for named arrays like [colors]f+
5268 // Note: this declaration style is recognized only for versions >= 3.5.3
5269 // so plugins using this syntax won't load/work with older AviSynth versions
5270 // Older versions without this check will report various errors like "named parameter was given more than once"
5271 // but only when given with names. Providing a named parameter as unnamed was possible from
5272 // direct plugin use formerly as well.
5273
2/4
✓ Branch 129 → 130 taken 37 times.
✗ Branch 129 → 131 not taken.
✗ Branch 130 → 131 not taken.
✓ Branch 130 → 132 taken 37 times.
37 if ((p[0] == '*') || (p[0] == '+'))
5274 p++;
5275
5276 37 last_was_named = false;
5277 }
5278 }
5279
1/2
✗ Branch 135 → 136 not taken.
✓ Branch 135 → 137 taken 7 times.
7 if (src_index < args2_count)
5280 ThrowError("Too many arguments to function %s", name);
5281
5282 // Step #4: Named arguments
5283
5284 // copy named args
5285
1/2
✗ Branch 243 → 138 not taken.
✓ Branch 243 → 244 taken 7 times.
7 for (int i = 0; i<args_names_count; ++i) {
5286 if (arg_names[i]) {
5287 size_t named_arg_index = 0;
5288 for (const char* p = f->param_types; *p; ++p) {
5289 if (*p == '*' || *p == '+') {
5290 continue; // without incrementing named_arg_index
5291 }
5292 else if (*p == '[') {
5293 p += 1;
5294 const char* q = strchr(p, ']');
5295 if (!q) break;
5296 if (strlen(arg_names[i]) == size_t(q - p) && !_strnicmp(arg_names[i], p, q - p)) {
5297 // we have a match
5298 if (args3[named_arg_index].Defined() && args3_really_filled[named_arg_index]) {
5299 // when a parameter like named array was filled as an empty array
5300 // from the unnamed section we don't throw error for the first time
5301 ThrowError("Script error: the named argument \"%s\" was passed more than once (twice as named or first unnamed then named) to %s", arg_names[i], name);
5302 }
5303 else if (args[i].Defined() && args[i].IsArray() && ((q[2] == '*' || q[2] == '+')) && !AVSFunction::SingleTypeMatchArray(q[1], args[i], false))
5304 {
5305 // e.g. passing colors=[235, 128, "Hello"] to [colors]f+
5306 ThrowError("Script error: the named array argument \"%s\" to %s had a wrong element type", arg_names[i], name);
5307 }
5308 else if (args[i].Defined() && !args[i].IsArray() && !AVSFunction::SingleTypeMatch(q[1], args[i], false))
5309 {
5310 ThrowError("Script error: the named argument \"%s\" to %s had the wrong type (passed '%c')", arg_names[i], name, args[i].GetType(), q[1]);
5311 }
5312 else {
5313 if (args[i].Defined() && args[i].IsArray()) {
5314 // e.g. foo(sigma=[1.0, 1.1]) to [sigma]f is invalid
5315 if (!(q[2] == '*' || q[2] == '+') && q[1] != '.' && q[1] != 'a')
5316 ThrowError("Script error: the named argument \"%s\" to %s had the wrong type (passed an array to a non-array and not-any parameter)", arg_names[i], name);
5317 if (q[2] == '+' && args[i].ArraySize() == 0)
5318 ThrowError("Script error: the named argument \"%s\" to %s is a 'one-or-more' element style array but had zero element count.", arg_names[i], name);
5319 }
5320 // Note (after having array type parameters in script functions)
5321 // When passing a simple value to an array parameter, we should really make an array of it
5322 // or else script function parameters of array types won't see this parameter as an array inside the function body.
5323 // Note: Unlike AVS scripts a real plugin - through Avisynth interface - is
5324 // - allowed to index an AVSValue which is not even an array. Index 0 returns the simple-type value itself.
5325 // - calling AVSValue ArraySize() returns 1
5326 // But an AVS script syntax indexing and ArraySize() requires a real array, simple base type is not allowed there.
5327 if(args[i].Defined() && !args[i].IsArray() && (q[2] == '*' || q[2] == '+' || q[1] == 'a'))
5328 args3[named_arg_index] = AVSValue(&args[i],1); // really create an array with one element
5329 else
5330 args3[named_arg_index] = args[i];
5331 args3_really_filled[named_arg_index] = true;
5332 goto success;
5333 }
5334 }
5335 else {
5336 p = q + 1;
5337 }
5338 }
5339 named_arg_index++;
5340 }
5341 // failure
5342 ThrowError("Script error: %s does not have a named argument \"%s\"", name, arg_names[i]);
5343 success:;
5344 }
5345 }
5346
5347
2/4
✓ Branch 244 → 245 taken 7 times.
✗ Branch 244 → 896 not taken.
✓ Branch 245 → 246 taken 7 times.
✗ Branch 245 → 894 not taken.
7 std::vector<AVSValue>(args3).shrink_to_fit();
5348
5349 // end of parameter matching and parsing
5350
5351
1/2
✗ Branch 247 → 248 not taken.
✓ Branch 247 → 279 taken 7 times.
7 if(is_runtime) {
5352 // Invoked by a thread or GetFrame
5353 AVSValue funcArgs(args3.data(), (int)args3.size());
5354
5355 if (f->isPluginAvs25) // like GRunT's AverageLuma wrapper
5356 *result = f->apply(funcArgs, f->user_data, (IScriptEnvironment*)((IScriptEnvironment_Avs25*)env_thread));
5357 else if (f->isPluginPreV11C)
5358 *result = f->apply(funcArgs, f->user_data, (IScriptEnvironment*)((IScriptEnvironment_AvsPreV11C*)env_thread));
5359 else
5360 *result = f->apply(funcArgs, f->user_data, env_thread);
5361 return true;
5362 }
5363
5364 // 3.7.2: changed memory_mutex to invoke_mutex
5365 // Concurrent GetFrame with Invoke causes deadlock.
5366 // Healing mode #2
5367
1/2
✓ Branch 279 → 280 taken 7 times.
✗ Branch 279 → 1169 not taken.
14 std::lock_guard<std::recursive_mutex> env_lock(invoke_mutex);
5368
5369 // Concurrent GetFrame with Invoke causes deadlock.
5370 // Increment this variable when Invoke running
5371 // to prevent submitting job to threadpool
5372 // Healing mode #1 from Neo
5373
1/2
✓ Branch 281 → 282 taken 7 times.
✗ Branch 281 → 1167 not taken.
14 ScopedCounter suppressThreadCount_(threadEnv->GetSuppressThreadCount());
5374
5375 // chainedCtor is true if we are being constructed inside/by the
5376 // constructor of another filter. In that case we want MT protections
5377 // applied not here, but by the Invoke() call of that filter.
5378 7 const bool chainedCtor = invoke_stack.size() > 0;
5379
5380 7 MtModeEvaluator mthelper;
5381
5382 // Save clips whether one of them is returned unaltered after Invoke
5383 14 std::vector<IClip*> clip_parameters_raw;
5384
5385 7 bool foundClipArgument = false;
5386
2/2
✓ Branch 299 → 286 taken 16 times.
✓ Branch 299 → 300 taken 7 times.
23 for (int i = argbase; i < (int)args2.size(); ++i)
5387 {
5388 16 auto& argx = args2[i];
5389 // todo PF 161112 new arrays: recursive look into arrays whether they contain clips
5390
3/4
✓ Branch 287 → 288 taken 16 times.
✗ Branch 287 → 1163 not taken.
✓ Branch 288 → 289 taken 7 times.
✓ Branch 288 → 297 taken 9 times.
16 if (argx.IsClip())
5391 {
5392 7 foundClipArgument = true;
5393
5394
1/2
✓ Branch 289 → 290 taken 7 times.
✗ Branch 289 → 920 not taken.
7 const PClip &clip = argx.AsClip();
5395 7 IClip *clip_raw = (IClip*)((void*)clip);
5396
1/2
✓ Branch 291 → 292 taken 7 times.
✗ Branch 291 → 918 not taken.
7 ClipDataStore *data = this->ClipData(clip_raw);
5397 // Save clips whether one of them is returned unaltered after Invoke
5398
1/2
✓ Branch 292 → 293 taken 7 times.
✗ Branch 292 → 918 not taken.
7 clip_parameters_raw.push_back(clip_raw);
5399
5400
1/2
✓ Branch 293 → 294 taken 7 times.
✗ Branch 293 → 295 not taken.
7 if (!data->CreatedByInvoke)
5401 {
5402 #ifdef _DEBUG
5403 _RPT3(0, "ScriptEnvironment::Invoke.AddChainedFilter %s thread %d this->DefaultMtMode=%d\n", name, GetCurrentThreadId(), (int)this->DefaultMtMode);
5404 #endif
5405
1/2
✓ Branch 294 → 295 taken 7 times.
✗ Branch 294 → 918 not taken.
7 mthelper.AddChainedFilter(clip, this->DefaultMtMode);
5406 }
5407 7 }
5408 }
5409 7 bool isSourceFilter = !foundClipArgument;
5410
5411 // Capture call-time parameter values for filter-prop injection.
5412 // Must run before funcCtor (which moves args3) so we can read args3 here.
5413 // Using args3 (by now it is fully resolved) lets us match both positional and named call args.
5414 // For 99.9% of filters (not in FilterPropMap) this is just one fast O(1) map lookup.
5415 // Memo: f->param_types is the full function parameter signature.
5416 7 const std::vector<PropEntry>* prop_entries_to_inject = nullptr;
5417 14 std::vector<PropEntry> captured_entries;
5418
1/2
✓ Branch 301 → 302 taken 7 times.
✗ Branch 301 → 422 not taken.
7 if (f->name != nullptr) {
5419
3/6
✓ Branch 304 → 305 taken 7 times.
✗ Branch 304 → 306 not taken.
✓ Branch 307 → 308 taken 7 times.
✗ Branch 307 → 923 not taken.
✓ Branch 308 → 309 taken 7 times.
✗ Branch 308 → 921 not taken.
7 const std::string fname_for_prop = NormalizeString(f->canon_name ? f->canon_name : f->name);
5420
1/2
✓ Branch 311 → 312 taken 7 times.
✗ Branch 311 → 939 not taken.
7 auto prop_it = FilterPropMap.find(fname_for_prop);
5421
3/6
✓ Branch 314 → 315 taken 7 times.
✗ Branch 314 → 317 not taken.
✓ Branch 315 → 316 taken 7 times.
✗ Branch 315 → 317 not taken.
✓ Branch 318 → 319 taken 7 times.
✗ Branch 318 → 328 not taken.
7 if (prop_it == FilterPropMap.end() && f->canon_name != nullptr)
5422
3/6
✓ Branch 321 → 322 taken 7 times.
✗ Branch 321 → 931 not taken.
✓ Branch 322 → 323 taken 7 times.
✗ Branch 322 → 929 not taken.
✓ Branch 323 → 324 taken 7 times.
✗ Branch 323 → 927 not taken.
21 prop_it = FilterPropMap.find(NormalizeString(f->name));
5423
1/2
✗ Branch 330 → 331 not taken.
✓ Branch 330 → 420 taken 7 times.
7 if (prop_it != FilterPropMap.end()) {
5424 bool needs_capture = false;
5425 for (const auto& e : prop_it->second)
5426 if (!e.value.Defined() || !e.param_name.empty()) { needs_capture = true; break; }
5427 if (needs_capture) {
5428 // Scan f->param_types for a [name]type parameter, return its args3 index or -1.
5429 // Works for both positional and named call args because args3 is fully resolved.
5430 auto findParamIdx = [&](const char* pname) -> int {
5431 int idx = 0;
5432 for (const char* p = f->param_types; *p; ++p) {
5433 if (*p == '*' || *p == '+') continue; // modifier, doesn't add a new slot
5434 if (*p == '[') {
5435 ++p;
5436 const char* q = strchr(p, ']');
5437 if (!q) break;
5438 if (!_strnicmp(pname, p, q - p) && strlen(pname) == size_t(q - p))
5439 return idx; // found: args3[idx] holds this param's value
5440 p = q + 1; // advance to type char, outer ++p moves past it
5441 }
5442 idx++;
5443 }
5444 return -1;
5445 };
5446 auto scalarMatch = [](const AVSValue& actual, const AVSValue& candidate) -> bool {
5447 if (actual.IsString() && candidate.IsString())
5448 return _stricmp(actual.AsString(), candidate.AsString()) == 0;
5449 // bool and int are interchangeable: true==1, false==0
5450 if ((actual.IsInt() || actual.IsBool()) && (candidate.IsInt() || candidate.IsBool())) {
5451 int64_t a = actual.IsInt() ? actual.AsLong() : (actual.AsBool() ? 1LL : 0LL);
5452 int64_t c = candidate.IsInt() ? candidate.AsLong() : (candidate.AsBool() ? 1LL : 0LL);
5453 return a == c;
5454 }
5455 return (actual.IsFloat() && candidate.IsFloat() &&
5456 actual.AsFloat() == candidate.AsFloat());
5457 };
5458 captured_entries = prop_it->second; // copy to resolve
5459 for (auto& entry : captured_entries) {
5460 if (!entry.param_name.empty()) {
5461 // Conditional: find formal param 'param_name' in args3 and compare to param_match.
5462 // param_match may be a scalar or an alias array (any element satisfies).
5463 // Works for positional and named call sites alike.
5464 int idx = findParamIdx(entry.param_name.c_str());
5465 if (idx < 0 || idx >= (int)args3.size() || !args3[idx].Defined()) {
5466 entry.key.clear(); // param not in signature or not provided: skip
5467 } else {
5468 const AVSValue& actual = args3[idx];
5469 bool matched = false;
5470 if (entry.param_match.IsArray()) {
5471 for (int j = 0; j < entry.param_match.ArraySize() && !matched; ++j)
5472 matched = scalarMatch(actual, entry.param_match[j]);
5473 } else {
5474 matched = scalarMatch(actual, entry.param_match);
5475 }
5476 if (!matched)
5477 entry.key.clear(); // condition not met: skip
5478 }
5479 } else if (!entry.value.Defined()) {
5480 // Unconditional capture: find formal param named entry.key in args3.
5481 int idx = findParamIdx(entry.key.c_str());
5482 if (idx >= 0 && idx < (int)args3.size() && args3[idx].Defined())
5483 entry.value = args3[idx];
5484 // Still undefined: param not found or not passed; skipped at injection
5485 }
5486 }
5487 prop_entries_to_inject = &captured_entries;
5488 } else {
5489 prop_entries_to_inject = &(prop_it->second); // all entries have static values
5490 }
5491 }
5492 7 }
5493
5494 // Capture passthrough source clip before funcCtor moves args3.
5495 // Only set when the filter is registered in FilterPropPassthroughSet AND args3[0] is a clip.
5496
1/2
✓ Branch 422 → 423 taken 7 times.
✗ Branch 422 → 1161 not taken.
14 PClip prop_passthrough_src;
5497
1/2
✓ Branch 423 → 424 taken 7 times.
✗ Branch 423 → 461 not taken.
7 if (f->name != nullptr) {
5498
3/6
✓ Branch 426 → 427 taken 7 times.
✗ Branch 426 → 428 not taken.
✓ Branch 429 → 430 taken 7 times.
✗ Branch 429 → 944 not taken.
✓ Branch 430 → 431 taken 7 times.
✗ Branch 430 → 942 not taken.
7 const std::string fname_pt = NormalizeString(f->canon_name ? f->canon_name : f->name);
5499
1/2
✓ Branch 433 → 434 taken 7 times.
✗ Branch 433 → 960 not taken.
7 bool in_set = FilterPropPassthroughSet.count(fname_pt) > 0;
5500
2/4
✓ Branch 434 → 435 taken 7 times.
✗ Branch 434 → 445 not taken.
✓ Branch 435 → 436 taken 7 times.
✗ Branch 435 → 445 not taken.
7 if (!in_set && f->canon_name != nullptr)
5501
3/6
✓ Branch 438 → 439 taken 7 times.
✗ Branch 438 → 952 not taken.
✓ Branch 439 → 440 taken 7 times.
✗ Branch 439 → 950 not taken.
✓ Branch 440 → 441 taken 7 times.
✗ Branch 440 → 948 not taken.
21 in_set = FilterPropPassthroughSet.count(NormalizeString(f->name)) > 0;
5502
2/10
✗ Branch 445 → 446 not taken.
✓ Branch 445 → 452 taken 7 times.
✗ Branch 447 → 448 not taken.
✗ Branch 447 → 452 not taken.
✗ Branch 449 → 450 not taken.
✗ Branch 449 → 960 not taken.
✗ Branch 450 → 451 not taken.
✗ Branch 450 → 452 not taken.
✗ Branch 453 → 454 not taken.
✓ Branch 453 → 459 taken 7 times.
7 if (in_set && !args3.empty() && args3[0].IsClip())
5503 prop_passthrough_src = args3[0].AsClip();
5504 7 }
5505
5506
2/4
✓ Branch 461 → 462 taken 7 times.
✗ Branch 461 → 464 not taken.
✓ Branch 462 → 463 taken 7 times.
✗ Branch 462 → 464 not taken.
7 auto call_env = f->isPluginAvs25 || f->isPluginPreV11C ? nullptr : threadEnv.get();
5507
1/2
✗ Branch 465 → 466 not taken.
✓ Branch 465 → 468 taken 7 times.
7 auto call_env25 = f->isPluginAvs25 ? threadEnv.get()->GetEnv25() : nullptr;
5508
1/2
✗ Branch 469 → 470 not taken.
✓ Branch 469 → 472 taken 7 times.
7 auto call_envPreV11C = f->isPluginPreV11C ? threadEnv.get()->GetEnvPreV11C() : nullptr;
5509 // ... and we're finally ready to make the call
5510 std::unique_ptr<const FilterConstructor> funcCtor =
5511
1/2
✓ Branch 473 → 474 taken 7 times.
✗ Branch 473 → 963 not taken.
7 std::make_unique<const FilterConstructor>(call_env, call_env25, call_envPreV11C, f, &args2, &args3);
5512 _RPT1(0, "ScriptEnvironment::Invoke after funcCtor make unique %s\r\n", name);
5513
5514 // args2 and args3 are not valid after this point anymore
5515
5516 bool is_mtmode_forced;
5517
1/2
✓ Branch 474 → 475 taken 7 times.
✗ Branch 474 → 1157 not taken.
7 bool filterHasSpecialMT = this->GetFilterMTMode(f, &is_mtmode_forced) == MT_SPECIAL_MT;
5518
5519 7 bool instantiated_clip_unaltered = false;
5520 #ifdef _DEBUG
5521 bool cache_guard_called = false;
5522 #endif
5523
5524
1/2
✗ Branch 475 → 476 not taken.
✓ Branch 475 → 540 taken 7 times.
7 if (filterHasSpecialMT) // pre-avs 3.6 workaround for MP_Pipeline
5525 {
5526 *result = funcCtor->InstantiateFilter();
5527 #ifdef _DEBUG
5528 _RPT1(0, "ScriptEnvironment::Invoke done funcCtor->InstantiateFilter %s\r\n", name);
5529 #endif
5530 if ((*result).IsClip()) {
5531 // PropPassthrough: forward input frame properties for old filters that drop them.
5532 // Inserted before SetFilterProp entries so specific injections override inherited props.
5533 if (prop_passthrough_src)
5534 *result = new PropPassthrough((*result).AsClip(), prop_passthrough_src, threadEnv.get());
5535 // property injection
5536 if (prop_entries_to_inject != nullptr) {
5537 for (const auto& entry : *prop_entries_to_inject) {
5538 if (entry.key.empty()) continue;
5539 if (!entry.value.Defined()) continue;
5540 const char* saved_key = threadEnv->SaveString(entry.key.c_str());
5541 AVSValue prop_args[4] = { *result, AVSValue(saved_key), entry.value, AVSValue(entry.mode) };
5542 *result = SetProperty::Create(AVSValue(prop_args, 4), (void*)0, threadEnv.get());
5543 }
5544 }
5545 }
5546 }
5547
2/4
✓ Branch 541 → 542 taken 7 times.
✗ Branch 541 → 1157 not taken.
✗ Branch 542 → 543 not taken.
✓ Branch 542 → 613 taken 7 times.
7 else if (funcCtor->IsScriptFunction())
5548 {
5549 // Eval, EvalOop, Import and user defined script functions
5550 // Warn user if he set an MT-mode for a script function
5551 if (this->FilterHasMtMode(f))
5552 {
5553 OneTimeLogTicket ticket(LOGTICKET_W1010, f);
5554 LogMsgOnce(ticket, LOGLEVEL_WARNING, "An MT-mode is set for %s() but it is a script function. You can only set the MT-mode for binary filters, for scripted functions it will be ignored.", f->name);
5555 }
5556
5557 *result = funcCtor->InstantiateFilter();
5558 #ifdef _DEBUG
5559 _RPT1(0, "ScriptEnvironment::Invoke done funcCtor->InstantiateFilter %s\r\n", name);
5560 #endif
5561 if ((*result).IsClip()) {
5562 if (prop_passthrough_src)
5563 *result = new PropPassthrough((*result).AsClip(), prop_passthrough_src, threadEnv.get());
5564 if (prop_entries_to_inject != nullptr) {
5565 for (const auto& entry : *prop_entries_to_inject) {
5566 if (entry.key.empty()) continue;
5567 if (!entry.value.Defined()) continue;
5568 const char* saved_key = threadEnv->SaveString(entry.key.c_str());
5569 AVSValue prop_args[4] = { *result, AVSValue(saved_key), entry.value, AVSValue(entry.mode) };
5570 *result = SetProperty::Create(AVSValue(prop_args, 4), (void*)0, threadEnv.get());
5571 }
5572 }
5573 }
5574 }
5575 else
5576 {
5577 #ifdef _DEBUG
5578 AvsCache *PrevFrontCache = FrontCache;
5579 #endif
5580
5581
1/2
✓ Branch 613 → 614 taken 7 times.
✗ Branch 613 → 1156 not taken.
7 AVSValue fret;
5582
5583
1/2
✓ Branch 614 → 615 taken 7 times.
✗ Branch 614 → 1038 not taken.
7 invoke_stack.push(&mthelper);
5584 try
5585 {
5586
2/4
✓ Branch 616 → 617 taken 7 times.
✗ Branch 616 → 1041 not taken.
✓ Branch 617 → 618 taken 7 times.
✗ Branch 617 → 1039 not taken.
7 fret = funcCtor->InstantiateFilter();
5587
5588 // Detect whether one of the parameter clip was returned unaltered.
5589 // Introduce bool instantiated_clip_unaltered.
5590
2/4
✓ Branch 619 → 620 taken 7 times.
✗ Branch 619 → 1043 not taken.
✓ Branch 620 → 621 taken 7 times.
✗ Branch 620 → 641 not taken.
7 if (fret.IsClip()) {
5591
1/2
✓ Branch 621 → 622 taken 7 times.
✗ Branch 621 → 1042 not taken.
14 const PClip& clip = fret.AsClip();
5592 7 IClip* clip_raw = (IClip*)((void*)clip);
5593
2/2
✓ Branch 638 → 625 taken 7 times.
✓ Branch 638 → 639 taken 6 times.
20 for (auto comparewith : clip_parameters_raw) {
5594
2/2
✓ Branch 627 → 628 taken 1 time.
✓ Branch 627 → 629 taken 6 times.
7 if (comparewith == clip_raw) {
5595 _RPT1(0, "ScriptEnvironment::Invoke funcCtor->InstantiateFilter %s returned the very same clip unaltered that was in a parameter\r\n", name);
5596 1 instantiated_clip_unaltered = true;
5597 1 break;
5598 }
5599 }
5600 }
5601
5602
1/2
✓ Branch 641 → 642 taken 7 times.
✗ Branch 641 → 1043 not taken.
7 invoke_stack.pop();
5603 }
5604 catch (...)
5605 {
5606 // comment: Issue20200818
5607 invoke_stack.pop();
5608 throw;
5609 }
5610
5611 // Determine MT-mode, as if this instance had not called Invoke()
5612 // in its constructor. Note that this is not necessary the final
5613 // MT-mode.
5614 // PF 161012 hack(?) don't call if prefetch. If effective mt mode is MT_MULTI, then
5615 // Prefetch create gets called again
5616 // Prefetch is activated above in: fret = funcCtor->InstantiateFilter();
5617
5/10
✓ Branch 642 → 643 taken 7 times.
✗ Branch 642 → 1154 not taken.
✓ Branch 643 → 644 taken 7 times.
✗ Branch 643 → 647 not taken.
✓ Branch 644 → 645 taken 7 times.
✗ Branch 644 → 646 not taken.
✓ Branch 645 → 646 taken 7 times.
✗ Branch 645 → 647 not taken.
✓ Branch 648 → 649 taken 7 times.
✗ Branch 648 → 823 not taken.
7 if (fret.IsClip() && (f->name == nullptr || strcmp(f->name, "Prefetch")))
5618 {
5619
1/2
✓ Branch 649 → 650 taken 7 times.
✗ Branch 649 → 1135 not taken.
14 const PClip &clip = fret.AsClip();
5620
5621 bool is_mtmode_forced;
5622
1/2
✓ Branch 650 → 651 taken 7 times.
✗ Branch 650 → 1133 not taken.
7 this->GetFilterMTMode(f, &is_mtmode_forced);
5623
1/2
✓ Branch 652 → 653 taken 7 times.
✗ Branch 652 → 1133 not taken.
7 MtMode mtmode = MtModeEvaluator::GetMtMode(clip, f, threadEnv.get());
5624
5625
1/2
✗ Branch 653 → 654 not taken.
✓ Branch 653 → 659 taken 7 times.
7 if (chainedCtor)
5626 {
5627 // Propagate information about our children's MT-safety
5628 // to our parent.
5629 invoke_stack.top()->Accumulate(mthelper);
5630
5631 // Add our own MT-mode's information to the parent.
5632 invoke_stack.top()->Accumulate(mtmode);
5633
5634 *result = fret;
5635 }
5636 else
5637 {
5638
1/2
✓ Branch 659 → 660 taken 7 times.
✗ Branch 659 → 661 not taken.
7 if (!is_mtmode_forced) {
5639 7 mtmode = mthelper.GetFinalMode(mtmode);
5640 }
5641
5642 // Special handling for source filters
5643 7 if (isSourceFilter
5644 && MtModeEvaluator::UsesDefaultMtMode(clip, f, threadEnv.get())
5645
2/6
✗ Branch 661 → 662 not taken.
✓ Branch 661 → 667 taken 7 times.
✗ Branch 665 → 666 not taken.
✗ Branch 665 → 667 not taken.
✗ Branch 668 → 669 not taken.
✓ Branch 668 → 673 taken 7 times.
7 && (MT_SERIALIZED != mtmode))
5646 {
5647 mtmode = MT_SERIALIZED;
5648 OneTimeLogTicket ticket(LOGTICKET_W1001, f);
5649 LogMsgOnce(ticket, LOGLEVEL_INFO, "%s() does not have any MT-mode specification. Because it is a source filter, it will use MT_SERIALIZED instead of the default MT mode.", f->canon_name);
5650 }
5651
5652 // pass current directory as well, in order to remember it when a MT_MULTI_INSTANCE filter is populated to threads during Prefetch
5653
1/2
✓ Branch 673 → 674 taken 7 times.
✗ Branch 673 → 1069 not taken.
7 auto current_directory = CWDChanger::GetCurrentWorkingDirectory(); // wstring on Windows, string on POSIX
5654
5655 // Do dot create MTGuard on an already existing clip
5656
2/2
✓ Branch 674 → 675 taken 6 times.
✓ Branch 674 → 691 taken 1 time.
7 if (!instantiated_clip_unaltered) {
5657
4/8
✓ Branch 680 → 681 taken 6 times.
✗ Branch 680 → 1058 not taken.
✓ Branch 681 → 682 taken 6 times.
✗ Branch 681 → 1056 not taken.
✓ Branch 682 → 683 taken 6 times.
✗ Branch 682 → 1054 not taken.
✓ Branch 683 → 684 taken 6 times.
✗ Branch 683 → 1052 not taken.
12 *result = MTGuard::Create(mtmode, clip, std::move(funcCtor), current_directory.c_str(), threadEnv.get());
5658
5659 6 IClip *clip_raw = (IClip*)((void*)clip);
5660
1/2
✓ Branch 689 → 690 taken 6 times.
✗ Branch 689 → 1067 not taken.
6 ClipDataStore *data = this->ClipData(clip_raw);
5661 6 data->CreatedByInvoke = true;
5662 }
5663 else {
5664
2/4
✓ Branch 691 → 692 taken 1 time.
✗ Branch 691 → 1066 not taken.
✓ Branch 692 → 693 taken 1 time.
✗ Branch 692 → 1064 not taken.
1 *result = clip;
5665 }
5666 7 } // if (chainedCtor)
5667
5668 // Nekopanda: moved here from above.
5669 // some filters invoke complex filters in its constructor, and they need cache.
5670 // pf: Do not introduce (another) cache on an existing PClip
5671
2/2
✓ Branch 697 → 698 taken 6 times.
✓ Branch 697 → 761 taken 1 time.
7 if (!instantiated_clip_unaltered) {
5672
2/8
✓ Branch 698 → 699 taken 6 times.
✗ Branch 698 → 1070 not taken.
✓ Branch 699 → 700 taken 6 times.
✗ Branch 699 → 1070 not taken.
✗ Branch 1070 → 1071 not taken.
✗ Branch 1070 → 1074 not taken.
✗ Branch 1072 → 1073 not taken.
✗ Branch 1072 → 1074 not taken.
24 AVSValue args_cacheguard[2]{ *result, f->name };
5673
3/6
✓ Branch 701 → 702 taken 6 times.
✗ Branch 701 → 1079 not taken.
✓ Branch 702 → 703 taken 6 times.
✗ Branch 702 → 1077 not taken.
✓ Branch 703 → 704 taken 6 times.
✗ Branch 703 → 1075 not taken.
6 *result = CacheGuard::Create(AVSValue(args_cacheguard, 2), NULL, threadEnv.get());
5674 #ifdef _DEBUG
5675 cache_guard_called = true;
5676 #endif
5677
5678 // Check that the filter returns zero for unknown queries in SetCacheHints().
5679 // This is actually something we rely upon.
5680
5/10
✓ Branch 707 → 708 taken 6 times.
✗ Branch 707 → 1093 not taken.
✓ Branch 708 → 709 taken 6 times.
✗ Branch 708 → 713 not taken.
✓ Branch 710 → 711 taken 6 times.
✗ Branch 710 → 1093 not taken.
✗ Branch 711 → 712 not taken.
✓ Branch 711 → 713 taken 6 times.
✗ Branch 714 → 715 not taken.
✓ Branch 714 → 719 taken 6 times.
6 if ((clip->GetVersion() >= 5) && (0 != clip->SetCacheHints(CACHE_USER_CONSTANTS, 0)))
5681 {
5682 OneTimeLogTicket ticket(LOGTICKET_W1002, f);
5683 LogMsgOnce(ticket, LOGLEVEL_WARNING, "%s() violates semantic contracts and may cause undefined behavior. Please inform the author of the plugin.", f->canon_name);
5684 }
5685
5686 // Warn user if the MT-mode of this filter is unknown
5687
6/8
✓ Branch 720 → 721 taken 6 times.
✗ Branch 720 → 1093 not taken.
✓ Branch 721 → 722 taken 1 time.
✓ Branch 721 → 724 taken 5 times.
✓ Branch 722 → 723 taken 1 time.
✗ Branch 722 → 724 not taken.
✓ Branch 725 → 726 taken 1 time.
✓ Branch 725 → 730 taken 5 times.
6 if (MtModeEvaluator::UsesDefaultMtMode(clip, f, threadEnv.get()) && !isSourceFilter)
5688 {
5689 1 OneTimeLogTicket ticket(LOGTICKET_W1004, f);
5690
1/2
✓ Branch 727 → 728 taken 1 time.
✗ Branch 727 → 1084 not taken.
1 LogMsgOnce(ticket, LOGLEVEL_WARNING, "%s() has no MT-mode set and will use the default MT-mode. This might be dangerous.", f->canon_name);
5691 1 }
5692
5693 // Warn user if he forced an MT-mode that differs from the one specified by the filter itself
5694 6 if (is_mtmode_forced
5695 && MtModeEvaluator::ClipSpecifiesMtMode(clip)
5696
2/8
✗ Branch 730 → 731 not taken.
✓ Branch 730 → 736 taken 6 times.
✗ Branch 733 → 734 not taken.
✗ Branch 733 → 1093 not taken.
✗ Branch 734 → 735 not taken.
✗ Branch 734 → 736 not taken.
✗ Branch 737 → 738 not taken.
✓ Branch 737 → 742 taken 6 times.
6 && MtModeEvaluator::GetInstanceMode(clip) != mtmode)
5697 {
5698 OneTimeLogTicket ticket(LOGTICKET_W1005, f);
5699 LogMsgOnce(ticket, LOGLEVEL_WARNING, "%s() specifies an MT-mode for itself, but a script forced a different one. Either the plugin or the script is erronous.", f->canon_name);
5700 }
5701
5702 // Inform user if a script unnecessarily specifies an MT-mode for this filter
5703 12 if (!is_mtmode_forced
5704
2/4
✓ Branch 743 → 744 taken 6 times.
✗ Branch 743 → 1093 not taken.
✗ Branch 744 → 745 not taken.
✓ Branch 744 → 748 taken 6 times.
6 && this->FilterHasMtMode(f)
5705
2/8
✓ Branch 742 → 743 taken 6 times.
✗ Branch 742 → 748 not taken.
✗ Branch 745 → 746 not taken.
✗ Branch 745 → 1093 not taken.
✗ Branch 746 → 747 not taken.
✗ Branch 746 → 748 not taken.
✗ Branch 749 → 750 not taken.
✓ Branch 749 → 754 taken 6 times.
12 && MtModeEvaluator::ClipSpecifiesMtMode(clip))
5706 {
5707 OneTimeLogTicket ticket(LOGTICKET_W1006, f);
5708 LogMsgOnce(ticket, LOGLEVEL_INFO, "Ignoring unnecessary MT-mode specification for %s() by script.", f->canon_name);
5709 }
5710
2/4
✓ Branch 755 → 756 taken 12 times.
✓ Branch 755 → 759 taken 6 times.
✗ Branch 1094 → 1095 not taken.
✗ Branch 1094 → 1098 not taken.
18 }
5711
5712 // Auto-inject registered frame properties for this filter, outside !instantiated_clip_unaltered:
5713 // rules apply even when the filter returned a parameter clip unaltered.
5714 // PropPassthrough wraps first so specific SetFilterProp entries override inherited props.
5715
2/4
✓ Branch 761 → 762 taken 7 times.
✗ Branch 761 → 1133 not taken.
✓ Branch 762 → 763 taken 7 times.
✗ Branch 762 → 821 not taken.
7 if ((*result).IsClip()) {
5716
1/2
✗ Branch 764 → 765 not taken.
✓ Branch 764 → 778 taken 7 times.
7 if (prop_passthrough_src)
5717 *result = new PropPassthrough((*result).AsClip(), prop_passthrough_src, threadEnv.get());
5718 // prop_entries_to_inject was resolved before InstantiateFilter(); undefined entries are
5719 // ones where capture-from-param failed (no matching named arg) and are skipped here.
5720
1/2
✗ Branch 778 → 779 not taken.
✓ Branch 778 → 821 taken 7 times.
7 if (prop_entries_to_inject != nullptr) {
5721 for (const auto& entry : *prop_entries_to_inject) {
5722 if (entry.key.empty()) continue; // conditional entry: param did not match
5723 if (!entry.value.Defined()) continue; // capture-from-param: no named arg matched
5724 const char* saved_key = threadEnv->SaveString(entry.key.c_str());
5725 AVSValue prop_args[4] = { *result, AVSValue(saved_key), entry.value, AVSValue(entry.mode) };
5726 *result = SetProperty::Create(AVSValue(prop_args, 4), (void*)0, threadEnv.get());
5727 }
5728 }
5729 }
5730
5731 } // if (fret.IsClip())
5732 else
5733 {
5734 *result = fret;
5735 }
5736
5737 // static device check
5738 // this is not enough to check all dependencies but much helpful to users
5739
2/4
✓ Branch 824 → 825 taken 7 times.
✗ Branch 824 → 1154 not taken.
✓ Branch 825 → 826 taken 7 times.
✗ Branch 825 → 835 not taken.
7 if ((*result).IsClip()) {
5740
2/6
✗ Branch 826 → 827 not taken.
✓ Branch 826 → 828 taken 7 times.
✗ Branch 827 → 829 not taken.
✗ Branch 827 → 1141 not taken.
✓ Branch 828 → 829 taken 7 times.
✗ Branch 828 → 1141 not taken.
7 auto last = (argbase == 0) ? implicit_last : AVSValue();
5741
2/4
✓ Branch 830 → 831 taken 7 times.
✗ Branch 830 → 1138 not taken.
✓ Branch 831 → 832 taken 7 times.
✗ Branch 831 → 1136 not taken.
7 CheckChildDeviceTypes((*result).AsClip(), name, last, args, arg_names, threadEnv.get());
5742 7 }
5743
5744 // filter graph
5745
2/8
✗ Branch 835 → 836 not taken.
✓ Branch 835 → 839 taken 7 times.
✗ Branch 836 → 837 not taken.
✗ Branch 836 → 1154 not taken.
✗ Branch 837 → 838 not taken.
✗ Branch 837 → 839 not taken.
✗ Branch 840 → 841 not taken.
✓ Branch 840 → 856 taken 7 times.
7 if (graphAnalysisEnable && (*result).IsClip()) {
5746 auto last = (argbase == 0) ? implicit_last : AVSValue();
5747 *result = new FilterGraphNode((*result).AsClip(), f->name, last, args, arg_names, threadEnv.get());
5748 }
5749
5750 #ifdef _DEBUG
5751 if (PrevFrontCache != FrontCache && FrontCache != NULL) // cache registering swaps frontcache to the current
5752 {
5753 std::string real_function_name = name;
5754 if (!cache_guard_called) {
5755 real_function_name = "Unknow_Function_Invoked_By_No-Op_" + real_function_name + "_Create";
5756 _RPT2(0, "ScriptEnvironment::Invoke nearEnd: FrontCache altered, (but no direct AvsCache call) FuncName=%s cache_id=%p\r\n", real_function_name.c_str(), (void*)FrontCache);
5757 }
5758 else {
5759 _RPT2(0, "ScriptEnvironment::Invoke nearEnd: FrontCache altered. FuncName=%s cache_id=%p\r\n", real_function_name.c_str(), (void*)FrontCache);
5760 }
5761 FrontCache->FuncName = real_function_name; // helps debugging. See also in cache.cpp
5762 }
5763 _RPT1(0, "ScriptEnvironment::Invoke done %s\r\n", name);
5764 #endif
5765 7 }
5766
5767 7 return true;
5768 7 }
5769
5770
5771 bool ScriptEnvironment::FunctionExists(const char* name)
5772 {
5773 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
5774
5775 // Look among variable table
5776 AVSValue result;
5777 if (threadEnv->GetVarTry(name, &result)) {
5778 if (result.IsFunction()) {
5779 return true;
5780 }
5781 }
5782
5783 // Look among internal functions
5784 if (InternalFunctionExists(name))
5785 return true;
5786
5787 // Look among plugin functions
5788 if (plugin_manager->FunctionExists(name))
5789 return true;
5790
5791 // Uhh... maybe if we load the plugins we'll have the function
5792 if (!plugin_manager->HasAutoloadExecuted())
5793 {
5794 plugin_manager->AutoloadPlugins();
5795 return this->FunctionExists(name);
5796 }
5797
5798 return false;
5799 }
5800
5801 bool ScriptEnvironment::InternalFunctionExists(const char* name)
5802 {
5803 for (int i = 0; i < sizeof(builtin_functions)/sizeof(builtin_functions[0]); ++i)
5804 for (const AVSFunction* j = builtin_functions[i]; !j->empty(); ++j)
5805 if (streqi(j->name, name))
5806 return true;
5807
5808 return false;
5809 }
5810
5811 520 void ScriptEnvironment::BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) {
5812
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 520 times.
520 if (height<0)
5813 ThrowError("Filter Error: Attempting to blit an image with negative height.");
5814
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 520 times.
520 if (row_size<0)
5815 ThrowError("Filter Error: Attempting to blit an image with negative row size.");
5816 520 ::BitBlt(dstp, dst_pitch, srcp, src_pitch, row_size, height);
5817 520 }
5818
5819 void ScriptEnvironment::ThrowError(const char* fmt, ...)
5820 {
5821 va_list val;
5822 va_start(val, fmt);
5823 threadEnv->VThrowError(fmt, val);
5824 va_end(val);
5825 }
5826
5827 void ScriptEnvironment::VThrowError(const char* fmt, va_list va)
5828 {
5829 threadEnv->VThrowError(fmt, va);
5830 }
5831
5832 // since IF V8 it moved from IScriptEnvironment2 to IScriptEnvironment
5833 3 PVideoFrame ScriptEnvironment::SubframePlanarA(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA)
5834 {
5835
2/4
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 10 not taken.
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 8 not taken.
3 return SubframePlanar(src, rel_offset, new_pitch, new_row_size, new_height, rel_offsetU, rel_offsetV, new_pitchUV, rel_offsetA);
5836 }
5837
5838 bool ScriptEnvironment::MakePropertyWritable(PVideoFrame* pvf)
5839 {
5840 const PVideoFrame& vf = *pvf;
5841
5842 // If the frame is already writable, do nothing.
5843 if (vf->IsPropertyWritable())
5844 return false;
5845
5846 // Otherwise, allocate a new frame (using Subframe)
5847 // Thus we avoid the frame-content copy overhead and still get a new frame with its unique frameprop
5848 VideoFrame *dst;
5849 if (vf->GetPitch(PLANAR_A)) {
5850 // planar + alpha
5851 dst = vf->Subframe(0, vf->GetPitch(), vf->GetRowSize(), vf->GetHeight(), 0, 0, vf->GetPitch(PLANAR_U), 0);
5852 }
5853 else if (vf->GetPitch(PLANAR_U)) {
5854 // planar
5855 dst = vf->Subframe(0, vf->GetPitch(), vf->GetRowSize(), vf->GetHeight(), 0, 0, vf->GetPitch(PLANAR_U));
5856 }
5857 else {
5858 // single plane
5859 dst = vf->Subframe(0, vf->GetPitch(), vf->GetRowSize(), vf->GetHeight());
5860 }
5861
5862 const AVSMap& avsmap = vf->getConstProperties();
5863 if (propNumKeys(&avsmap) > 0)
5864 dst->setProperties(avsmap);
5865
5866 size_t vfb_size = vf->GetFrameBuffer()->GetDataSize();
5867
5868 // vector and maps needs locking!
5869 std::unique_lock<std::recursive_mutex> env_lock(memory_mutex);
5870 assert(dst != NULL);
5871
5872 RegisterSubFrameInRegistry(vfb_size, vf->GetFrameBuffer(), dst);
5873
5874 *pvf = dst;
5875 return true;
5876 }
5877
5878 // since IF V8 frame property helpers are part of IScriptEnvironment
5879 276 void ScriptEnvironment::copyFrameProps(const PVideoFrame& src, PVideoFrame& dst)
5880 {
5881 276 dst->setProperties(src->getProperties());
5882 276 }
5883
5884 // frame properties support
5885 // core imported from VapourSynth
5886 // from vsapi.cpp
5887 48 const AVSMap* ScriptEnvironment::getFramePropsRO(const PVideoFrame& frame) AVS_NOEXCEPT {
5888
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 48 times.
48 assert(frame);
5889 48 return &(frame->getConstProperties());
5890 }
5891
5892 60 AVSMap* ScriptEnvironment::getFramePropsRW(PVideoFrame &frame) AVS_NOEXCEPT {
5893
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 60 times.
60 assert(frame);
5894 60 return &(frame->getProperties());
5895 }
5896
5897 50 int ScriptEnvironment::propNumKeys(const AVSMap* map) AVS_NOEXCEPT {
5898
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 50 times.
50 assert(map);
5899 50 return static_cast<int>(map->size());
5900 }
5901
5902 1 const char* ScriptEnvironment::propGetKey(const AVSMap* map, int index) AVS_NOEXCEPT {
5903
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1 time.
1 assert(map);
5904
3/6
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 7 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1 time.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 26 taken 1 time.
1 if (index < 0 || static_cast<size_t>(index) >= map->size())
5905 ThrowError(("propGetKey: Out of bounds index " + std::to_string(index) + " passed. Valid range: [0," + std::to_string(map->size() - 1) + "]").c_str());
5906
5907 1 return map->key(index);
5908 }
5909
5910 77 int ScriptEnvironment::propNumElements(const AVSMap* map, const char* key) AVS_NOEXCEPT {
5911
2/4
✓ Branch 2 → 3 taken 77 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 77 times.
✗ Branch 3 → 5 not taken.
77 assert(map && key);
5912 154 VSArrayBase* val = map->find(key);
5913
2/2
✓ Branch 12 → 13 taken 29 times.
✓ Branch 12 → 15 taken 48 times.
77 return val ? static_cast<int>(val->size()) : -1;
5914 }
5915
5916 1 char ScriptEnvironment::propGetType(const AVSMap* map, const char* key) AVS_NOEXCEPT {
5917
2/4
✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 5 not taken.
1 assert(map && key);
5918 1 const char a[] = { 'u', 'i', 'f', 's', 'm', 'c', '?', 'v', '?' };
5919 /*
5920 u PROPERTYTYPE_UNSET = 0, // ptUnset = 0,
5921 i PROPERTYTYPE_INT = 1, // ptInt = 1,
5922 f PROPERTYTYPE_FLOAT = 2, // ptFloat = 2,
5923 s PROPERTYTYPE_DATA = 3, // ptData = 3,
5924 m // ptFunction = 4,
5925 c PROPERTYTYPE_CLIP = 5, // ptVideoNode = 5,
5926 ? // ptAudioNode = 6,
5927 v PROPERTYTYPE_FRAME = 7, // ptVideoFrame = 7,
5928 ? // ptAudioFrame = 8
5929 */
5930 2 VSArrayBase* val = map->find(key);
5931
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 15 not taken.
1 return val ? a[val->type()] : 'u';
5932 }
5933
5934 34 int ScriptEnvironment::propDeleteKey(AVSMap* map, const char* key) AVS_NOEXCEPT {
5935
2/4
✓ Branch 2 → 3 taken 34 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 34 times.
✗ Branch 3 → 5 not taken.
34 assert(map && key);
5936 102 return map->erase(key);
5937 }
5938
5939 42 static VSArrayBase* propGetShared(const AVSMap* map, const char* key, int index, int* error, AVSPropertyType propType, ScriptEnvironment *env) noexcept {
5940
3/6
✓ Branch 2 → 3 taken 42 times.
✗ Branch 2 → 6 not taken.
✓ Branch 3 → 4 taken 42 times.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 42 times.
✗ Branch 4 → 6 not taken.
42 assert(map && key && index >= 0);
5941
5942
1/2
✓ Branch 7 → 8 taken 42 times.
✗ Branch 7 → 9 not taken.
42 if (error)
5943 42 *error = AVSGetPropErrors::GETPROPERROR_SUCCESS; // peSuccess;
5944
5945
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 24 taken 42 times.
42 if (map->hasError()) {
5946 if (error)
5947 *error = AVSGetPropErrors::GETPROPERROR_ERROR; // peError;
5948 else
5949 env->ThrowError(("Property read unsuccessful on map with error set but no error output: " + std::string(key)).c_str());
5950 return nullptr;
5951 }
5952
5953 84 VSArrayBase* arr = map->find(key);
5954
5955
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 44 taken 42 times.
42 if (!arr) {
5956 if (error)
5957 *error = AVSGetPropErrors::GETPROPERROR_UNSET; // peUnset;
5958 else
5959 env->ThrowError(("Property read unsuccessful due to missing key but no error output: " + std::string(key)).c_str());
5960 return nullptr;
5961 }
5962
5963
3/6
✓ Branch 44 → 45 taken 42 times.
✗ Branch 44 → 47 not taken.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 48 taken 42 times.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 63 taken 42 times.
42 if (index < 0 || index >= static_cast<int>(arr->size())) {
5964 if (error)
5965 *error = AVSGetPropErrors::GETPROPERROR_INDEX; // peIndex;
5966 else
5967 env->ThrowError(("Property read unsuccessful due to out of bounds index but no error output: " + std::string(key)).c_str());
5968 return nullptr;
5969 }
5970
5971
1/2
✗ Branch 64 → 65 not taken.
✓ Branch 64 → 78 taken 42 times.
42 if (arr->type() != propType) {
5972 if (error)
5973 *error = AVSGetPropErrors::GETPROPERROR_TYPE; // peType;
5974 else
5975 env->ThrowError(("Property read unsuccessful due to wrong type but no error output: " + std::string(key)).c_str());
5976 return nullptr;
5977 }
5978
5979 42 return arr;
5980 }
5981
5982 41 int64_t ScriptEnvironment::propGetInt(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
5983 41 VSArrayBase* arr = propGetShared(map, key, index, error, AVSPropertyType::PROPERTYTYPE_INT, this); // ptInt
5984
1/2
✓ Branch 3 → 4 taken 41 times.
✗ Branch 3 → 6 not taken.
41 if (arr)
5985 41 return reinterpret_cast<const VSIntArray*>(arr)->at(index);
5986 else
5987 return 0;
5988 }
5989
5990 20 int ScriptEnvironment::propGetIntSaturated(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
5991 20 int internalError = 0;
5992 20 int64_t value = propGetInt(map, key, index, &internalError);
5993
5994
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 20 times.
20 if (error) {
5995 *error = internalError;
5996 }
5997
5998
1/2
✓ Branch 5 → 6 taken 20 times.
✗ Branch 5 → 12 not taken.
20 if (internalError == 0) {
5999
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 20 times.
20 if (value > std::numeric_limits<int>::max()) {
6000 return std::numeric_limits<int>::max();
6001 }
6002
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 20 times.
20 if (value < std::numeric_limits<int>::min()) {
6003 return std::numeric_limits<int>::min();
6004 }
6005 }
6006
6007 20 return static_cast<int>(value);
6008 }
6009
6010 double ScriptEnvironment::propGetFloat(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6011 VSArrayBase* arr = propGetShared(map, key, index, error, AVSPropertyType::PROPERTYTYPE_FLOAT, this); // ptFloat
6012 if (arr)
6013 return reinterpret_cast<const VSFloatArray*>(arr)->at(index);
6014 else
6015 return 0;
6016 }
6017
6018 float ScriptEnvironment::propGetFloatSaturated(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6019 int internalError = 0;
6020 double value = propGetFloat(map, key, index, &internalError);
6021
6022 if (error) {
6023 *error = internalError;
6024 }
6025
6026 if (internalError == 0) {
6027 if (std::isnan(value)) {
6028 return std::numeric_limits<float>::quiet_NaN();
6029 }
6030 if (value > std::numeric_limits<float>::max()) {
6031 return std::numeric_limits<float>::max();
6032 }
6033 if (value < -std::numeric_limits<float>::max()) {
6034 return -std::numeric_limits<float>::max();
6035 }
6036 }
6037
6038 return static_cast<float>(value);
6039 }
6040
6041 1 const char* ScriptEnvironment::propGetData(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6042 1 VSArrayBase* arr = propGetShared(map, key, index, error, AVSPropertyType::PROPERTYTYPE_DATA, this); // ptData
6043
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 6 not taken.
1 if (arr)
6044 1 return reinterpret_cast<const VSDataArray*>(arr)->at(index).data.c_str();
6045 else
6046 return nullptr;
6047 }
6048
6049 int ScriptEnvironment::propGetDataSize(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6050 VSArrayBase* arr = propGetShared(map, key, index, error, AVSPropertyType::PROPERTYTYPE_DATA, this); // ptData
6051 if (arr)
6052 return static_cast<int>(reinterpret_cast<const VSDataArray*>(arr)->at(index).data.size());
6053 else
6054 return -1;
6055 }
6056
6057 int ScriptEnvironment::propGetDataTypeHint(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6058 VSArrayBase* arr = propGetShared(map, key, index, error, AVSPropertyType::PROPERTYTYPE_DATA, this); // ptData
6059 if (arr)
6060 return reinterpret_cast<const VSDataArray*>(arr)->at(index).typeHint;
6061 else
6062 return AVSPropDataTypeHint::PROPDATATYPEHINT_UNKNOWN; // dtUnknown;
6063 }
6064
6065 PClip ScriptEnvironment::propGetClip(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6066 int dummyError;
6067 VSArrayBase* arr = propGetShared(map, key, index, &dummyError, AVSPropertyType::PROPERTYTYPE_CLIP, this); // ptVideoNode
6068 if (arr) {
6069 // PClip itself is reference counted
6070 PClip ref = reinterpret_cast<VSVideoNodeArray*>(arr)->at(index);
6071 return ref;
6072 }
6073 else {
6074 // no separate audio node in AVS
6075 return nullptr;
6076 }
6077 }
6078
6079 const PVideoFrame ScriptEnvironment::propGetFrame(const AVSMap* map, const char* key, int index, int* error) AVS_NOEXCEPT {
6080 // PVideoFrame itself is reference counted
6081 int dummyError;
6082 VSArrayBase* arr = propGetShared(map, key, index, &dummyError, AVSPropertyType::PROPERTYTYPE_FRAME, this); // ptVideoFrame
6083 if (arr) {
6084 PVideoFrame ref = reinterpret_cast<VSVideoFrameArray*>(arr)->at(index);
6085 return ref;
6086 }
6087 else {
6088 // no separate audio frame in AVS
6089 return nullptr;
6090 }
6091 }
6092
6093 82 static inline bool isAlphaUnderscore(char c) {
6094
5/10
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 82 times.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 7 not taken.
✓ Branch 4 → 5 taken 82 times.
✗ Branch 4 → 6 not taken.
✓ Branch 5 → 6 taken 79 times.
✓ Branch 5 → 7 taken 3 times.
✓ Branch 6 → 7 taken 79 times.
✗ Branch 6 → 8 not taken.
82 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
6095 }
6096
6097 743 static inline bool isAlphaNumUnderscore(char c) {
6098
8/14
✓ Branch 2 → 3 taken 603 times.
✓ Branch 2 → 4 taken 140 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 9 taken 603 times.
✓ Branch 4 → 5 taken 137 times.
✓ Branch 4 → 6 taken 3 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 9 taken 137 times.
✓ Branch 6 → 7 taken 3 times.
✗ Branch 6 → 8 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 3 times.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 10 not taken.
743 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
6099 }
6100
6101 82 static bool isValidVSMapKey(const std::string& s) {
6102 82 size_t len = s.length();
6103
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 82 times.
82 if (!len)
6104 return false;
6105
6106
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 82 times.
82 if (!isAlphaUnderscore(s[0]))
6107 return false;
6108
2/2
✓ Branch 15 → 10 taken 743 times.
✓ Branch 15 → 16 taken 82 times.
825 for (size_t i = 1; i < len; i++)
6109
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 743 times.
743 if (!isAlphaNumUnderscore(s[i]))
6110 return false;
6111 82 return true;
6112 }
6113
6114 static int mapSetEmpty(AVSMap* map, const char* key, int type) AVS_NOEXCEPT {
6115 assert(map && key);
6116 if (!isValidVSMapKey(key))
6117 return 1;
6118
6119 std::string skey = key;
6120 if (map->find(skey))
6121 return 1;
6122
6123 switch (type) {
6124 case AVSPropertyType::PROPERTYTYPE_INT: // ptInt:
6125 map->insert(key, new VSIntArray);
6126 break;
6127 case AVSPropertyType::PROPERTYTYPE_FLOAT: // ptFloat:
6128 map->insert(key, new VSFloatArray);
6129 break;
6130 case AVSPropertyType::PROPERTYTYPE_DATA: // ptData:
6131 map->insert(key, new VSDataArray);
6132 break;
6133 case AVSPropertyType::PROPERTYTYPE_CLIP: // ptVideoNode:
6134 map->insert(key, new VSVideoNodeArray);
6135 break;
6136 /*
6137 case ptAudioNode:
6138 map->insert(key, new VSAudioNodeArray);
6139 break;
6140 */
6141 case AVSPropertyType::PROPERTYTYPE_FRAME: // ptVideoFrame:
6142 map->insert(key, new VSVideoFrameArray);
6143 break;
6144 /*
6145 case ptAudioFrame:
6146 map->insert(key, new VSAudioFrameArray);
6147 break;
6148 */
6149 /*
6150 case AVSPropertyType::PROPERTYTYPE_FUNCTION: // ptFunction:
6151 map->insert(key, new VSFunctionArray);
6152 break;
6153 */
6154 default:
6155 return 1;
6156 }
6157 return 0;
6158 }
6159
6160 template<typename T, AVSPropertyType propType>
6161 82 bool propSetShared(AVSMap* map, const char* key, const T& val, int append, ScriptEnvironment *env) {
6162
4/20
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 5 not taken.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 5 not taken.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 5 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 5 not taken.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✓ Branch 2 → 3 taken 80 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 80 times.
✗ Branch 3 → 5 not taken.
82 assert(map && key);
6163
4/20
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 10 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 10 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 10 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 10 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 10 taken 1 time.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 1 time.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 10 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 10 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 10 taken 80 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 10 not taken.
82 if (append != AVSPropAppendMode::PROPAPPENDMODE_REPLACE &&
6164 append != AVSPropAppendMode::PROPAPPENDMODE_APPEND &&
6165 append != AVSPropAppendMode::PROPAPPENDMODE_TOUCH) // in VS4 this mode was dropped
6166 env->ThrowError("Invalid prop append mode given when setting key '%s'", key);
6167
6168
4/20
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 65 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 65 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 65 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 2 times.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 65 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✓ Branch 12 → 13 taken 80 times.
✗ Branch 12 → 65 not taken.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 80 times.
164 if (!isValidVSMapKey(key))
6169 return false;
6170
2/10
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 69 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 69 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 20 → 21 taken 2 times.
✗ Branch 20 → 69 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 69 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✓ Branch 20 → 21 taken 80 times.
✗ Branch 20 → 69 not taken.
82 std::string skey = key;
6171
6172
3/10
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 35 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 35 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 22 → 23 taken 1 time.
✓ Branch 22 → 35 taken 1 time.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 35 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✓ Branch 22 → 23 taken 80 times.
✗ Branch 22 → 35 not taken.
82 if (append == AVSPropAppendMode::PROPAPPENDMODE_REPLACE) {
6173
4/20
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 84 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 84 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 23 → 24 taken 1 time.
✗ Branch 23 → 84 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 1 time.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 84 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✓ Branch 23 → 24 taken 80 times.
✗ Branch 23 → 84 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 80 times.
81 VSArray<T, propType>* v = new VSArray<T, propType>();
6174 81 v->push_back(val);
6175
4/20
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 74 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 72 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 74 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 72 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 74 not taken.
✓ Branch 31 → 32 taken 1 time.
✗ Branch 31 → 72 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 74 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 72 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✓ Branch 30 → 31 taken 80 times.
✗ Branch 30 → 74 not taken.
✓ Branch 31 → 32 taken 80 times.
✗ Branch 31 → 72 not taken.
162 map->insert(key, v);
6176 81 return true;
6177 }
6178
1/10
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 60 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 60 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 60 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 60 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 60 not taken.
1 else if (append == AVSPropAppendMode::PROPAPPENDMODE_APPEND) {
6179
1/10
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 84 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 84 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 84 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 84 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 84 not taken.
1 VSArrayBase* arr = map->find(skey);
6180
3/30
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 41 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 46 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 41 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 46 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 37 → 38 taken 1 time.
✗ Branch 37 → 41 not taken.
✓ Branch 39 → 40 taken 1 time.
✗ Branch 39 → 41 not taken.
✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 46 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 41 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 46 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 41 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 46 not taken.
1 if (arr && arr->type() == propType) {
6181
1/10
bool propSetShared<PVideoFrame, (AVSPropertyType)7>(AVSMap*, char const*, PVideoFrame const&, int, ScriptEnvironment*):
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 84 not taken.
bool propSetShared<PClip, (AVSPropertyType)5>(AVSMap*, char const*, PClip const&, int, ScriptEnvironment*):
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 84 not taken.
bool propSetShared<VSMapData, (AVSPropertyType)3>(AVSMap*, char const*, VSMapData const&, int, ScriptEnvironment*):
✓ Branch 43 → 44 taken 1 time.
✗ Branch 43 → 84 not taken.
bool propSetShared<double, (AVSPropertyType)2>(AVSMap*, char const*, double const&, int, ScriptEnvironment*):
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 84 not taken.
bool propSetShared<long, (AVSPropertyType)1>(AVSMap*, char const*, long const&, int, ScriptEnvironment*):
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 84 not taken.
1 arr = map->detach(skey);
6182 1 reinterpret_cast<VSArray<T, propType>*>(arr)->push_back(val);
6183 1 return true;
6184 }
6185 else if (arr) {
6186 return false;
6187 }
6188 else {
6189 VSArray<T, propType>* v = new VSArray<T, propType>();
6190 v->push_back(val);
6191 map->insert(key, v);
6192 return true;
6193 }
6194 }
6195 else /* if (append == vs3::paTouch) */ {
6196 return !mapSetEmpty(map, key, propType);
6197 }
6198 82 }
6199
6200
6201 80 int ScriptEnvironment::propSetInt(AVSMap* map, const char* key, int64_t i, int append) AVS_NOEXCEPT {
6202 80 return !propSetShared<int64_t, AVSPropertyType::PROPERTYTYPE_INT>(map, key, i, append, this); // ptInt
6203 }
6204
6205 int ScriptEnvironment::propSetFloat(AVSMap* map, const char* key, double d, int append) AVS_NOEXCEPT {
6206 return !propSetShared<double, AVSPropertyType::PROPERTYTYPE_FLOAT>(map, key, d, append, this); // ptFloat
6207 }
6208
6209 // Unlike VS API4, Avisynth must maintain old function name, and introduce propSetDataH
6210 int ScriptEnvironment::propSetData(AVSMap* map, const char* key, const char* d, int length, int append) AVS_NOEXCEPT {
6211 return propSetDataH(map, key, d, length, AVSPropDataTypeHint::PROPDATATYPEHINT_UNKNOWN, append); // dtUnknown
6212 }
6213
6214 // v11
6215 2 int ScriptEnvironment::propSetDataH(AVSMap* map, const char* key, const char* d, int length, int type, int append) AVS_NOEXCEPT {
6216
2/4
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 15 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 18 taken 2 times.
4 return !propSetShared<VSMapData, AVSPropertyType::PROPERTYTYPE_DATA>(map, key, { static_cast<AVSPropDataTypeHint>(type), (length >= 0) ? std::string(d, length) : std::string(d) }, append, this);
6217
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 2 times.
4 }
6218
6219 int ScriptEnvironment::propSetClip(AVSMap* map, const char* key, PClip& clip, int append) AVS_NOEXCEPT {
6220 return !propSetShared<PClip, AVSPropertyType::PROPERTYTYPE_CLIP>(map, key, clip, append, this); // ptVideoNode
6221 }
6222
6223 int ScriptEnvironment::propSetFrame(AVSMap* map, const char* key, const PVideoFrame &frame, int append) AVS_NOEXCEPT {
6224 return !propSetShared<PVideoFrame, AVSPropertyType::PROPERTYTYPE_FRAME>(map, key, frame, append, this); // ptVideoFrame
6225 }
6226
6227 const int64_t* ScriptEnvironment::propGetIntArray(const AVSMap* map, const char* key, int* error) AVS_NOEXCEPT {
6228 const VSArrayBase* arr = propGetShared(map, key, 0, error, AVSPropertyType::PROPERTYTYPE_INT, this);
6229 if (arr) {
6230 return reinterpret_cast<const VSIntArray*>(arr)->getDataPointer();
6231 }
6232 else {
6233 return nullptr;
6234 }
6235 }
6236
6237 const double* ScriptEnvironment::propGetFloatArray(const AVSMap* map, const char* key, int* error) AVS_NOEXCEPT {
6238 const VSArrayBase* arr = propGetShared(map, key, 0, error, AVSPropertyType::PROPERTYTYPE_FLOAT, this);
6239 if (arr) {
6240 return reinterpret_cast<const VSFloatArray*>(arr)->getDataPointer();
6241 }
6242 else {
6243 return nullptr;
6244 }
6245 }
6246
6247 int ScriptEnvironment::propSetIntArray(AVSMap* map, const char* key, const int64_t* i, int size) AVS_NOEXCEPT {
6248 assert(map && key && size >= 0);
6249 if (size < 0)
6250 return 1;
6251 if (!isValidVSMapKey(key))
6252 return 1;
6253 map->insert(key, new VSIntArray(i, size));
6254 return 0;
6255 }
6256
6257 int ScriptEnvironment::propSetFloatArray(AVSMap* map, const char* key, const double* d, int size) AVS_NOEXCEPT {
6258 assert(map && key && size >= 0);
6259 if (size < 0)
6260 return 1;
6261 if (!isValidVSMapKey(key))
6262 return 1;
6263 map->insert(key, new VSFloatArray(d, size));
6264 return 0;
6265 }
6266
6267 15 AVSMap* ScriptEnvironment::createMap() AVS_NOEXCEPT {
6268
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 15 times.
15 return new AVSMap();
6269 }
6270
6271 15 void ScriptEnvironment::freeMap(AVSMap* map) AVS_NOEXCEPT {
6272
1/2
✓ Branch 2 → 3 taken 15 times.
✗ Branch 2 → 5 not taken.
15 delete map;
6273 15 }
6274
6275 void ScriptEnvironment::clearMap(AVSMap* map) AVS_NOEXCEPT {
6276 assert(map);
6277 map->clear();
6278 }
6279 // end of frame prop support functions
6280
6281 PDevice ScriptEnvironment::GetDevice(AvsDeviceType device_type, int device_index) const
6282 {
6283 return Devices->GetDevice(device_type, device_index);
6284 }
6285
6286 int ScriptEnvironment::SetMemoryMax(AvsDeviceType type, int index, int mem)
6287 {
6288 return Devices->GetDevice(type, index)->SetMemoryMax(mem);
6289 }
6290
6291 bool ScriptEnvironment::AcquireGlobalLock(const char* name)
6292 {
6293 if (!name) return false;
6294 std::string lock_name(name);
6295 std::mutex& mtx = GlobalLockManager::get_mutex(lock_name);
6296 mtx.lock(); // Blocks until lock is acquired.
6297 GlobalLockManager::acquire_lock_for_env(lock_name, this); // Track for cleanup.
6298 return true;
6299 }
6300
6301 void ScriptEnvironment::ReleaseGlobalLock(const char* name)
6302 {
6303 if (!name) return;
6304 std::string lock_name(name);
6305 std::mutex& mtx = GlobalLockManager::get_mutex(lock_name);
6306 GlobalLockManager::release_lock_for_env(lock_name, this); // Untrack.
6307 mtx.unlock();
6308 }
6309
6310 PVideoFrame ScriptEnvironment::GetOnDeviceFrame(const PVideoFrame& src, Device* device)
6311 {
6312 typedef int diff_t;
6313
6314 size_t srchead = GetFrameHead(src);
6315
6316 // make space for alignment
6317 size_t size = GetFrameTail(src) - srchead;
6318
6319 VideoFrame *res = GetNewFrame(size, frame_align - 1, device);
6320
6321 const diff_t offset = (diff_t)(AlignPointer(res->vfb->GetWritePtr(), frame_align) - res->vfb->GetWritePtr()); // first line offset for proper alignment
6322 const diff_t diff = offset - (diff_t)srchead;
6323
6324 res->offset = src->offset + diff;
6325 res->pitch = src->pitch;
6326 res->row_size = src->row_size;
6327 res->height = src->height;
6328
6329 res->offsetU = src->pitchUV ? (src->offsetU + diff) : res->offset;
6330 res->offsetV = src->pitchUV ? (src->offsetV + diff) : res->offset;
6331 res->pitchUV = src->pitchUV;
6332 res->row_sizeUV = src->row_sizeUV;
6333 res->heightUV = src->heightUV;
6334
6335 res->offsetA = src->pitchA ? (src->offsetA + diff) : 0;
6336 res->pitchA = src->pitchA;
6337 res->row_sizeA = src->row_sizeA;
6338
6339 res->pixel_type = src->pixel_type;
6340
6341 *res->properties = *src->properties;
6342
6343 return PVideoFrame(res);
6344 }
6345
6346
6347 ThreadPool* ScriptEnvironment::NewThreadPool(size_t nThreads)
6348 {
6349 // Creates threads with threadIDs (which envI->GetThreadId() is returning) starting from
6350 // (nTotalThreads+0) to (nTotalThreads+nThreads-1)
6351 ThreadPool* pool = new ThreadPool(nThreads, nTotalThreads, threadEnv.get());
6352 ThreadPoolRegistry.emplace_back(pool);
6353
6354 nTotalThreads += nThreads;
6355
6356 // TotalThreads: 1
6357 //*Prefetch(3)
6358 // added threads are 3, threadids: 1+0,1+1,1+2 (1,2,3)
6359 // TotalThreads = 4 from now
6360 // MaxFilterInstances -> 3->rounded up to 4 (next power of two). Number of instantiations
6361 // ChildFilter[0..3] (size = MaxFilterInstances)
6362 // GetThreadID & (4-1) = GetThreadID & 3
6363 // ThreadID 1,2,3 will map to ChildFilter[x] where x is 1,2,3
6364
6365 // TotalThreads: 4
6366 //*Prefetch(6)
6367 // added threads are 6, threadids: 4+0,4+1,4+2,4+3,4+4,4+5 (4,5,6,7,8,9)
6368 // TotalThreads = 10 from now
6369 // MaxFilterInstances -> 6->rounded up to 8 (next power of two). Number of instantiations
6370 // ChildFilter[0..7] (size = MaxFilterInstances)
6371 // GetThreadID & (8-1) = GetThreadID & 7
6372 // ThreadID 4,5,6,7,8,9 will map to ChildFilter[x] where x is 4,5,6,7,0,1
6373
6374 // TotalThreads: 10
6375 //*Prefetch(3)
6376 // added threads are 3, threadids: 10+0,10+1,10+2 (10,11,12)
6377 // TotalThreads = 13 from now
6378 // MaxFilterInstances -> 3->rounded up to 4 (next power of two). Number of instantiations
6379 // ChildFilter[0..3] (size = MaxFilterInstances)
6380 // GetThreadID & (4-1) = GetThreadID & 3
6381 // ThreadID 10,11,12 will map to ChildFilter[x] where x is 2,3,0
6382
6383 // PF remark: this is not too memory friendly, because the excessive numbers of MT_MULTI_INSTANCE filters
6384 // Prefetch(3) will create 4 instances
6385 // Prefetch(4) will create 8 instances
6386 // Prefetch(9) will still create 16 instances, of which 7 is not accessed at all
6387
6388 nMaxFilterInstances = nThreads; // really n/a
6389 // AEP_FILTERCHAIN_THREADS environment property ID returns this value. Unlikely if someone used it
6390 // for meaningful purposes.
6391 // Avisynth does not use that internally, and called from a filter preceesing another Prefetch,
6392 // only the last Prefetch's value is effectively returned, belonging to the actual 'env'.
6393
6394 // Since this method basically enables MT operation,
6395 // upgrade all MTGuards to MT-mode.
6396 // iterates through all filters in the chain, even is an earlier Prefetch has set the MT threads earlier
6397 for (MTGuard* guard : MTGuardRegistry)
6398 {
6399 if (guard != NULL)
6400 guard->EnableMT(nThreads);
6401 }
6402
6403 return pool;
6404 }
6405
6406 void ScriptEnvironment::SetDeviceOpt(DeviceOpt opt, int val)
6407 {
6408 Devices->SetDeviceOpt(opt, val, threadEnv.get());
6409 }
6410
6411 void ScriptEnvironment::UpdateFunctionExports(const char* funcName, const char* funcParams, const char *exportVar)
6412 {
6413 std::unique_lock<std::recursive_mutex> env_lock(plugin_mutex);
6414 plugin_manager->UpdateFunctionExports(funcName, funcParams, exportVar);
6415 }
6416
6417 extern void ApplyMessage(PVideoFrame* frame, const VideoInfo& vi,
6418 const char* message, int size, int textcolor, int halocolor, int bgcolor,
6419 IScriptEnvironment* env);
6420
6421 extern void ApplyMessageEx(PVideoFrame* frame, const VideoInfo& vi,
6422 const char* message, int size, int textcolor, int halocolor, int bgcolor, bool utf8,
6423 IScriptEnvironment* env);
6424
6425 const AVS_Linkage* ScriptEnvironment::GetAVSLinkage() {
6426 extern const AVS_Linkage* const AVS_linkage; // In interface.cpp
6427
6428 return AVS_linkage;
6429 }
6430
6431 1 void ScriptEnvironment::ApplyMessageEx(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, int textcolor, int halocolor, int bgcolor, bool utf8)
6432 {
6433 #ifdef ENABLE_CUDA
6434 if ((*frame)->GetDevice()->device_type == DEV_TYPE_CUDA) {
6435 // if frame is CUDA frame, copy to CPU and apply
6436 PVideoFrame copy = GetOnDeviceFrame(*frame, Devices->GetCPUDevice());
6437 CopyCUDAFrame(copy, *frame, threadEnv.get(), true);
6438 ::ApplyMessageEx(&copy, vi, message, size, textcolor, halocolor, bgcolor, utf8, threadEnv.get());
6439 CopyCUDAFrame(*frame, copy, threadEnv.get(), true);
6440 }
6441 else
6442 #endif
6443 {
6444 1 ::ApplyMessageEx(frame, vi, message, size, textcolor, halocolor, bgcolor, utf8, threadEnv.get());
6445 }
6446 1 }
6447
6448 1 void ScriptEnvironment::ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, int textcolor, int halocolor, int bgcolor)
6449 {
6450 1 ApplyMessageEx(frame, vi, message, size, textcolor, halocolor, bgcolor, false /* utf8 */);
6451 1 }
6452
6453 453 void ScriptEnvironment::DeleteScriptEnvironment() {
6454 // Provide a method to delete this ScriptEnvironment in
6455 // the same malloc context in which it was created below.
6456
1/2
✓ Branch 2 → 3 taken 453 times.
✗ Branch 2 → 5 not taken.
453 delete this;
6457 453 }
6458
6459
6460 AVSC_API(IScriptEnvironment*, CreateScriptEnvironment)(int version) {
6461 return CreateScriptEnvironment2(version);
6462 }
6463
6464 // Can be called from C interface create_script_environment with specially marking the C interface source
6465 453 IScriptEnvironment2* CreateScriptEnvironment2_internal(int version, bool fromAvs25, bool fromC)
6466 {
6467 /* Some plugins use OpenMP. But OMP threads do not exit immediately
6468 * after all work is exhausted, and keep spinning for a small amount
6469 * of time waiting for new jobs. If we unload the OMP DLL (indirectly
6470 * by unloading its plugin that started it) while its threads are
6471 * running, the sky comes crashing down. This results in crashes
6472 * from OMP plugins if the IScriptEnvironment is destructed shortly
6473 * after a GetFrame() call.
6474 *
6475 * OMP_WAIT_POLICY=passive changes the behavior of OMP thread pools
6476 * to shut down immediately instead of continuing to spin.
6477 * This solves our problem at the cost of some performance.
6478 */
6479 #ifdef AVS_WINDOWS
6480 _putenv("OMP_WAIT_POLICY=passive");
6481 #endif
6482
6483 // When a CPP plugin explicitely requests avs2.5 interface
6484
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 10 taken 453 times.
453 if (fromAvs25) {
6485 auto IEnv25 = (new ScriptEnvironment())->GetMainThreadEnv()->GetEnv25();
6486 // return a disguised IScriptEnvironment_Avs25
6487 return reinterpret_cast<IScriptEnvironment2*>(IEnv25);
6488 }
6489
1/4
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 19 taken 453 times.
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 19 not taken.
453 else if (fromC && version < 11) {
6490 // V11 supports 64 bit data types; no difference in IScriptEnvironment
6491 auto IEnvPreV11C = (new ScriptEnvironment())->GetMainThreadEnv()->GetEnvPreV11C();
6492 // return a disguised IScriptEnvironment_AvsC
6493 return reinterpret_cast<IScriptEnvironment2*>(IEnvPreV11C);
6494 }
6495
1/2
✓ Branch 19 → 20 taken 453 times.
✗ Branch 19 → 26 not taken.
453 else if (version <= AVISYNTH_INTERFACE_VERSION)
6496
2/6
✓ Branch 21 → 22 taken 453 times.
✗ Branch 21 → 34 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 453 times.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 36 not taken.
453 return (new ScriptEnvironment())->GetMainThreadEnv();
6497 else
6498 return NULL;
6499 }
6500
6501 453 AVSC_API(IScriptEnvironment2*, CreateScriptEnvironment2)(int version)
6502 {
6503
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 453 times.
453 if (version <= AVISYNTH_CLASSIC_INTERFACE_VERSION_25)
6504 return CreateScriptEnvironment2_internal(version, true, false); // avs 2.5, non-C
6505
1/2
✓ Branch 5 → 6 taken 453 times.
✗ Branch 5 → 8 not taken.
453 else if (version <= AVISYNTH_INTERFACE_VERSION)
6506 453 return CreateScriptEnvironment2_internal(version, false, false); // modern avs, non-C
6507
6508 return nullptr;
6509
6510 }
6511
6512 ///////////////
6513
6514