1 /*
2 * Copyright (c) 2012-2017 Fredrik Mellbin
3 *
4 * This file is part of VapourSynth.
5 *
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * VapourSynth is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with VapourSynth; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "vscore.h"
22 #include "VSHelper.h"
23 #include "version.h"
24 #include "cpufeatures.h"
25 #ifndef VS_TARGET_OS_WINDOWS
26 #include <dirent.h>
27 #include <cstddef>
28 #include <unistd.h>
29 #include "settings.h"
30 #endif
31 #include <cassert>
32 #include <queue>
33
34 #ifdef VS_TARGET_CPU_X86
35 #include "x86utils.h"
36 #endif
37
38 #ifdef VS_TARGET_OS_WINDOWS
39 #include <shlobj.h>
40 #include <locale>
41 #include "../common/vsutf16.h"
42 #endif
43
44 // Internal filter headers
45 #include "internalfilters.h"
46 #include "cachefilter.h"
47
48 #ifdef VS_TARGET_OS_DARWIN
49 #define thread_local __thread
50 #endif
51
isAlpha(char c)52 static inline bool isAlpha(char c) {
53 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
54 }
55
isAlphaNumUnderscore(char c)56 static inline bool isAlphaNumUnderscore(char c) {
57 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
58 }
59
isValidIdentifier(const std::string & s)60 static bool isValidIdentifier(const std::string &s) {
61 size_t len = s.length();
62 if (!len)
63 return false;
64
65 if (!isAlpha(s[0]))
66 return false;
67 for (size_t i = 1; i < len; i++)
68 if (!isAlphaNumUnderscore(s[i]))
69 return false;
70 return true;
71 }
72
73 #ifdef VS_TARGET_OS_WINDOWS
readRegistryValue(const wchar_t * keyName,const wchar_t * valueName)74 static std::wstring readRegistryValue(const wchar_t *keyName, const wchar_t *valueName) {
75 HKEY hKey;
76 LONG lRes = RegOpenKeyEx(HKEY_CURRENT_USER, keyName, 0, KEY_READ, &hKey);
77 if (lRes != ERROR_SUCCESS) {
78 lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &hKey);
79 if (lRes != ERROR_SUCCESS)
80 return std::wstring();
81 }
82 WCHAR szBuffer[512];
83 DWORD dwBufferSize = sizeof(szBuffer);
84 ULONG nError;
85 nError = RegQueryValueEx(hKey, valueName, 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize);
86 RegCloseKey(hKey);
87 if (ERROR_SUCCESS == nError)
88 return szBuffer;
89 return std::wstring();
90 }
91 #endif
92
FrameContext(int n,int index,VSNode * clip,const PFrameContext & upstreamContext)93 FrameContext::FrameContext(int n, int index, VSNode *clip, const PFrameContext &upstreamContext) :
94 reqOrder(upstreamContext->reqOrder), numFrameRequests(0), n(n), clip(clip), upstreamContext(upstreamContext), userData(nullptr), frameDone(nullptr), error(false), lockOnOutput(true), node(nullptr), lastCompletedN(-1), index(index), lastCompletedNode(nullptr), frameContext(nullptr) {
95 }
96
FrameContext(int n,int index,VSNodeRef * node,VSFrameDoneCallback frameDone,void * userData,bool lockOnOutput)97 FrameContext::FrameContext(int n, int index, VSNodeRef *node, VSFrameDoneCallback frameDone, void *userData, bool lockOnOutput) :
98 reqOrder(0), numFrameRequests(0), n(n), clip(node->clip.get()), userData(userData), frameDone(frameDone), error(false), lockOnOutput(lockOnOutput), node(node), lastCompletedN(-1), index(index), lastCompletedNode(nullptr), frameContext(nullptr) {
99 }
100
setError(const std::string & errorMsg)101 bool FrameContext::setError(const std::string &errorMsg) {
102 bool prevState = error;
103 error = true;
104 if (!prevState)
105 errorMessage = errorMsg;
106 return prevState;
107 }
108
109 ///////////////
110
ExtFunction(VSPublicFunction func,void * userData,VSFreeFuncData free,VSCore * core,const VSAPI * vsapi)111 ExtFunction::ExtFunction(VSPublicFunction func, void *userData, VSFreeFuncData free, VSCore *core, const VSAPI *vsapi) : func(func), userData(userData), free(free), core(core), vsapi(vsapi) {
112 core->functionInstanceCreated();
113 }
114
~ExtFunction()115 ExtFunction::~ExtFunction() {
116 if (free)
117 free(userData);
118 core->functionInstanceDestroyed();
119 }
120
call(const VSMap * in,VSMap * out)121 void ExtFunction::call(const VSMap *in, VSMap *out) {
122 func(in, out, userData, core, vsapi);
123 }
124
125 ///////////////
126
VSVariant(VSVType vtype)127 VSVariant::VSVariant(VSVType vtype) : vtype(vtype), internalSize(0), storage(nullptr) {
128 }
129
VSVariant(const VSVariant & v)130 VSVariant::VSVariant(const VSVariant &v) : vtype(v.vtype), internalSize(v.internalSize), storage(nullptr) {
131 if (internalSize) {
132 switch (vtype) {
133 case VSVariant::vInt:
134 storage = new IntList(*reinterpret_cast<IntList *>(v.storage)); break;
135 case VSVariant::vFloat:
136 storage = new FloatList(*reinterpret_cast<FloatList *>(v.storage)); break;
137 case VSVariant::vData:
138 storage = new DataList(*reinterpret_cast<DataList *>(v.storage)); break;
139 case VSVariant::vNode:
140 storage = new NodeList(*reinterpret_cast<NodeList *>(v.storage)); break;
141 case VSVariant::vFrame:
142 storage = new FrameList(*reinterpret_cast<FrameList *>(v.storage)); break;
143 case VSVariant::vMethod:
144 storage = new FuncList(*reinterpret_cast<FuncList *>(v.storage)); break;
145 default:;
146 }
147 }
148 }
149
VSVariant(VSVariant && v)150 VSVariant::VSVariant(VSVariant &&v) : vtype(v.vtype), internalSize(v.internalSize), storage(v.storage) {
151 v.vtype = vUnset;
152 v.storage = nullptr;
153 v.internalSize = 0;
154 }
155
~VSVariant()156 VSVariant::~VSVariant() {
157 if (storage) {
158 switch (vtype) {
159 case VSVariant::vInt:
160 delete reinterpret_cast<IntList *>(storage); break;
161 case VSVariant::vFloat:
162 delete reinterpret_cast<FloatList *>(storage); break;
163 case VSVariant::vData:
164 delete reinterpret_cast<DataList *>(storage); break;
165 case VSVariant::vNode:
166 delete reinterpret_cast<NodeList *>(storage); break;
167 case VSVariant::vFrame:
168 delete reinterpret_cast<FrameList *>(storage); break;
169 case VSVariant::vMethod:
170 delete reinterpret_cast<FuncList *>(storage); break;
171 default:;
172 }
173 }
174 }
175
size() const176 size_t VSVariant::size() const {
177 return internalSize;
178 }
179
getType() const180 VSVariant::VSVType VSVariant::getType() const {
181 return vtype;
182 }
183
append(int64_t val)184 void VSVariant::append(int64_t val) {
185 initStorage(vInt);
186 reinterpret_cast<IntList *>(storage)->push_back(val);
187 internalSize++;
188 }
189
append(double val)190 void VSVariant::append(double val) {
191 initStorage(vFloat);
192 reinterpret_cast<FloatList *>(storage)->push_back(val);
193 internalSize++;
194 }
195
append(const std::string & val)196 void VSVariant::append(const std::string &val) {
197 initStorage(vData);
198 reinterpret_cast<DataList *>(storage)->push_back(std::make_shared<std::string>(val));
199 internalSize++;
200 }
201
append(const VSNodeRef & val)202 void VSVariant::append(const VSNodeRef &val) {
203 initStorage(vNode);
204 reinterpret_cast<NodeList *>(storage)->push_back(val);
205 internalSize++;
206 }
207
append(const PVideoFrame & val)208 void VSVariant::append(const PVideoFrame &val) {
209 initStorage(vFrame);
210 reinterpret_cast<FrameList *>(storage)->push_back(val);
211 internalSize++;
212 }
213
append(const PExtFunction & val)214 void VSVariant::append(const PExtFunction &val) {
215 initStorage(vMethod);
216 reinterpret_cast<FuncList *>(storage)->push_back(val);
217 internalSize++;
218 }
219
initStorage(VSVType t)220 void VSVariant::initStorage(VSVType t) {
221 assert(vtype == vUnset || vtype == t);
222 vtype = t;
223 if (!storage) {
224 switch (t) {
225 case VSVariant::vInt:
226 storage = new IntList(); break;
227 case VSVariant::vFloat:
228 storage = new FloatList(); break;
229 case VSVariant::vData:
230 storage = new DataList(); break;
231 case VSVariant::vNode:
232 storage = new NodeList(); break;
233 case VSVariant::vFrame:
234 storage = new FrameList(); break;
235 case VSVariant::vMethod:
236 storage = new FuncList(); break;
237 default:;
238 }
239 }
240 }
241
242 ///////////////
243
isWindowsLargePageBroken()244 static bool isWindowsLargePageBroken() {
245 // A Windows bug exists where a VirtualAlloc call immediately after VirtualFree
246 // yields a page that has not been zeroed. The returned page is asynchronously
247 // zeroed a few milliseconds later, resulting in memory corruption. The same bug
248 // allows VirtualFree to return before the page has been unmapped.
249 static const bool broken = []() -> bool {
250 #ifdef VS_TARGET_OS_WINDOWS
251 size_t size = GetLargePageMinimum();
252
253 for (int i = 0; i < 100; ++i) {
254 void *ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
255 if (!ptr)
256 return true;
257
258 for (size_t n = 0; n < 64; ++n) {
259 if (static_cast<uint8_t *>(ptr)[n]) {
260 vsWarning("Windows 10 VirtualAlloc bug detected: update to version 1803+");
261 return true;
262 }
263 }
264 memset(ptr, 0xFF, 64);
265
266 if (VirtualFree(ptr, 0, MEM_RELEASE) != TRUE)
267 return true;
268 if (!IsBadReadPtr(ptr, 1)) {
269 vsWarning("Windows 10 VirtualAlloc bug detected: update to version 1803+");
270 return true;
271 }
272 }
273 #endif // VS_TARGET_OS_WINDOWS
274 return false;
275 }();
276 return broken;
277 }
278
largePageSupported()279 /* static */ bool MemoryUse::largePageSupported() {
280 // Disable large pages on 32-bit to avoid memory fragmentation.
281 if (sizeof(void *) < 8)
282 return false;
283
284 static const bool supported = []() -> bool {
285 #ifdef VS_TARGET_OS_WINDOWS
286 HANDLE token = INVALID_HANDLE_VALUE;
287 TOKEN_PRIVILEGES priv = {};
288
289 if (!(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)))
290 return false;
291
292 if (!(LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &priv.Privileges[0].Luid))) {
293 CloseHandle(token);
294 return false;
295 }
296
297 priv.PrivilegeCount = 1;
298 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
299
300 if (!(AdjustTokenPrivileges(token, FALSE, &priv, 0, nullptr, 0))) {
301 CloseHandle(token);
302 return false;
303 }
304
305 CloseHandle(token);
306 return true;
307 #else
308 return false;
309 #endif // VS_TARGET_OS_WINDOWS
310 }();
311 return supported;
312 }
313
largePageSize()314 /* static */ size_t MemoryUse::largePageSize() {
315 static const size_t size = []() -> size_t {
316 #ifdef VS_TARGET_OS_WINDOWS
317 return GetLargePageMinimum();
318 #else
319 return 2 * (1UL << 20);
320 #endif
321 }();
322 return size;
323 }
324
allocateLargePage(size_t bytes) const325 void *MemoryUse::allocateLargePage(size_t bytes) const {
326 if (!largePageEnabled)
327 return nullptr;
328
329 size_t granularity = largePageSize();
330 size_t allocBytes = VSFrame::alignment + bytes;
331 allocBytes = (allocBytes + (granularity - 1)) & ~(granularity - 1);
332 assert(allocBytes % granularity == 0);
333
334 // Don't allocate a large page if it would conflict with the buffer recycling logic.
335 if (!isGoodFit(bytes, allocBytes - VSFrame::alignment))
336 return nullptr;
337
338 void *ptr = nullptr;
339 #ifdef VS_TARGET_OS_WINDOWS
340 ptr = VirtualAlloc(nullptr, allocBytes, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
341 #else
342 ptr = vs_aligned_malloc(allocBytes, VSFrame::alignment);
343 #endif
344 if (!ptr)
345 return nullptr;
346
347 BlockHeader *header = new (ptr) BlockHeader;
348 header->size = allocBytes - VSFrame::alignment;
349 header->large = true;
350 return ptr;
351 }
352
freeLargePage(void * ptr) const353 void MemoryUse::freeLargePage(void *ptr) const {
354 #ifdef VS_TARGET_OS_WINDOWS
355 VirtualFree(ptr, 0, MEM_RELEASE);
356 #else
357 vs_aligned_free(ptr);
358 #endif
359 }
360
allocateMemory(size_t bytes) const361 void *MemoryUse::allocateMemory(size_t bytes) const {
362 void *ptr = allocateLargePage(bytes);
363 if (ptr)
364 return ptr;
365
366 ptr = vs_aligned_malloc(VSFrame::alignment + bytes, VSFrame::alignment);
367 if (!ptr)
368 vsFatal("out of memory: %zu", bytes);
369
370 BlockHeader *header = new (ptr) BlockHeader;
371 header->size = bytes;
372 header->large = false;
373 return ptr;
374 }
375
freeMemory(void * ptr) const376 void MemoryUse::freeMemory(void *ptr) const {
377 const BlockHeader *header = static_cast<const BlockHeader *>(ptr);
378 if (header->large)
379 freeLargePage(ptr);
380 else
381 vs_aligned_free(ptr);
382 }
383
isGoodFit(size_t requested,size_t actual) const384 bool MemoryUse::isGoodFit(size_t requested, size_t actual) const {
385 return actual <= requested + requested / 8;
386 }
387
add(size_t bytes)388 void MemoryUse::add(size_t bytes) {
389 used.fetch_add(bytes);
390 }
391
subtract(size_t bytes)392 void MemoryUse::subtract(size_t bytes) {
393 used.fetch_sub(bytes);
394 if (freeOnZero && !used)
395 delete this;
396 }
397
allocBuffer(size_t bytes)398 uint8_t *MemoryUse::allocBuffer(size_t bytes) {
399 std::lock_guard<std::mutex> lock(mutex);
400 auto iter = buffers.lower_bound(bytes);
401 if (iter != buffers.end()) {
402 if (isGoodFit(bytes, iter->first)) {
403 unusedBufferSize -= iter->first;
404 uint8_t *buf = iter->second;
405 buffers.erase(iter);
406 return buf + VSFrame::alignment;
407 }
408 }
409
410 uint8_t *buf = static_cast<uint8_t *>(allocateMemory(bytes));
411 return buf + VSFrame::alignment;
412 }
413
freeBuffer(uint8_t * buf)414 void MemoryUse::freeBuffer(uint8_t *buf) {
415 assert(buf);
416
417 std::lock_guard<std::mutex> lock(mutex);
418 buf -= VSFrame::alignment;
419
420 const BlockHeader *header = reinterpret_cast<const BlockHeader *>(buf);
421 if (!header->size)
422 vsFatal("Memory corruption detected. Windows bug?");
423
424 buffers.emplace(std::make_pair(header->size, buf));
425 unusedBufferSize += header->size;
426
427 size_t memoryUsed = used;
428 while (memoryUsed + unusedBufferSize > maxMemoryUse && !buffers.empty()) {
429 if (!memoryWarningIssued) {
430 vsWarning("Script exceeded memory limit. Consider raising cache size.");
431 memoryWarningIssued = true;
432 }
433 std::uniform_int_distribution<size_t> randSrc(0, buffers.size() - 1);
434 auto iter = buffers.begin();
435 std::advance(iter, randSrc(generator));
436 assert(unusedBufferSize >= iter->first);
437 unusedBufferSize -= iter->first;
438 freeMemory(iter->second);
439 buffers.erase(iter);
440 }
441 }
442
memoryUse()443 size_t MemoryUse::memoryUse() {
444 return used;
445 }
446
getLimit()447 size_t MemoryUse::getLimit() {
448 std::lock_guard<std::mutex> lock(mutex);
449 return maxMemoryUse;
450 }
451
setMaxMemoryUse(int64_t bytes)452 int64_t MemoryUse::setMaxMemoryUse(int64_t bytes) {
453 std::lock_guard<std::mutex> lock(mutex);
454 if (bytes > 0 && static_cast<uint64_t>(bytes) <= SIZE_MAX)
455 maxMemoryUse = static_cast<size_t>(bytes);
456 return maxMemoryUse;
457 }
458
isOverLimit()459 bool MemoryUse::isOverLimit() {
460 return used > maxMemoryUse;
461 }
462
signalFree()463 void MemoryUse::signalFree() {
464 freeOnZero = true;
465 if (!used)
466 delete this;
467 }
468
MemoryUse()469 MemoryUse::MemoryUse() : used(0), freeOnZero(false), largePageEnabled(largePageSupported()), memoryWarningIssued(false), unusedBufferSize(0) {
470 assert(VSFrame::alignment >= sizeof(BlockHeader));
471
472 // If the Windows VirtualAlloc bug is present, it is not safe to use large pages by default,
473 // because another application could trigger the bug.
474 //if (isWindowsLargePageBroken())
475 // largePageEnabled = false;
476
477 // Always disable large pages at the moment
478 largePageEnabled = false;
479
480 // 1GB
481 setMaxMemoryUse(1024 * 1024 * 1024);
482
483 // set 4GB as default on systems with (probably) 64bit address space
484 if (sizeof(void *) >= 8)
485 setMaxMemoryUse(static_cast<int64_t>(4) * 1024 * 1024 * 1024);
486 }
487
~MemoryUse()488 MemoryUse::~MemoryUse() {
489 for (auto &iter : buffers)
490 freeMemory(iter.second);
491 }
492
493 ///////////////
494
VSPlaneData(size_t dataSize,MemoryUse & mem)495 VSPlaneData::VSPlaneData(size_t dataSize, MemoryUse &mem) : refCount(1), mem(mem), size(dataSize + 2 * VSFrame::guardSpace) {
496 #ifdef VS_FRAME_POOL
497 data = mem.allocBuffer(size + 2 * VSFrame::guardSpace);
498 #else
499 data = vs_aligned_malloc<uint8_t>(size + 2 * VSFrame::guardSpace, VSFrame::alignment);
500 #endif
501 assert(data);
502 if (!data)
503 vsFatal("Failed to allocate memory for planes. Out of memory.");
504 mem.add(size);
505 #ifdef VS_FRAME_GUARD
506 for (size_t i = 0; i < VSFrame::guardSpace / sizeof(VS_FRAME_GUARD_PATTERN); i++) {
507 reinterpret_cast<uint32_t *>(data)[i] = VS_FRAME_GUARD_PATTERN;
508 reinterpret_cast<uint32_t *>(data + size - VSFrame::guardSpace)[i] = VS_FRAME_GUARD_PATTERN;
509 }
510 #endif
511 }
512
VSPlaneData(const VSPlaneData & d)513 VSPlaneData::VSPlaneData(const VSPlaneData &d) : refCount(1), mem(d.mem), size(d.size) {
514 #ifdef VS_FRAME_POOL
515 data = mem.allocBuffer(size);
516 #else
517 data = vs_aligned_malloc<uint8_t>(size, VSFrame::alignment);
518 #endif
519 assert(data);
520 if (!data)
521 vsFatal("Failed to allocate memory for plane in copy constructor. Out of memory.");
522 mem.add(size);
523 memcpy(data, d.data, size);
524 }
525
~VSPlaneData()526 VSPlaneData::~VSPlaneData() {
527 #ifdef VS_FRAME_POOL
528 mem.freeBuffer(data);
529 #else
530 vs_aligned_free(data);
531 #endif
532 mem.subtract(size);
533 }
534
unique()535 bool VSPlaneData::unique() {
536 return (refCount == 1);
537 }
538
addRef()539 void VSPlaneData::addRef() {
540 ++refCount;
541 }
542
release()543 void VSPlaneData::release() {
544 if (!--refCount)
545 delete this;
546 }
547
548 ///////////////
549
VSFrame(const VSFormat * f,int width,int height,const VSFrame * propSrc,VSCore * core)550 VSFrame::VSFrame(const VSFormat *f, int width, int height, const VSFrame *propSrc, VSCore *core) : format(f), data(), width(width), height(height) {
551 if (!f)
552 vsFatal("Error in frame creation: null format");
553
554 if (width <= 0 || height <= 0)
555 vsFatal("Error in frame creation: dimensions are negative (%dx%d)", width, height);
556
557 if (propSrc)
558 properties = propSrc->properties;
559
560 stride[0] = (width * (f->bytesPerSample) + (alignment - 1)) & ~(alignment - 1);
561
562 if (f->numPlanes == 3) {
563 int plane23 = ((width >> f->subSamplingW) * (f->bytesPerSample) + (alignment - 1)) & ~(alignment - 1);
564 stride[1] = plane23;
565 stride[2] = plane23;
566 } else {
567 stride[1] = 0;
568 stride[2] = 0;
569 }
570
571 data[0] = new VSPlaneData(stride[0] * height, *core->memory);
572 if (f->numPlanes == 3) {
573 int size23 = stride[1] * (height >> f->subSamplingH);
574 data[1] = new VSPlaneData(size23, *core->memory);
575 data[2] = new VSPlaneData(size23, *core->memory);
576 }
577 }
578
VSFrame(const VSFormat * f,int width,int height,const VSFrame * const * planeSrc,const int * plane,const VSFrame * propSrc,VSCore * core)579 VSFrame::VSFrame(const VSFormat *f, int width, int height, const VSFrame * const *planeSrc, const int *plane, const VSFrame *propSrc, VSCore *core) : format(f), data(), width(width), height(height) {
580 if (!f)
581 vsFatal("Error in frame creation: null format");
582
583 if (width <= 0 || height <= 0)
584 vsFatal("Error in frame creation: dimensions are negative (%dx%d)", width, height);
585
586 if (propSrc)
587 properties = propSrc->properties;
588
589 stride[0] = (width * (f->bytesPerSample) + (alignment - 1)) & ~(alignment - 1);
590
591 if (f->numPlanes == 3) {
592 int plane23 = ((width >> f->subSamplingW) * (f->bytesPerSample) + (alignment - 1)) & ~(alignment - 1);
593 stride[1] = plane23;
594 stride[2] = plane23;
595 } else {
596 stride[1] = 0;
597 stride[2] = 0;
598 }
599
600 for (int i = 0; i < format->numPlanes; i++) {
601 if (planeSrc[i]) {
602 if (plane[i] < 0 || plane[i] >= planeSrc[i]->format->numPlanes)
603 vsFatal("Error in frame creation: plane %d does not exist in the source frame", plane[i]);
604 if (planeSrc[i]->getHeight(plane[i]) != getHeight(i) || planeSrc[i]->getWidth(plane[i]) != getWidth(i))
605 vsFatal("Error in frame creation: dimensions of plane %d do not match. Source: %dx%d; destination: %dx%d", plane[i], planeSrc[i]->getWidth(plane[i]), planeSrc[i]->getHeight(plane[i]), getWidth(i), getHeight(i));
606 data[i] = planeSrc[i]->data[plane[i]];
607 data[i]->addRef();
608 } else {
609 if (i == 0) {
610 data[i] = new VSPlaneData(stride[i] * height, *core->memory);
611 } else {
612 data[i] = new VSPlaneData(stride[i] * (height >> f->subSamplingH), *core->memory);
613 }
614 }
615 }
616 }
617
VSFrame(const VSFrame & f)618 VSFrame::VSFrame(const VSFrame &f) {
619 data[0] = f.data[0];
620 data[1] = f.data[1];
621 data[2] = f.data[2];
622 data[0]->addRef();
623 if (data[1]) {
624 data[1]->addRef();
625 data[2]->addRef();
626 }
627 format = f.format;
628 width = f.width;
629 height = f.height;
630 stride[0] = f.stride[0];
631 stride[1] = f.stride[1];
632 stride[2] = f.stride[2];
633 properties = f.properties;
634 }
635
~VSFrame()636 VSFrame::~VSFrame() {
637 data[0]->release();
638 if (data[1]) {
639 data[1]->release();
640 data[2]->release();
641 }
642 }
643
getStride(int plane) const644 int VSFrame::getStride(int plane) const {
645 assert(plane >= 0 && plane < 3);
646 if (plane < 0 || plane >= format->numPlanes)
647 vsFatal("Requested stride of nonexistent plane %d", plane);
648 return stride[plane];
649 }
650
getReadPtr(int plane) const651 const uint8_t *VSFrame::getReadPtr(int plane) const {
652 if (plane < 0 || plane >= format->numPlanes)
653 vsFatal("Requested read pointer for nonexistent plane %d", plane);
654
655 return data[plane]->data + guardSpace;
656 }
657
getWritePtr(int plane)658 uint8_t *VSFrame::getWritePtr(int plane) {
659 if (plane < 0 || plane >= format->numPlanes)
660 vsFatal("Requested write pointer for nonexistent plane %d", plane);
661
662 // copy the plane data if this isn't the only reference
663 if (!data[plane]->unique()) {
664 VSPlaneData *old = data[plane];
665 data[plane] = new VSPlaneData(*data[plane]);
666 old->release();
667 }
668
669 return data[plane]->data + guardSpace;
670 }
671
672 #ifdef VS_FRAME_GUARD
verifyGuardPattern()673 bool VSFrame::verifyGuardPattern() {
674 for (int p = 0; p < format->numPlanes; p++) {
675 for (size_t i = 0; i < guardSpace / sizeof(VS_FRAME_GUARD_PATTERN); i++) {
676 uint32_t p1 = reinterpret_cast<uint32_t *>(data[p]->data)[i];
677 uint32_t p2 = reinterpret_cast<uint32_t *>(data[p]->data + data[p]->size - guardSpace)[i];
678 if (reinterpret_cast<uint32_t *>(data[p]->data)[i] != VS_FRAME_GUARD_PATTERN ||
679 reinterpret_cast<uint32_t *>(data[p]->data + data[p]->size - guardSpace)[i] != VS_FRAME_GUARD_PATTERN)
680 return false;
681 }
682 }
683
684 return true;
685 }
686 #endif
687
688 struct split1 {
689 enum empties_t { empties_ok, no_empties };
690 };
691
692 template <typename Container>
split(Container & result,const typename Container::value_type & s,const typename Container::value_type & delimiters,split1::empties_t empties=split1::empties_ok)693 Container& split(
694 Container& result,
695 const typename Container::value_type& s,
696 const typename Container::value_type& delimiters,
697 split1::empties_t empties = split1::empties_ok) {
698 result.clear();
699 size_t current;
700 size_t next = -1;
701 do {
702 if (empties == split1::no_empties) {
703 next = s.find_first_not_of(delimiters, next + 1);
704 if (next == Container::value_type::npos) break;
705 next -= 1;
706 }
707 current = next + 1;
708 next = s.find_first_of(delimiters, current);
709 result.push_back(s.substr(current, next - current));
710 } while (next != Container::value_type::npos);
711 return result;
712 }
713
VSFunction(const std::string & argString,VSPublicFunction func,void * functionData)714 VSFunction::VSFunction(const std::string &argString, VSPublicFunction func, void *functionData)
715 : argString(argString), functionData(functionData), func(func) {
716 std::vector<std::string> argList;
717 split(argList, argString, std::string(";"), split1::no_empties);
718 for(const std::string &arg : argList) {
719 std::vector<std::string> argParts;
720 split(argParts, arg, std::string(":"), split1::no_empties);
721
722 if (argParts.size() < 2)
723 vsFatal("Invalid argument specifier '%s'. It appears to be incomplete.", arg.c_str());
724
725 bool arr = false;
726 enum FilterArgumentType type = faNone;
727 const std::string &argName = argParts[0];
728 const std::string &typeName = argParts[1];
729
730 if (typeName == "int")
731 type = faInt;
732 else if (typeName == "float")
733 type = faFloat;
734 else if (typeName == "data")
735 type = faData;
736 else if (typeName == "clip")
737 type = faClip;
738 else if (typeName == "frame")
739 type = faFrame;
740 else if (typeName == "func")
741 type = faFunc;
742 else {
743 arr = true;
744
745 if (typeName == "int[]")
746 type = faInt;
747 else if (typeName == "float[]")
748 type = faFloat;
749 else if (typeName == "data[]")
750 type = faData;
751 else if (typeName == "clip[]")
752 type = faClip;
753 else if (typeName == "frame[]")
754 type = faFrame;
755 else if (typeName == "func[]")
756 type = faFunc;
757 else
758 vsFatal("Argument '%s' has invalid type '%s'.", argName.c_str(), typeName.c_str());
759 }
760
761 bool opt = false;
762 bool empty = false;
763
764 for (size_t i = 2; i < argParts.size(); i++) {
765 if (argParts[i] == "opt") {
766 if (opt)
767 vsFatal("Argument '%s' has duplicate argument specifier '%s'", argName.c_str(), argParts[i].c_str());
768 opt = true;
769 } else if (argParts[i] == "empty") {
770 if (empty)
771 vsFatal("Argument '%s' has duplicate argument specifier '%s'", argName.c_str(), argParts[i].c_str());
772 empty = true;
773 } else {
774 vsFatal("Argument '%s' has unknown argument modifier '%s'", argName.c_str(), argParts[i].c_str());
775 }
776 }
777
778 if (!isValidIdentifier(argName))
779 vsFatal("Argument name '%s' contains illegal characters.", argName.c_str());
780
781 if (empty && !arr)
782 vsFatal("Argument '%s' is not an array. Only array arguments can have the empty flag set.", argName.c_str());
783
784 args.push_back(FilterArgument(argName, type, arr, empty, opt));
785 }
786 }
787
VSNode(const VSMap * in,VSMap * out,const std::string & name,VSFilterInit init,VSFilterGetFrame getFrame,VSFilterFree free,VSFilterMode filterMode,int flags,void * instanceData,int apiMajor,VSCore * core)788 VSNode::VSNode(const VSMap *in, VSMap *out, const std::string &name, VSFilterInit init, VSFilterGetFrame getFrame, VSFilterFree free, VSFilterMode filterMode, int flags, void *instanceData, int apiMajor, VSCore *core) :
789 instanceData(instanceData), name(name), init(init), filterGetFrame(getFrame), free(free), filterMode(filterMode), apiMajor(apiMajor), core(core), flags(flags), hasVi(false), serialFrame(-1) {
790
791 if (flags & ~(nfNoCache | nfIsCache | nfMakeLinear))
792 throw VSException("Filter " + name + " specified unknown flags");
793
794 if ((flags & nfIsCache) && !(flags & nfNoCache))
795 throw VSException("Filter " + name + " specified an illegal combination of flags (nfNoCache must always be set with nfIsCache)");
796
797 core->filterInstanceCreated();
798 VSMap inval(*in);
799 init(&inval, out, &this->instanceData, this, core, getVSAPIInternal(apiMajor));
800
801 if (out->hasError()) {
802 core->filterInstanceDestroyed();
803 throw VSException(vs_internal_vsapi.getError(out));
804 }
805
806 if (!hasVi) {
807 core->filterInstanceDestroyed();
808 throw VSException("Filter " + name + " didn't set vi");
809 }
810
811 for (const auto &iter : vi) {
812 if (iter.numFrames <= 0) {
813 core->filterInstanceDestroyed();
814 throw VSException("Filter " + name + " returned zero or negative frame count");
815 }
816 }
817 }
818
~VSNode()819 VSNode::~VSNode() {
820 core->destroyFilterInstance(this);
821 }
822
getFrame(const PFrameContext & ct)823 void VSNode::getFrame(const PFrameContext &ct) {
824 core->threadPool->start(ct);
825 }
826
getVideoInfo(int index)827 const VSVideoInfo &VSNode::getVideoInfo(int index) {
828 if (index < 0 || index >= static_cast<int>(vi.size()))
829 vsFatal("getVideoInfo: Out of bounds videoinfo index %d. Valid range: [0,%d].", index, static_cast<int>(vi.size() - 1));
830 return vi[index];
831 }
832
setVideoInfo(const VSVideoInfo * vi,int numOutputs)833 void VSNode::setVideoInfo(const VSVideoInfo *vi, int numOutputs) {
834 if (numOutputs < 1)
835 vsFatal("setVideoInfo: Video filter %s needs to have at least one output (%d were given).", name.c_str(), numOutputs);
836 for (int i = 0; i < numOutputs; i++) {
837 if ((!!vi[i].height) ^ (!!vi[i].width))
838 vsFatal("setVideoInfo: Variable dimension clips must have both width and height set to 0. Dimensions given by filter %s: %dx%d.", name.c_str(), vi[i].width, vi[i].height);
839 if (vi[i].format && !core->isValidFormatPointer(vi[i].format))
840 vsFatal("setVideoInfo: The VSFormat pointer passed by %s was not obtained from registerFormat() or getFormatPreset().", name.c_str());
841 int64_t num = vi[i].fpsNum;
842 int64_t den = vi[i].fpsDen;
843 vs_normalizeRational(&num, &den);
844 if (num != vi[i].fpsNum || den != vi[i].fpsDen)
845 vsFatal(("setVideoInfo: The frame rate specified by " + name + " must be a reduced fraction. (Instead, it is " + std::to_string(vi[i].fpsNum) + "/" + std::to_string(vi[i].fpsDen) + ".)").c_str());
846
847 this->vi.push_back(vi[i]);
848 this->vi[i].flags = flags;
849 }
850 hasVi = true;
851 }
852
getFrameInternal(int n,int activationReason,VSFrameContext & frameCtx)853 PVideoFrame VSNode::getFrameInternal(int n, int activationReason, VSFrameContext &frameCtx) {
854 const VSFrameRef *r = filterGetFrame(n, activationReason, &instanceData, &frameCtx.ctx->frameContext, &frameCtx, core, &vs_internal_vsapi);
855
856 #ifdef VS_TARGET_OS_WINDOWS
857 if (!vs_isSSEStateOk())
858 vsFatal("Bad SSE state detected after return from %s", name.c_str());
859 #endif
860
861 if (r) {
862 PVideoFrame p(std::move(r->frame));
863 delete r;
864 const VSFormat *fi = p->getFormat();
865 const VSVideoInfo &lvi = vi[frameCtx.ctx->index];
866
867 if (!lvi.format && fi->colorFamily == cmCompat)
868 vsFatal("Illegal compat frame returned by %s.", name.c_str());
869 else if (lvi.format && lvi.format != fi)
870 vsFatal("Filter %s declared the format %s (id %d), but it returned a frame with the format %s (id %d).", name.c_str(), lvi.format->name, lvi.format->id, fi->name, fi->id);
871 else if ((lvi.width || lvi.height) && (p->getWidth(0) != lvi.width || p->getHeight(0) != lvi.height))
872 vsFatal("Filter %s declared the size %dx%d, but it returned a frame with the size %dx%d.", name.c_str(), lvi.width, lvi.height, p->getWidth(0), p->getHeight(0));
873
874 #ifdef VS_FRAME_GUARD
875 if (!p->verifyGuardPattern())
876 vsFatal("Guard memory corrupted in frame %d returned from %s", n, name.c_str());
877 #endif
878
879 return p;
880 }
881
882 PVideoFrame p;
883 return p;
884 }
885
reserveThread()886 void VSNode::reserveThread() {
887 core->threadPool->reserveThread();
888 }
889
releaseThread()890 void VSNode::releaseThread() {
891 core->threadPool->releaseThread();
892 }
893
isWorkerThread()894 bool VSNode::isWorkerThread() {
895 return core->threadPool->isWorkerThread();
896 }
897
notifyCache(bool needMemory)898 void VSNode::notifyCache(bool needMemory) {
899 std::lock_guard<std::mutex> lock(serialMutex);
900 CacheInstance *cache = (CacheInstance *)instanceData;
901 cache->cache.adjustSize(needMemory);
902 }
903
newVideoFrame(const VSFormat * f,int width,int height,const VSFrame * propSrc)904 PVideoFrame VSCore::newVideoFrame(const VSFormat *f, int width, int height, const VSFrame *propSrc) {
905 return std::make_shared<VSFrame>(f, width, height, propSrc, this);
906 }
907
newVideoFrame(const VSFormat * f,int width,int height,const VSFrame * const * planeSrc,const int * planes,const VSFrame * propSrc)908 PVideoFrame VSCore::newVideoFrame(const VSFormat *f, int width, int height, const VSFrame * const *planeSrc, const int *planes, const VSFrame *propSrc) {
909 return std::make_shared<VSFrame>(f, width, height, planeSrc, planes, propSrc, this);
910 }
911
copyFrame(const PVideoFrame & srcf)912 PVideoFrame VSCore::copyFrame(const PVideoFrame &srcf) {
913 return std::make_shared<VSFrame>(*srcf.get());
914 }
915
copyFrameProps(const PVideoFrame & src,PVideoFrame & dst)916 void VSCore::copyFrameProps(const PVideoFrame &src, PVideoFrame &dst) {
917 dst->setProperties(src->getProperties());
918 }
919
getFormatPreset(int id)920 const VSFormat *VSCore::getFormatPreset(int id) {
921 std::lock_guard<std::mutex> lock(formatLock);
922
923 auto f = formats.find(id);
924 if (f != formats.end())
925 return f->second;
926 return nullptr;
927 }
928
registerFormat(VSColorFamily colorFamily,VSSampleType sampleType,int bitsPerSample,int subSamplingW,int subSamplingH,const char * name,int id)929 const VSFormat *VSCore::registerFormat(VSColorFamily colorFamily, VSSampleType sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, const char *name, int id) {
930 // this is to make exact format comparisons easy by simply allowing pointer comparison
931
932 // block nonsense formats
933 if (subSamplingH < 0 || subSamplingW < 0 || subSamplingH > 4 || subSamplingW > 4)
934 return nullptr;
935
936 if (sampleType < 0 || sampleType > 1)
937 return nullptr;
938
939 if (colorFamily == cmRGB && (subSamplingH != 0 || subSamplingW != 0))
940 return nullptr;
941
942 if (sampleType == stFloat && (bitsPerSample != 16 && bitsPerSample != 32))
943 return nullptr;
944
945 if (bitsPerSample < 8 || bitsPerSample > 32)
946 return nullptr;
947
948 if (colorFamily == cmCompat && !name)
949 return nullptr;
950
951 std::lock_guard<std::mutex> lock(formatLock);
952
953 for (const auto &iter : formats) {
954 const VSFormat *f = iter.second;
955
956 if (f->colorFamily == colorFamily && f->sampleType == sampleType
957 && f->subSamplingW == subSamplingW && f->subSamplingH == subSamplingH && f->bitsPerSample == bitsPerSample)
958 return f;
959 }
960
961 VSFormat *f = new VSFormat();
962 memset(f->name, 0, sizeof(f->name));
963
964 if (name) {
965 strcpy(f->name, name);
966 } else {
967 const char *sampleTypeStr = "";
968 if (sampleType == stFloat)
969 sampleTypeStr = (bitsPerSample == 32) ? "S" : "H";
970
971 const char *yuvName = nullptr;
972
973 switch (colorFamily) {
974 case cmGray:
975 snprintf(f->name, sizeof(f->name), "Gray%s%d", sampleTypeStr, bitsPerSample);
976 break;
977 case cmRGB:
978 snprintf(f->name, sizeof(f->name), "RGB%s%d", sampleTypeStr, bitsPerSample * 3);
979 break;
980 case cmYUV:
981 if (subSamplingW == 1 && subSamplingH == 1)
982 yuvName = "420";
983 else if (subSamplingW == 1 && subSamplingH == 0)
984 yuvName = "422";
985 else if (subSamplingW == 0 && subSamplingH == 0)
986 yuvName = "444";
987 else if (subSamplingW == 2 && subSamplingH == 2)
988 yuvName = "410";
989 else if (subSamplingW == 2 && subSamplingH == 0)
990 yuvName = "411";
991 else if (subSamplingW == 0 && subSamplingH == 1)
992 yuvName = "440";
993 if (yuvName)
994 snprintf(f->name, sizeof(f->name), "YUV%sP%s%d", yuvName, sampleTypeStr, bitsPerSample);
995 else
996 snprintf(f->name, sizeof(f->name), "YUVssw%dssh%dP%s%d", subSamplingW, subSamplingH, sampleTypeStr, bitsPerSample);
997 break;
998 case cmYCoCg:
999 snprintf(f->name, sizeof(f->name), "YCoCgssw%dssh%dP%s%d", subSamplingW, subSamplingH, sampleTypeStr, bitsPerSample);
1000 break;
1001 default:;
1002 }
1003 }
1004
1005 if (id != pfNone)
1006 f->id = id;
1007 else
1008 f->id = colorFamily + formatIdOffset++;
1009
1010 f->colorFamily = colorFamily;
1011 f->sampleType = sampleType;
1012 f->bitsPerSample = bitsPerSample;
1013 f->bytesPerSample = 1;
1014
1015 while (f->bytesPerSample * 8 < bitsPerSample)
1016 f->bytesPerSample *= 2;
1017
1018 f->subSamplingW = subSamplingW;
1019 f->subSamplingH = subSamplingH;
1020 f->numPlanes = (colorFamily == cmGray || colorFamily == cmCompat) ? 1 : 3;
1021
1022 formats.insert(std::make_pair(f->id, f));
1023 return f;
1024 }
1025
isValidFormatPointer(const VSFormat * f)1026 bool VSCore::isValidFormatPointer(const VSFormat *f) {
1027 std::lock_guard<std::mutex> lock(formatLock);
1028
1029 for (const auto &iter : formats) {
1030 if (iter.second == f)
1031 return true;
1032 }
1033 return false;
1034 }
1035
getCoreInfo()1036 const VSCoreInfo &VSCore::getCoreInfo() {
1037 getCoreInfo2(coreInfo);
1038 return coreInfo;
1039 }
1040
getCoreInfo2(VSCoreInfo & info)1041 void VSCore::getCoreInfo2(VSCoreInfo &info) {
1042 info.versionString = VAPOURSYNTH_VERSION_STRING;
1043 info.core = VAPOURSYNTH_CORE_VERSION;
1044 info.api = VAPOURSYNTH_API_VERSION;
1045 info.numThreads = threadPool->threadCount();
1046 info.maxFramebufferSize = memory->getLimit();
1047 info.usedFramebufferSize = memory->memoryUse();
1048 }
1049
1050 void VS_CC vs_internal_configPlugin(const char *identifier, const char *defaultNamespace, const char *name, int apiVersion, int readOnly, VSPlugin *plugin);
1051 void VS_CC vs_internal_registerFunction(const char *name, const char *args, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin);
1052
loadPlugin(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)1053 static void VS_CC loadPlugin(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
1054 try {
1055 int err;
1056 const char *forcens = vsapi->propGetData(in, "forcens", 0, &err);
1057 if (!forcens)
1058 forcens = "";
1059 const char *forceid = vsapi->propGetData(in, "forceid", 0, &err);
1060 if (!forceid)
1061 forceid = "";
1062 bool altSearchPath = !!vsapi->propGetInt(in, "altsearchpath", 0, &err);
1063 core->loadPlugin(vsapi->propGetData(in, "path", 0, nullptr), forcens, forceid, altSearchPath);
1064 } catch (VSException &e) {
1065 vsapi->setError(out, e.what());
1066 }
1067 }
1068
loadPluginInitialize(VSConfigPlugin configFunc,VSRegisterFunction registerFunc,VSPlugin * plugin)1069 void VS_CC loadPluginInitialize(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) {
1070 registerFunc("LoadPlugin", "path:data;altsearchpath:int:opt;forcens:data:opt;forceid:data:opt;", &loadPlugin, nullptr, plugin);
1071 }
1072
registerFormats()1073 void VSCore::registerFormats() {
1074 // Register known formats with informational names
1075 registerFormat(cmGray, stInteger, 8, 0, 0, "Gray8", pfGray8);
1076 registerFormat(cmGray, stInteger, 16, 0, 0, "Gray16", pfGray16);
1077
1078 registerFormat(cmGray, stFloat, 16, 0, 0, "GrayH", pfGrayH);
1079 registerFormat(cmGray, stFloat, 32, 0, 0, "GrayS", pfGrayS);
1080
1081 registerFormat(cmYUV, stInteger, 8, 1, 1, "YUV420P8", pfYUV420P8);
1082 registerFormat(cmYUV, stInteger, 8, 1, 0, "YUV422P8", pfYUV422P8);
1083 registerFormat(cmYUV, stInteger, 8, 0, 0, "YUV444P8", pfYUV444P8);
1084 registerFormat(cmYUV, stInteger, 8, 2, 2, "YUV410P8", pfYUV410P8);
1085 registerFormat(cmYUV, stInteger, 8, 2, 0, "YUV411P8", pfYUV411P8);
1086 registerFormat(cmYUV, stInteger, 8, 0, 1, "YUV440P8", pfYUV440P8);
1087
1088 registerFormat(cmYUV, stInteger, 9, 1, 1, "YUV420P9", pfYUV420P9);
1089 registerFormat(cmYUV, stInteger, 9, 1, 0, "YUV422P9", pfYUV422P9);
1090 registerFormat(cmYUV, stInteger, 9, 0, 0, "YUV444P9", pfYUV444P9);
1091
1092 registerFormat(cmYUV, stInteger, 10, 1, 1, "YUV420P10", pfYUV420P10);
1093 registerFormat(cmYUV, stInteger, 10, 1, 0, "YUV422P10", pfYUV422P10);
1094 registerFormat(cmYUV, stInteger, 10, 0, 0, "YUV444P10", pfYUV444P10);
1095
1096 registerFormat(cmYUV, stInteger, 12, 1, 1, "YUV420P12", pfYUV420P12);
1097 registerFormat(cmYUV, stInteger, 12, 1, 0, "YUV422P12", pfYUV422P12);
1098 registerFormat(cmYUV, stInteger, 12, 0, 0, "YUV444P12", pfYUV444P12);
1099
1100 registerFormat(cmYUV, stInteger, 14, 1, 1, "YUV420P14", pfYUV420P14);
1101 registerFormat(cmYUV, stInteger, 14, 1, 0, "YUV422P14", pfYUV422P14);
1102 registerFormat(cmYUV, stInteger, 14, 0, 0, "YUV444P14", pfYUV444P14);
1103
1104 registerFormat(cmYUV, stInteger, 16, 1, 1, "YUV420P16", pfYUV420P16);
1105 registerFormat(cmYUV, stInteger, 16, 1, 0, "YUV422P16", pfYUV422P16);
1106 registerFormat(cmYUV, stInteger, 16, 0, 0, "YUV444P16", pfYUV444P16);
1107
1108 registerFormat(cmYUV, stFloat, 16, 0, 0, "YUV444PH", pfYUV444PH);
1109 registerFormat(cmYUV, stFloat, 32, 0, 0, "YUV444PS", pfYUV444PS);
1110
1111 registerFormat(cmRGB, stInteger, 8, 0, 0, "RGB24", pfRGB24);
1112 registerFormat(cmRGB, stInteger, 9, 0, 0, "RGB27", pfRGB27);
1113 registerFormat(cmRGB, stInteger, 10, 0, 0, "RGB30", pfRGB30);
1114 registerFormat(cmRGB, stInteger, 16, 0, 0, "RGB48", pfRGB48);
1115
1116 registerFormat(cmRGB, stFloat, 16, 0, 0, "RGBH", pfRGBH);
1117 registerFormat(cmRGB, stFloat, 32, 0, 0, "RGBS", pfRGBS);
1118
1119 registerFormat(cmCompat, stInteger, 32, 0, 0, "CompatBGR32", pfCompatBGR32);
1120 registerFormat(cmCompat, stInteger, 16, 1, 0, "CompatYUY2", pfCompatYUY2);
1121 }
1122
1123
1124 #ifdef VS_TARGET_OS_WINDOWS
loadAllPluginsInPath(const std::wstring & path,const std::wstring & filter)1125 bool VSCore::loadAllPluginsInPath(const std::wstring &path, const std::wstring &filter) {
1126 #else
1127 bool VSCore::loadAllPluginsInPath(const std::string &path, const std::string &filter) {
1128 #endif
1129 if (path.empty())
1130 return false;
1131
1132 #ifdef VS_TARGET_OS_WINDOWS
1133 std::wstring wPath = path + L"\\" + filter;
1134 WIN32_FIND_DATA findData;
1135 HANDLE findHandle = FindFirstFile(wPath.c_str(), &findData);
1136 if (findHandle == INVALID_HANDLE_VALUE)
1137 return false;
1138 do {
1139 try {
1140 loadPlugin(utf16_to_utf8(path + L"\\" + findData.cFileName));
1141 } catch (VSException &) {
1142 // Ignore any errors
1143 }
1144 } while (FindNextFile(findHandle, &findData));
1145 FindClose(findHandle);
1146 #else
1147 DIR *dir = opendir(path.c_str());
1148 if (!dir)
1149 return false;
1150
1151 int name_max = pathconf(path.c_str(), _PC_NAME_MAX);
1152 if (name_max == -1)
1153 name_max = 255;
1154
1155 while (true) {
1156 struct dirent *result = readdir(dir);
1157 if (!result) {
1158 break;
1159 }
1160
1161 std::string name(result->d_name);
1162 // If name ends with filter
1163 if (name.size() >= filter.size() && name.compare(name.size() - filter.size(), filter.size(), filter) == 0) {
1164 try {
1165 std::string fullname;
1166 fullname.append(path).append("/").append(name);
1167 loadPlugin(fullname);
1168 } catch (VSException &) {
1169 // Ignore any errors
1170 }
1171 }
1172 }
1173
1174 if (closedir(dir)) {
1175 // Shouldn't happen
1176 }
1177 #endif
1178
1179 return true;
1180 }
1181
1182 void VSCore::functionInstanceCreated() {
1183 ++numFunctionInstances;
1184 }
1185
1186 void VSCore::functionInstanceDestroyed() {
1187 --numFunctionInstances;
1188 }
1189
1190 void VSCore::filterInstanceCreated() {
1191 ++numFilterInstances;
1192 }
1193
1194 void VSCore::filterInstanceDestroyed() {
1195 if (!--numFilterInstances) {
1196 assert(coreFreed);
1197 delete this;
1198 }
1199 }
1200
1201 struct VSCoreShittyList {
1202 VSFilterFree free;
1203 void *instanceData;
1204 VSCoreShittyList *next;
1205 // add stuff like vsapi here if multiple versions end up floating around for compatibility
1206 };
1207
1208 void VSCore::destroyFilterInstance(VSNode *node) {
1209 static thread_local int freeDepth = 0;
1210 static thread_local VSCoreShittyList *nodeFreeList = nullptr;
1211 freeDepth++;
1212
1213 if (node->free) {
1214 nodeFreeList = new VSCoreShittyList({ node->free, node->instanceData, nodeFreeList });
1215 } else {
1216 filterInstanceDestroyed();
1217 }
1218
1219 if (freeDepth == 1) {
1220 while (nodeFreeList) {
1221 VSCoreShittyList *current = nodeFreeList;
1222 nodeFreeList = current->next;
1223 current->free(current->instanceData, this, &vs_internal_vsapi);
1224 delete current;
1225 filterInstanceDestroyed();
1226 }
1227 }
1228
1229 freeDepth--;
1230 }
1231
1232 VSCore::VSCore(int threads) :
1233 coreFreed(false),
1234 numFilterInstances(1),
1235 numFunctionInstances(0),
1236 formatIdOffset(1000),
1237 cpuLevel(INT_MAX),
1238 memory(new MemoryUse()) {
1239 #ifdef VS_TARGET_OS_WINDOWS
1240 if (!vs_isSSEStateOk())
1241 vsFatal("Bad SSE state detected when creating new core");
1242 #endif
1243
1244 threadPool = new VSThreadPool(this, threads);
1245
1246 registerFormats();
1247
1248 // The internal plugin units, the loading is a bit special so they can get special flags
1249 VSPlugin *p;
1250
1251 // Initialize internal plugins
1252 p = new VSPlugin(this);
1253 ::vs_internal_configPlugin("com.vapoursynth.std", "std", "VapourSynth Core Functions", VAPOURSYNTH_API_VERSION, 0, p);
1254 loadPluginInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1255 cacheInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1256 exprInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1257 genericInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1258 lutInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1259 boxBlurInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1260 mergeInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1261 reorderInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1262 stdlibInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1263 p->enableCompat();
1264 p->lock();
1265
1266 plugins.insert(std::make_pair(p->id, p));
1267 p = new VSPlugin(this);
1268 resizeInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1269 plugins.insert(std::make_pair(p->id, p));
1270 p->enableCompat();
1271
1272 plugins.insert(std::make_pair(p->id, p));
1273 p = new VSPlugin(this);
1274 textInitialize(::vs_internal_configPlugin, ::vs_internal_registerFunction, p);
1275 plugins.insert(std::make_pair(p->id, p));
1276 p->enableCompat();
1277
1278 #ifdef VS_TARGET_OS_WINDOWS
1279
1280 const std::wstring filter = L"*.dll";
1281
1282 #ifdef _WIN64
1283 #define VS_INSTALL_REGKEY L"Software\\VapourSynth"
1284 std::wstring bits(L"64");
1285 #else
1286 #define VS_INSTALL_REGKEY L"Software\\VapourSynth-32"
1287 std::wstring bits(L"32");
1288 #endif
1289
1290 HMODULE module;
1291 GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)&vs_internal_configPlugin, &module);
1292 std::vector<wchar_t> pathBuf(65536);
1293 GetModuleFileName(module, pathBuf.data(), (DWORD)pathBuf.size());
1294 std::wstring dllPath = pathBuf.data();
1295 dllPath.resize(dllPath.find_last_of('\\') + 1);
1296 std::wstring portableFilePath = dllPath + L"portable.vs";
1297 FILE *portableFile = _wfopen(portableFilePath.c_str(), L"rb");
1298 bool isPortable = !!portableFile;
1299 if (portableFile)
1300 fclose(portableFile);
1301
1302 if (isPortable) {
1303 // Use alternative search strategy relative to dll path
1304
1305 // Autoload bundled plugins
1306 std::wstring corePluginPath = dllPath + L"vapoursynth" + bits + L"\\coreplugins";
1307 if (!loadAllPluginsInPath(corePluginPath, filter))
1308 vsCritical("Core plugin autoloading failed. Installation is broken?");
1309
1310 // Autoload global plugins last, this is so the bundled plugins cannot be overridden easily
1311 // and accidentally block updated bundled versions
1312 std::wstring globalPluginPath = dllPath + L"vapoursynth" + bits + L"\\plugins";
1313 loadAllPluginsInPath(globalPluginPath, filter);
1314 } else {
1315 // Autoload user specific plugins first so a user can always override
1316 std::vector<wchar_t> appDataBuffer(MAX_PATH + 1);
1317 if (SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, appDataBuffer.data()) != S_OK)
1318 SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_DEFAULT, appDataBuffer.data());
1319
1320 std::wstring appDataPath = std::wstring(appDataBuffer.data()) + L"\\VapourSynth\\plugins" + bits;
1321
1322 // Autoload per user plugins
1323 loadAllPluginsInPath(appDataPath, filter);
1324
1325 // Autoload bundled plugins
1326 std::wstring corePluginPath = readRegistryValue(VS_INSTALL_REGKEY, L"CorePlugins");
1327 if (!loadAllPluginsInPath(corePluginPath, filter))
1328 vsCritical("Core plugin autoloading failed. Installation is broken?");
1329
1330 // Autoload global plugins last, this is so the bundled plugins cannot be overridden easily
1331 // and accidentally block updated bundled versions
1332 std::wstring globalPluginPath = readRegistryValue(VS_INSTALL_REGKEY, L"Plugins");
1333 loadAllPluginsInPath(globalPluginPath, filter);
1334 }
1335
1336 #else
1337 std::string configFile;
1338 const char *home = getenv("HOME");
1339 #ifdef VS_TARGET_OS_DARWIN
1340 std::string filter = ".dylib";
1341 if (home) {
1342 configFile.append(home).append("/Library/Application Support/VapourSynth/vapoursynth.conf");
1343 }
1344 #else
1345 std::string filter = ".so";
1346 const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
1347 if (xdg_config_home) {
1348 configFile.append(xdg_config_home).append("/vapoursynth/vapoursynth.conf");
1349 } else if (home) {
1350 configFile.append(home).append("/.config/vapoursynth/vapoursynth.conf");
1351 } // If neither exists, an empty string will do.
1352 #endif
1353
1354 VSMap *settings = readSettings(configFile);
1355 const char *error = vs_internal_vsapi.getError(settings);
1356 if (error) {
1357 vsWarning("%s\n", error);
1358 } else {
1359 int err;
1360 const char *tmp;
1361
1362 tmp = vs_internal_vsapi.propGetData(settings, "UserPluginDir", 0, &err);
1363 std::string userPluginDir(tmp ? tmp : "");
1364
1365 tmp = vs_internal_vsapi.propGetData(settings, "SystemPluginDir", 0, &err);
1366 std::string systemPluginDir(tmp ? tmp : VS_PATH_PLUGINDIR);
1367
1368 tmp = vs_internal_vsapi.propGetData(settings, "AutoloadUserPluginDir", 0, &err);
1369 bool autoloadUserPluginDir = tmp ? std::string(tmp) == "true" : true;
1370
1371 tmp = vs_internal_vsapi.propGetData(settings, "AutoloadSystemPluginDir", 0, &err);
1372 bool autoloadSystemPluginDir = tmp ? std::string(tmp) == "true" : true;
1373
1374 if (autoloadUserPluginDir && !userPluginDir.empty()) {
1375 if (!loadAllPluginsInPath(userPluginDir, filter)) {
1376 vsWarning("Autoloading the user plugin dir '%s' failed. Directory doesn't exist?", userPluginDir.c_str());
1377 }
1378 }
1379
1380 if (autoloadSystemPluginDir) {
1381 if (!loadAllPluginsInPath(systemPluginDir, filter)) {
1382 vsCritical("Autoloading the system plugin dir '%s' failed. Directory doesn't exist?", systemPluginDir.c_str());
1383 }
1384 }
1385 }
1386
1387 vs_internal_vsapi.freeMap(settings);
1388 #endif
1389 }
1390
1391 void VSCore::freeCore() {
1392 if (coreFreed)
1393 vsFatal("Double free of core");
1394 coreFreed = true;
1395 threadPool->waitForDone();
1396 if (numFilterInstances > 1)
1397 vsWarning("Core freed but %d filter instance(s) still exist", numFilterInstances.load() - 1);
1398 if (memory->memoryUse() > 0)
1399 vsWarning("Core freed but %llu bytes still allocated in framebuffers", static_cast<unsigned long long>(memory->memoryUse()));
1400 if (numFunctionInstances > 0)
1401 vsWarning("Core freed but %d function instance(s) still exist", numFunctionInstances.load());
1402 // Release the extra filter instance that always keeps the core alive
1403 filterInstanceDestroyed();
1404 }
1405
1406 VSCore::~VSCore() {
1407 memory->signalFree();
1408 delete threadPool;
1409 for(const auto &iter : plugins)
1410 delete iter.second;
1411 plugins.clear();
1412 for (const auto &iter : formats)
1413 delete iter.second;
1414 formats.clear();
1415 }
1416
1417 VSMap VSCore::getPlugins() {
1418 VSMap m;
1419 std::lock_guard<std::recursive_mutex> lock(pluginLock);
1420 int num = 0;
1421 for (const auto &iter : plugins) {
1422 std::string b = iter.second->fnamespace + ";" + iter.second->id + ";" + iter.second->fullname;
1423 vs_internal_vsapi.propSetData(&m, ("Plugin" + std::to_string(++num)).c_str(), b.c_str(), static_cast<int>(b.size()), paReplace);
1424 }
1425 return m;
1426 }
1427
1428 VSPlugin *VSCore::getPluginById(const std::string &identifier) {
1429 std::lock_guard<std::recursive_mutex> lock(pluginLock);
1430 auto p = plugins.find(identifier);
1431 if (p != plugins.end())
1432 return p->second;
1433 return nullptr;
1434 }
1435
1436 VSPlugin *VSCore::getPluginByNs(const std::string &ns) {
1437 std::lock_guard<std::recursive_mutex> lock(pluginLock);
1438 for (const auto &iter : plugins) {
1439 if (iter.second->fnamespace == ns)
1440 return iter.second;
1441 }
1442 return nullptr;
1443 }
1444
1445 void VSCore::loadPlugin(const std::string &filename, const std::string &forcedNamespace, const std::string &forcedId, bool altSearchPath) {
1446 VSPlugin *p = new VSPlugin(filename, forcedNamespace, forcedId, altSearchPath, this);
1447
1448 std::lock_guard<std::recursive_mutex> lock(pluginLock);
1449
1450 VSPlugin *already_loaded_plugin = getPluginById(p->id);
1451 if (already_loaded_plugin) {
1452 std::string error = "Plugin " + filename + " already loaded (" + p->id + ")";
1453 if (already_loaded_plugin->filename.size())
1454 error += " from " + already_loaded_plugin->filename;
1455 delete p;
1456 throw VSException(error);
1457 }
1458
1459 already_loaded_plugin = getPluginByNs(p->fnamespace);
1460 if (already_loaded_plugin) {
1461 std::string error = "Plugin load of " + filename + " failed, namespace " + p->fnamespace + " already populated";
1462 if (already_loaded_plugin->filename.size())
1463 error += " by " + already_loaded_plugin->filename;
1464 delete p;
1465 throw VSException(error);
1466 }
1467
1468 plugins.insert(std::make_pair(p->id, p));
1469
1470 // allow avisynth plugins to accept legacy avisynth formats
1471 if (p->fnamespace == "avs" && p->id == "com.vapoursynth.avisynth")
1472 p->enableCompat();
1473 }
1474
1475 void VSCore::createFilter(const VSMap *in, VSMap *out, const std::string &name, VSFilterInit init, VSFilterGetFrame getFrame, VSFilterFree free, VSFilterMode filterMode, int flags, void *instanceData, int apiMajor) {
1476 try {
1477 PVideoNode node(std::make_shared<VSNode>(in, out, name, init, getFrame, free, filterMode, flags, instanceData, apiMajor, this));
1478 for (size_t i = 0; i < node->getNumOutputs(); i++) {
1479 // fixme, not that elegant but saves more variant poking code
1480 VSNodeRef *ref = new VSNodeRef(node, static_cast<int>(i));
1481 vs_internal_vsapi.propSetNode(out, "clip", ref, paAppend);
1482 delete ref;
1483 }
1484 } catch (VSException &e) {
1485 vs_internal_vsapi.setError(out, e.what());
1486 }
1487 }
1488
1489 int VSCore::getCpuLevel() const {
1490 return cpuLevel;
1491 }
1492
1493 int VSCore::setCpuLevel(int cpu) {
1494 return cpuLevel.exchange(cpu);
1495 }
1496
1497 VSPlugin::VSPlugin(VSCore *core)
1498 : apiMajor(0), apiMinor(0), hasConfig(false), readOnly(false), compat(false), libHandle(0), core(core) {
1499 }
1500
1501 VSPlugin::VSPlugin(const std::string &relFilename, const std::string &forcedNamespace, const std::string &forcedId, bool altSearchPath, VSCore *core)
1502 : apiMajor(0), apiMinor(0), hasConfig(false), readOnly(false), compat(false), libHandle(0), core(core), fnamespace(forcedNamespace), id(forcedId) {
1503 #ifdef VS_TARGET_OS_WINDOWS
1504 std::wstring wPath = utf16_from_utf8(relFilename);
1505 std::vector<wchar_t> fullPathBuffer(32767 + 1); // add 1 since msdn sucks at mentioning whether or not it includes the final null
1506 if (wPath.substr(0, 4) != L"\\\\?\\")
1507 wPath = L"\\\\?\\" + wPath;
1508 GetFullPathName(wPath.c_str(), static_cast<DWORD>(fullPathBuffer.size()), fullPathBuffer.data(), nullptr);
1509 wPath = fullPathBuffer.data();
1510 if (wPath.substr(0, 4) == L"\\\\?\\")
1511 wPath = wPath.substr(4);
1512 filename = utf16_to_utf8(wPath);
1513 for (auto &iter : filename)
1514 if (iter == '\\')
1515 iter = '/';
1516
1517 libHandle = LoadLibraryEx(wPath.c_str(), nullptr, altSearchPath ? 0 : (LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR));
1518
1519 if (!libHandle) {
1520 DWORD lastError = GetLastError();
1521
1522 if (lastError == 126)
1523 throw VSException("Failed to load " + relFilename + ". GetLastError() returned " + std::to_string(lastError) + ". The file you tried to load or one of its dependencies is probably missing.");
1524 throw VSException("Failed to load " + relFilename + ". GetLastError() returned " + std::to_string(lastError) + ".");
1525 }
1526
1527 VSInitPlugin pluginInit = (VSInitPlugin)GetProcAddress(libHandle, "VapourSynthPluginInit");
1528
1529 if (!pluginInit)
1530 pluginInit = (VSInitPlugin)GetProcAddress(libHandle, "_VapourSynthPluginInit@12");
1531
1532 if (!pluginInit) {
1533 FreeLibrary(libHandle);
1534 throw VSException("No entry point found in " + relFilename);
1535 }
1536 #else
1537 std::vector<char> fullPathBuffer(PATH_MAX + 1);
1538 if (realpath(relFilename.c_str(), fullPathBuffer.data()))
1539 filename = fullPathBuffer.data();
1540 else
1541 filename = relFilename;
1542
1543 libHandle = dlopen(filename.c_str(), RTLD_LAZY);
1544
1545 if (!libHandle) {
1546 const char *dlError = dlerror();
1547 if (dlError)
1548 throw VSException("Failed to load " + relFilename + ". Error given: " + dlError);
1549 else
1550 throw VSException("Failed to load " + relFilename);
1551 }
1552
1553 VSInitPlugin pluginInit = (VSInitPlugin)dlsym(libHandle, "VapourSynthPluginInit");
1554
1555 if (!pluginInit) {
1556 dlclose(libHandle);
1557 throw VSException("No entry point found in " + relFilename);
1558 }
1559
1560
1561 #endif
1562 pluginInit(::vs_internal_configPlugin, ::vs_internal_registerFunction, this);
1563
1564 #ifdef VS_TARGET_OS_WINDOWS
1565 if (!vs_isSSEStateOk())
1566 vsFatal("Bad SSE state detected after loading %s", filename.c_str());
1567 #endif
1568
1569 if (readOnlySet)
1570 readOnly = true;
1571
1572 if (apiMajor != VAPOURSYNTH_API_MAJOR || apiMinor > VAPOURSYNTH_API_MINOR) {
1573 #ifdef VS_TARGET_OS_WINDOWS
1574 FreeLibrary(libHandle);
1575 #else
1576 dlclose(libHandle);
1577 #endif
1578 throw VSException("Core only supports API R" + std::to_string(VAPOURSYNTH_API_MAJOR) + "." + std::to_string(VAPOURSYNTH_API_MINOR) + " but the loaded plugin requires API R" + std::to_string(apiMajor) + "." + std::to_string(apiMinor) + "; Filename: " + relFilename + "; Name: " + fullname);
1579 }
1580 }
1581
1582 VSPlugin::~VSPlugin() {
1583 #ifdef VS_TARGET_OS_WINDOWS
1584 if (libHandle)
1585 FreeLibrary(libHandle);
1586 #else
1587 if (libHandle)
1588 dlclose(libHandle);
1589 #endif
1590 }
1591
1592 void VSPlugin::configPlugin(const std::string &identifier, const std::string &defaultNamespace, const std::string &fullname, int apiVersion, bool readOnly) {
1593 if (hasConfig)
1594 vsFatal("Attempted to configure plugin %s twice", identifier.c_str());
1595
1596 if (id.empty())
1597 id = identifier;
1598
1599 if (fnamespace.empty())
1600 fnamespace = defaultNamespace;
1601
1602 this->fullname = fullname;
1603
1604 apiMajor = apiVersion;
1605 if (apiMajor >= 0x10000) {
1606 apiMinor = (apiMajor & 0xFFFF);
1607 apiMajor >>= 16;
1608 }
1609
1610 readOnlySet = readOnly;
1611 hasConfig = true;
1612 }
1613
1614 void VSPlugin::registerFunction(const std::string &name, const std::string &args, VSPublicFunction argsFunc, void *functionData) {
1615 if (readOnly)
1616 vsFatal("Plugin %s tried to modify read only namespace.", filename.c_str());
1617
1618 if (!isValidIdentifier(name))
1619 vsFatal("Plugin %s tried to register '%s', an illegal identifier.", filename.c_str(), name.c_str());
1620
1621 std::lock_guard<std::mutex> lock(registerFunctionLock);
1622
1623 if (funcs.count(name)) {
1624 vsWarning("Plugin %s tried to register '%s' more than once. Second registration ignored.", filename.c_str(), name.c_str());
1625 return;
1626 }
1627
1628 funcs.insert(std::make_pair(name, VSFunction(args, argsFunc, functionData)));
1629 }
1630
1631 static bool hasCompatNodes(const VSMap &m) {
1632 for (const auto &vsv : m.getStorage()) {
1633 if (vsv.second.getType() == VSVariant::vNode) {
1634 for (size_t i = 0; i < vsv.second.size(); i++) {
1635 for (size_t j = 0; j < vsv.second.getValue<VSNodeRef>(i).clip->getNumOutputs(); j++) {
1636 const VSNodeRef &ref = vsv.second.getValue<VSNodeRef>(i);
1637 const VSVideoInfo &vi = ref.clip->getVideoInfo(static_cast<int>(j));
1638 if (vi.format && vi.format->colorFamily == cmCompat)
1639 return true;
1640 }
1641 }
1642 }
1643 }
1644 return false;
1645 }
1646
1647 static bool hasForeignNodes(const VSMap &m, const VSCore *core) {
1648 for (const auto &vsv : m.getStorage()) {
1649 if (vsv.second.getType() == VSVariant::vNode) {
1650 for (size_t i = 0; i < vsv.second.size(); i++) {
1651 for (size_t j = 0; j < vsv.second.getValue<VSNodeRef>(i).clip->getNumOutputs(); j++) {
1652 const VSNodeRef &ref = vsv.second.getValue<VSNodeRef>(i);
1653 if (!ref.clip->isRightCore(core))
1654 return true;
1655 }
1656 }
1657 }
1658 }
1659 return false;
1660 }
1661
1662 VSMap VSPlugin::invoke(const std::string &funcName, const VSMap &args) {
1663 const char lookup[] = { 'i', 'f', 's', 'c', 'v', 'm' };
1664 VSMap v;
1665
1666 try {
1667 if (funcs.count(funcName)) {
1668 const VSFunction &f = funcs[funcName];
1669 if (!compat && hasCompatNodes(args))
1670 throw VSException(funcName + ": only special filters may accept compat input");
1671 if (hasForeignNodes(args, core))
1672 throw VSException(funcName + ": nodes foreign to this core passed as input, improper api usage detected");
1673
1674 std::set<std::string> remainingArgs;
1675 for (const auto &key : args.getStorage())
1676 remainingArgs.insert(key.first);
1677
1678 for (const FilterArgument &fa : f.args) {
1679 char c = vs_internal_vsapi.propGetType(&args, fa.name.c_str());
1680
1681 if (c != 'u') {
1682 remainingArgs.erase(fa.name);
1683
1684 if (lookup[static_cast<int>(fa.type)] != c)
1685 throw VSException(funcName + ": argument " + fa.name + " is not of the correct type");
1686
1687 if (!fa.arr && args[fa.name.c_str()].size() > 1)
1688 throw VSException(funcName + ": argument " + fa.name + " is not of array type but more than one value was supplied");
1689
1690 if (!fa.empty && args[fa.name.c_str()].size() < 1)
1691 throw VSException(funcName + ": argument " + fa.name + " does not accept empty arrays");
1692
1693 } else if (!fa.opt) {
1694 throw VSException(funcName + ": argument " + fa.name + " is required");
1695 }
1696 }
1697
1698 if (!remainingArgs.empty()) {
1699 auto iter = remainingArgs.cbegin();
1700 std::string s = *iter;
1701 ++iter;
1702 for (; iter != remainingArgs.cend(); ++iter)
1703 s += ", " + *iter;
1704 throw VSException(funcName + ": no argument(s) named " + s);
1705 }
1706
1707 f.func(&args, &v, f.functionData, core, getVSAPIInternal(apiMajor));
1708
1709 if (!compat && hasCompatNodes(v))
1710 vsFatal("%s: illegal filter node returning a compat format detected, DO NOT USE THE COMPAT FORMATS IN NEW FILTERS", funcName.c_str());
1711
1712 return v;
1713 }
1714 } catch (VSException &e) {
1715 vs_internal_vsapi.setError(&v, e.what());
1716 return v;
1717 }
1718
1719 vs_internal_vsapi.setError(&v, ("Function '" + funcName + "' not found in " + id).c_str());
1720 return v;
1721 }
1722
1723 VSMap VSPlugin::getFunctions() {
1724 VSMap m;
1725 for (const auto & f : funcs) {
1726 std::string b = f.first + ";" + f.second.argString;
1727 vs_internal_vsapi.propSetData(&m, f.first.c_str(), b.c_str(), static_cast<int>(b.size()), paReplace);
1728 }
1729 return m;
1730 }
1731
1732 #ifdef VS_TARGET_CPU_X86
1733 static int alignmentHelper() {
1734 return getCPUFeatures()->avx512_f ? 64 : 32;
1735 }
1736
1737 int VSFrame::alignment = alignmentHelper();
1738 #else
1739 int VSFrame::alignment = 32;
1740 #endif
1741