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 #ifndef VSCORE_H 22 #define VSCORE_H 23 24 #include "VapourSynth.h" 25 #include "vslog.h" 26 #include <cstdlib> 27 #include <stdexcept> 28 #include <string> 29 #include <cstring> 30 #include <cassert> 31 #include <vector> 32 #include <list> 33 #include <set> 34 #include <map> 35 #include <memory> 36 #include <atomic> 37 #include <mutex> 38 #include <thread> 39 #include <condition_variable> 40 #include <random> 41 #include <algorithm> 42 #ifdef VS_TARGET_OS_WINDOWS 43 # define WIN32_LEAN_AND_MEAN 44 # ifndef NOMINMAX 45 # define NOMINMAX 46 # endif 47 # include <windows.h> 48 # define VS_FRAME_POOL 49 #else 50 # include <dlfcn.h> 51 #endif 52 53 #ifdef VS_FRAME_GUARD 54 static const uint32_t VS_FRAME_GUARD_PATTERN = 0xDEADBEEF; 55 #endif 56 57 // Internal only filter mode for use by caches to make requests more linear 58 const int fmUnorderedLinear = fmUnordered + 13; 59 60 class VSFrame; 61 struct VSCore; 62 class VSCache; 63 struct VSNode; 64 class VSThreadPool; 65 class FrameContext; 66 class ExtFunction; 67 68 typedef std::shared_ptr<VSFrame> PVideoFrame; 69 typedef std::weak_ptr<VSFrame> WVideoFrame; 70 typedef std::shared_ptr<VSNode> PVideoNode; 71 typedef std::shared_ptr<ExtFunction> PExtFunction; 72 typedef std::shared_ptr<FrameContext> PFrameContext; 73 74 extern const VSAPI vs_internal_vsapi; 75 const VSAPI *getVSAPIInternal(int apiMajor); 76 77 class VSException : public std::runtime_error { 78 public: 79 using std::runtime_error::runtime_error; 80 }; 81 82 class NodeOutputKey { 83 private: 84 VSNode *node; 85 int n; 86 int index; 87 public: NodeOutputKey(VSNode * node,int n,int index)88 NodeOutputKey(VSNode *node, int n, int index) : node(node), n(n), index(index) {} 89 inline bool operator==(const NodeOutputKey &v) const { 90 return node == v.node && n == v.n && index == v.index; 91 } 92 inline bool operator<(const NodeOutputKey &v) const { 93 return (node < v.node) || (node == v.node && n < v.n) || (node == v.node && n == v.n && index < v.index); 94 } 95 }; 96 97 // variant types 98 typedef std::shared_ptr<std::string> VSMapData; 99 typedef std::vector<int64_t> IntList; 100 typedef std::vector<double> FloatList; 101 typedef std::vector<VSMapData> DataList; 102 typedef std::vector<VSNodeRef> NodeList; 103 typedef std::vector<PVideoFrame> FrameList; 104 typedef std::vector<PExtFunction> FuncList; 105 106 class ExtFunction { 107 private: 108 VSPublicFunction func; 109 void *userData; 110 VSFreeFuncData free; 111 VSCore *core; 112 const VSAPI *vsapi; 113 public: 114 ExtFunction(VSPublicFunction func, void *userData, VSFreeFuncData free, VSCore *core, const VSAPI *vsapi); 115 ~ExtFunction(); 116 void call(const VSMap *in, VSMap *out); 117 }; 118 119 class VSVariant { 120 public: 121 enum VSVType { vUnset, vInt, vFloat, vData, vNode, vFrame, vMethod }; 122 VSVariant(VSVType vtype = vUnset); 123 VSVariant(const VSVariant &v); 124 VSVariant(VSVariant &&v); 125 ~VSVariant(); 126 127 size_t size() const; 128 VSVType getType() const; 129 130 void append(int64_t val); 131 void append(double val); 132 void append(const std::string &val); 133 void append(const VSNodeRef &val); 134 void append(const PVideoFrame &val); 135 void append(const PExtFunction &val); 136 137 template<typename T> getValue(size_t index)138 const T &getValue(size_t index) const { 139 return reinterpret_cast<std::vector<T>*>(storage)->at(index); 140 } 141 142 template<typename T> getArray()143 const T *getArray() const { 144 return reinterpret_cast<std::vector<T>*>(storage)->data(); 145 } 146 147 template<typename T> setArray(const T * val,size_t size)148 void setArray(const T *val, size_t size) { 149 assert(val && !storage); 150 std::vector<T> *vect = new std::vector<T>(size); 151 if (size) 152 memcpy(vect->data(), val, size * sizeof(T)); 153 internalSize = size; 154 storage = vect; 155 } 156 157 private: 158 VSVType vtype; 159 size_t internalSize; 160 void *storage; 161 162 void initStorage(VSVType t); 163 }; 164 165 class VSMapStorage { 166 private: 167 std::atomic<int> refCount; 168 public: 169 std::map<std::string, VSVariant> data; 170 bool error; 171 VSMapStorage()172 VSMapStorage() : refCount(1), error(false) {} 173 VSMapStorage(const VSMapStorage & s)174 VSMapStorage(const VSMapStorage &s) : refCount(1), data(s.data), error(s.error) {} 175 unique()176 bool unique() { 177 return (refCount == 1); 178 }; 179 addRef()180 void addRef() { 181 ++refCount; 182 } 183 release()184 void release() { 185 if (!--refCount) 186 delete this; 187 } 188 }; 189 190 struct VSMap { 191 private: 192 VSMapStorage *data; 193 public: VSMapVSMap194 VSMap() : data(new VSMapStorage()) {} 195 VSMapVSMap196 VSMap(const VSMap &map) : data(map.data) { 197 data->addRef(); 198 } 199 VSMapVSMap200 VSMap(VSMap &&map) : data(map.data) { 201 map.data = new VSMapStorage(); 202 } 203 ~VSMapVSMap204 ~VSMap() { 205 data->release(); 206 } 207 208 VSMap &operator=(const VSMap &map) { 209 data->release(); 210 data = map.data; 211 data->addRef(); 212 return *this; 213 } 214 detachVSMap215 void detach() { 216 if (!data->unique()) { 217 VSMapStorage *old = data; 218 data = new VSMapStorage(*data); 219 old->release(); 220 } 221 } 222 containsVSMap223 bool contains(const std::string &key) const { 224 return !!data->data.count(key); 225 } 226 atVSMap227 VSVariant &at(const std::string &key) const { 228 return data->data.at(key); 229 } 230 231 VSVariant &operator[](const std::string &key) const { 232 // implicit creation is unwanted so make sure it doesn't happen by wrapping at() instead 233 return data->data.at(key); 234 } 235 findVSMap236 VSVariant *find(const std::string &key) const { 237 auto it = data->data.find(key); 238 return it == data->data.end() ? nullptr : &it->second; 239 } 240 eraseVSMap241 bool erase(const std::string &key) { 242 detach(); 243 return data->data.erase(key) > 0; 244 } 245 insertVSMap246 bool insert(const std::string &key, VSVariant &&v) { 247 detach(); 248 data->data.erase(key); 249 data->data.insert(std::make_pair(key, v)); 250 return true; 251 } 252 sizeVSMap253 size_t size() const { 254 return data->data.size(); 255 } 256 clearVSMap257 void clear() { 258 data->release(); 259 data = new VSMapStorage(); 260 } 261 keyVSMap262 const char *key(int n) const { 263 if (n >= static_cast<int>(size())) 264 return nullptr; 265 auto iter = data->data.cbegin(); 266 std::advance(iter, n); 267 return iter->first.c_str(); 268 } 269 getStorageVSMap270 const std::map<std::string, VSVariant> &getStorage() const { 271 return data->data; 272 } 273 setErrorVSMap274 void setError(const std::string &errMsg) { 275 clear(); 276 VSVariant v(VSVariant::vData); 277 v.append(errMsg); 278 insert("_Error", std::move(v)); 279 data->error = true; 280 } 281 hasErrorVSMap282 bool hasError() const { 283 return data->error; 284 } 285 getErrorMessageVSMap286 const std::string &getErrorMessage() const { 287 return *((*this)["_Error"].getValue<VSMapData>(0).get()); 288 } 289 }; 290 291 292 293 struct VSFrameRef { 294 PVideoFrame frame; VSFrameRefVSFrameRef295 VSFrameRef(const PVideoFrame &frame) : frame(frame) {} VSFrameRefVSFrameRef296 VSFrameRef(PVideoFrame &&frame) : frame(frame) {} 297 }; 298 299 struct VSNodeRef { 300 PVideoNode clip; 301 int index; VSNodeRefVSNodeRef302 VSNodeRef(const PVideoNode &clip, int index) : clip(clip), index(index) {} VSNodeRefVSNodeRef303 VSNodeRef(PVideoNode &&clip, int index) : clip(clip), index(index) {} 304 }; 305 306 struct VSFuncRef { 307 PExtFunction func; VSFuncRefVSFuncRef308 VSFuncRef(const PExtFunction &func) : func(func) {} VSFuncRefVSFuncRef309 VSFuncRef(PExtFunction &&func) : func(func) {} 310 }; 311 312 enum FilterArgumentType { 313 faNone = -1, 314 faInt = 0, 315 faFloat, 316 faData, 317 faClip, 318 faFrame, 319 faFunc 320 }; 321 322 class FilterArgument { 323 public: 324 std::string name; 325 FilterArgumentType type; 326 bool arr; 327 bool empty; 328 bool opt; FilterArgument(const std::string & name,FilterArgumentType type,bool arr,bool empty,bool opt)329 FilterArgument(const std::string &name, FilterArgumentType type, bool arr, bool empty, bool opt) 330 : name(name), type(type), arr(arr), empty(empty), opt(opt) {} 331 }; 332 333 class MemoryUse { 334 private: 335 struct BlockHeader { 336 size_t size; // Size of memory allocation, minus header and padding. 337 bool large : 1; // Memory is allocated with large pages. 338 }; 339 static_assert(sizeof(BlockHeader) <= 16, "block header too large"); 340 341 std::atomic<size_t> used; 342 size_t maxMemoryUse; 343 bool freeOnZero; 344 bool largePageEnabled; 345 bool memoryWarningIssued; 346 std::multimap<size_t, uint8_t *> buffers; 347 size_t unusedBufferSize; 348 std::minstd_rand generator; 349 std::mutex mutex; 350 351 static bool largePageSupported(); 352 static size_t largePageSize(); 353 354 // May allocate more than the requested amount. 355 void *allocateLargePage(size_t bytes) const; 356 void freeLargePage(void *ptr) const; 357 void *allocateMemory(size_t bytes) const; 358 void freeMemory(void *ptr) const; 359 bool isGoodFit(size_t requested, size_t actual) const; 360 public: 361 void add(size_t bytes); 362 void subtract(size_t bytes); 363 uint8_t *allocBuffer(size_t bytes); 364 void freeBuffer(uint8_t *buf); 365 size_t memoryUse(); 366 size_t getLimit(); 367 int64_t setMaxMemoryUse(int64_t bytes); 368 bool isOverLimit(); 369 void signalFree(); 370 MemoryUse(); 371 ~MemoryUse(); 372 }; 373 374 class VSPlaneData { 375 private: 376 std::atomic<int> refCount; 377 MemoryUse &mem; 378 public: 379 uint8_t *data; 380 const size_t size; 381 VSPlaneData(size_t dataSize, MemoryUse &mem); 382 VSPlaneData(const VSPlaneData &d); 383 ~VSPlaneData(); 384 bool unique(); 385 void addRef(); 386 void release(); 387 }; 388 389 class VSFrame { 390 private: 391 const VSFormat *format; 392 VSPlaneData *data[3]; 393 int width; 394 int height; 395 int stride[3]; 396 VSMap properties; 397 public: 398 static int alignment; 399 400 #ifdef VS_FRAME_GUARD 401 static const int guardSpace = 64; 402 #else 403 static const int guardSpace = 0; 404 #endif 405 406 VSFrame(const VSFormat *f, int width, int height, const VSFrame *propSrc, VSCore *core); 407 VSFrame(const VSFormat *f, int width, int height, const VSFrame * const *planeSrc, const int *plane, const VSFrame *propSrc, VSCore *core); 408 VSFrame(const VSFrame &f); 409 ~VSFrame(); 410 getProperties()411 VSMap &getProperties() { 412 return properties; 413 } getConstProperties()414 const VSMap &getConstProperties() const { 415 return properties; 416 } setProperties(const VSMap & properties)417 void setProperties(const VSMap &properties) { 418 this->properties = properties; 419 } getFormat()420 const VSFormat *getFormat() const { 421 return format; 422 } getWidth(int plane)423 int getWidth(int plane) const { 424 return width >> (plane ? format->subSamplingW : 0); 425 } getHeight(int plane)426 int getHeight(int plane) const { 427 return height >> (plane ? format->subSamplingH : 0); 428 } 429 int getStride(int plane) const; 430 const uint8_t *getReadPtr(int plane) const; 431 uint8_t *getWritePtr(int plane); 432 433 #ifdef VS_FRAME_GUARD 434 bool verifyGuardPattern(); 435 #endif 436 }; 437 438 class FrameContext { 439 friend class VSThreadPool; 440 private: 441 uintptr_t reqOrder; 442 unsigned numFrameRequests; 443 int n; 444 VSNode *clip; 445 PVideoFrame returnedFrame; 446 PFrameContext upstreamContext; 447 PFrameContext notificationChain; 448 void *userData; 449 VSFrameDoneCallback frameDone; 450 std::string errorMessage; 451 bool error; 452 bool lockOnOutput; 453 public: 454 VSNodeRef *node; 455 std::map<NodeOutputKey, PVideoFrame> availableFrames; 456 int lastCompletedN; 457 int index; 458 VSNodeRef *lastCompletedNode; 459 460 void *frameContext; 461 bool setError(const std::string &errorMsg); hasError()462 inline bool hasError() const { 463 return error; 464 } getErrorMessage()465 const std::string &getErrorMessage() { 466 return errorMessage; 467 } 468 FrameContext(int n, int index, VSNode *clip, const PFrameContext &upstreamContext); 469 FrameContext(int n, int index, VSNodeRef *node, VSFrameDoneCallback frameDone, void *userData, bool lockOnOutput = true); 470 }; 471 472 struct VSNode { 473 friend class VSThreadPool; 474 friend struct VSCore; 475 private: 476 void *instanceData; 477 std::string name; 478 VSFilterInit init; 479 VSFilterGetFrame filterGetFrame; 480 VSFilterFree free; 481 VSFilterMode filterMode; 482 483 int apiMajor; 484 VSCore *core; 485 int flags; 486 bool hasVi; 487 std::vector<VSVideoInfo> vi; 488 489 // for keeping track of when a filter is busy in the exclusive section and with which frame 490 // used for fmSerial and fmParallel (mutex only) 491 std::mutex serialMutex; 492 int serialFrame; 493 // to prevent multiple calls at the same time for the same frame 494 // this is used exclusively by fmParallel 495 // fmParallelRequests use this in combination with serialMutex to signal when all its frames are ready 496 std::mutex concurrentFramesMutex; 497 std::set<int> concurrentFrames; 498 499 PVideoFrame getFrameInternal(int n, int activationReason, VSFrameContext &frameCtx); 500 public: 501 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); 502 503 ~VSNode(); 504 isRightCoreVSNode505 bool isRightCore(const VSCore *core2) const { 506 return core == core2; 507 } 508 509 void getFrame(const PFrameContext &ct); 510 511 const VSVideoInfo &getVideoInfo(int index); 512 513 void setVideoInfo(const VSVideoInfo *vi, int numOutputs); 514 getNumOutputsVSNode515 size_t getNumOutputs() const { 516 return vi.size(); 517 } 518 getNameVSNode519 const std::string &getName() const { 520 return name; 521 } 522 523 // to get around encapsulation a bit, more elegant than making everything friends in this case 524 void reserveThread(); 525 void releaseThread(); 526 bool isWorkerThread(); 527 528 void notifyCache(bool needMemory); 529 }; 530 531 struct VSFrameContext { 532 PFrameContext &ctx; 533 std::vector<PFrameContext> reqList; VSFrameContextVSFrameContext534 VSFrameContext(PFrameContext &ctx) : ctx(ctx) {} 535 }; 536 537 class VSThreadPool { 538 friend struct VSCore; 539 private: 540 VSCore *core; 541 std::mutex lock; 542 std::mutex callbackLock; 543 std::map<std::thread::id, std::thread *> allThreads; 544 std::list<PFrameContext> tasks; 545 std::map<NodeOutputKey, PFrameContext> allContexts; 546 std::condition_variable newWork; 547 std::condition_variable allIdle; 548 std::atomic<unsigned> activeThreads; 549 std::atomic<unsigned> idleThreads; 550 std::atomic<uintptr_t> reqCounter; 551 unsigned maxThreads; 552 std::atomic<bool> stopThreads; 553 std::atomic<unsigned> ticks; 554 int getNumAvailableThreads(); 555 void wakeThread(); 556 void notifyCaches(bool needMemory); 557 void startInternal(const PFrameContext &context); 558 void spawnThread(); 559 static void runTasks(VSThreadPool *owner, std::atomic<bool> &stop); 560 static bool taskCmp(const PFrameContext &a, const PFrameContext &b); 561 public: 562 VSThreadPool(VSCore *core, int threads); 563 ~VSThreadPool(); 564 void returnFrame(const PFrameContext &rCtx, const PVideoFrame &f); 565 void returnFrame(const PFrameContext &rCtx, const std::string &errMsg); 566 int threadCount(); 567 int setThreadCount(int threads); 568 void start(const PFrameContext &context); 569 void releaseThread(); 570 void reserveThread(); 571 bool isWorkerThread(); 572 void waitForDone(); 573 }; 574 575 class VSFunction { 576 public: 577 std::vector<FilterArgument> args; 578 std::string argString; 579 void *functionData; 580 VSPublicFunction func; 581 VSFunction(const std::string &argString, VSPublicFunction func, void *functionData); VSFunction()582 VSFunction() : functionData(nullptr), func(nullptr) {} 583 }; 584 585 586 struct VSPlugin { 587 private: 588 int apiMajor; 589 int apiMinor; 590 bool hasConfig; 591 bool readOnly; 592 bool readOnlySet; 593 bool compat; 594 #ifdef VS_TARGET_OS_WINDOWS 595 HMODULE libHandle; 596 #else 597 void *libHandle; 598 #endif 599 std::map<std::string, VSFunction> funcs; 600 std::mutex registerFunctionLock; 601 VSCore *core; 602 public: 603 std::string filename; 604 std::string fullname; 605 std::string fnamespace; 606 std::string id; 607 explicit VSPlugin(VSCore *core); 608 VSPlugin(const std::string &relFilename, const std::string &forcedNamespace, const std::string &forcedId, bool altSearchPath, VSCore *core); 609 ~VSPlugin(); lockVSPlugin610 void lock() { 611 readOnly = true; 612 }; enableCompatVSPlugin613 void enableCompat() { 614 compat = true; 615 } 616 void configPlugin(const std::string &identifier, const std::string &defaultNamespace, const std::string &fullname, int apiVersion, bool readOnly); 617 void registerFunction(const std::string &name, const std::string &args, VSPublicFunction argsFunc, void *functionData); 618 VSMap invoke(const std::string &funcName, const VSMap &args); 619 VSMap getFunctions(); 620 }; 621 622 struct VSCore { 623 friend class VSFrame; 624 friend class VSThreadPool; 625 friend class CacheInstance; 626 private: 627 //number of filter instances plus one, freeing the core reduces it by one 628 // the core will be freed once it reaches 0 629 bool coreFreed; 630 std::atomic_int numFilterInstances; 631 std::atomic_int numFunctionInstances; 632 633 std::map<std::string, VSPlugin *> plugins; 634 std::recursive_mutex pluginLock; 635 std::map<int, VSFormat *> formats; 636 std::mutex formatLock; 637 int formatIdOffset; 638 VSCoreInfo coreInfo; 639 std::set<VSNode *> caches; 640 std::mutex cacheLock; 641 642 std::atomic_int cpuLevel; 643 644 ~VSCore(); 645 646 void registerFormats(); 647 #ifdef VS_TARGET_OS_WINDOWS 648 bool loadAllPluginsInPath(const std::wstring &path, const std::wstring &filter); 649 #else 650 bool loadAllPluginsInPath(const std::string &path, const std::string &filter); 651 #endif 652 public: 653 VSThreadPool *threadPool; 654 MemoryUse *memory; 655 656 PVideoFrame newVideoFrame(const VSFormat *f, int width, int height, const VSFrame *propSrc); 657 PVideoFrame newVideoFrame(const VSFormat *f, int width, int height, const VSFrame * const *planeSrc, const int *planes, const VSFrame *propSrc); 658 PVideoFrame copyFrame(const PVideoFrame &srcf); 659 void copyFrameProps(const PVideoFrame &src, PVideoFrame &dst); 660 661 const VSFormat *getFormatPreset(int id); 662 const VSFormat *registerFormat(VSColorFamily colorFamily, VSSampleType sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, const char *name = nullptr, int id = pfNone); 663 bool isValidFormatPointer(const VSFormat *f); 664 665 void loadPlugin(const std::string &filename, const std::string &forcedNamespace = std::string(), const std::string &forcedId = std::string(), bool altSearchPath = false); 666 void createFilter(const VSMap *in, VSMap *out, const std::string &name, VSFilterInit init, VSFilterGetFrame getFrame, VSFilterFree free, VSFilterMode filterMode, int flags, void *instanceData, int apiMajor); 667 668 int getCpuLevel() const; 669 int setCpuLevel(int cpu); 670 671 VSMap getPlugins(); 672 VSPlugin *getPluginById(const std::string &identifier); 673 VSPlugin *getPluginByNs(const std::string &ns); 674 675 const VSCoreInfo &getCoreInfo(); 676 void getCoreInfo2(VSCoreInfo &info); 677 678 void functionInstanceCreated(); 679 void functionInstanceDestroyed(); 680 void filterInstanceCreated(); 681 void filterInstanceDestroyed(); 682 void destroyFilterInstance(VSNode *node); 683 684 explicit VSCore(int threads); 685 void freeCore(); 686 }; 687 688 #endif // VSCORE_H 689