├── meson.build ├── LICENSE ├── .github └── workflows │ └── build.yaml ├── README.md └── api3.cc /meson.build: -------------------------------------------------------------------------------- 1 | project('Plugin', 'c', 'cpp', 2 | default_options: ['buildtype=release', 'b_ndebug=if-release', 'c_std=c99', 'cpp_std=c++17'], 3 | meson_version: '>=0.48.0', 4 | version: '8' 5 | ) 6 | 7 | vapoursynth_dep = dependency('vapoursynth').partial_dependency(compile_args: true, includes: true) 8 | 9 | libs = [] 10 | 11 | shared_module('api3', [ 'api3.cc' ], 12 | cpp_args: '-DSTANDALONE', 13 | dependencies: vapoursynth_dep, 14 | link_with: [], 15 | install: false, 16 | include_directories: [], 17 | gnu_symbol_visibility: 'hidden' 18 | ) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 私立七森中ごらく部 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | - push 4 | - release 5 | - pull_request 6 | - workflow_dispatch 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-2019 11 | strategy: 12 | matrix: 13 | arch: 14 | - amd64 15 | - x86 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: setup MS dev commands 19 | uses: ilammy/msvc-dev-cmd@v1 20 | with: 21 | arch: ${{ matrix.arch }} 22 | - name: Setup Python 23 | uses: actions/setup-python@v1 24 | with: 25 | python-version: '3.x' 26 | - name: install meson and ninja 27 | run: pip install meson ninja 28 | - name: download VS headers and patch header location 29 | shell: bash 30 | run: | 31 | git clone https://github.com/AmusementClub/vapoursynth-classic --depth=1 --branch doodle2 vapoursynth 32 | cp vapoursynth/include/*.h . 33 | sed -i -e '/vapoursynth_dep = /s/.*/vapoursynth_dep = []/' meson.build 34 | - name: Meson setup 35 | run: meson setup builddir/ -Db_vscrt=mt 36 | - name: Meson compile 37 | run: meson compile -C builddir/ -v 38 | - name: Upload artifact 39 | uses: actions/upload-artifact@v2 40 | with: 41 | name: release-${{matrix.arch}} 42 | path: | 43 | builddir/api3.dll 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VapourSynth api3-to-api4 brdige 2 | ------------------------------- 3 | 4 | # Introduction 5 | 6 | Recent VapourSynth releases (R55+) introduced a new API version (api4) that is 7 | not compatible with the previous plugin API (api3). Even though newer VS versions 8 | can load plugins using either api3 or api4, older VS versions are stuck with 9 | api3 plugins. This poses a dilemma for (video) plugin authors: 10 | - either you abandon users still stuck with older api3 VS versions by migrating to api4 11 | - or, you maintain two copies (branches) of essentially the same code. 12 | 13 | The best course of action, IMHO, is to stick with api3 to support both worlds 14 | without maintaining two copies of essentially the same code. 15 | 16 | As anyone who has migrated video plugin from api3 to api4 would testify, there 17 | aren't that many differences between the two APIs. One might ask: is it possible 18 | to support both both API versions without duplicating code? 19 | 20 | The answer is yes, and this project is one solution that should help with this 21 | situation. 22 | 23 | # Usage for End Users 24 | 25 | For end users of VS plugins, this project can be used to load api4 *vdieo* plugins 26 | in api3 VS. (api3 VS does not have audio support, so obviously this project won't 27 | change that.) 28 | 29 | Suppose you are stuck with VS R54, but you want to use a *video* plugin `filter.dll` 30 | that only has api4 support. You can download the released `api3.dll` and save it 31 | as `filter.api3.dll` along side with `filter.dll` into VS `plugins` directory. And 32 | then you should be able to use the filter in your api3 VS as usual. 33 | 34 | Note for Unix users: as this process involves making multiple copies of `api3.so` 35 | if you have multiple api4 plugins to wrap, in order to save disk space, please 36 | use *hard* links instead of symbolic links. 37 | 38 | # Usage for Plugin Developers 39 | 40 | For plugin developers, this project can help you support both api3 and api4 without 41 | any code duplication. Just migrate to api4 and include api3.cc in your project. And 42 | the rest should be automatically handled. 43 | 44 | Examples: 45 | - [TCanny](https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TCanny) has switched to api4 recently. By using this project, we just need to slightly [tweak](https://github.com/AmusementClub/VapourSynth-TCanny/commit/4700c10c0118a9178604240d3fe131bf72228e72) its build workflow to make a hybrid api3/api4 filter [release](https://github.com/AmusementClub/VapourSynth-TCanny/releases/tag/r13.AC2). 46 | - [AddGrain](https://github.com/HomeOfVapourSynthEvolution/VapourSynth-AddGrain), similar to TCanny. We [tweaked](https://github.com/AmusementClub/VapourSynth-AddGrain/commit/4e8d7ab18252f3f92ee2abdd26f998e3128e22a4#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1R29-R35) its build workflow to make a hybrid api3/api4 filter [release](https://github.com/AmusementClub/VapourSynth-AddGrain/releases/tag/r9.AC). 47 | - [TTempSmooth](https://github.com/HomeOfVapourSynthEvolution/VapourSynth-TTempSmooth), similar to TCanny. We [tweaked](https://github.com/AmusementClub/VapourSynth-TTempSmooth/commit/b7f90808114ca43f4e52386a1c8afad7be72334e#diff-d0777657fa3fd81d23aaf7273e58aee453b04e67882517900c56daeef9b3e4c1R29-R35) its build workflow to make a hybrid api3/api4 filter [release](https://github.com/AmusementClub/VapourSynth-TTempSmooth/releases/tag/r4.1-AC). 48 | - One [FFT3DFilter](https://github.com/myrsloik/VapourSynth-FFT3DFilter) fork has received some significant speed optimizations, but it's only available with an api4 interface. Again, we [tweaked](https://github.com/AmusementClub/VapourSynth-FFT3DFilter/commit/65a310689d29e7f4725b8169d3bac2c0f577367e) the build workflow to produce a hybrid api3/api4 filter [release](https://github.com/AmusementClub/VapourSynth-FFT3DFilter/releases/tag/R2.AC). 49 | -------------------------------------------------------------------------------- /api3.cc: -------------------------------------------------------------------------------- 1 | // This file serves a an api3-to-api4 bridge: it makes it easy to write plugins 2 | // that supports both api3 and api4 interfaces by translating api4 into api3. 3 | // 4 | // Only video filters are supported. 5 | // 6 | // Usage: 7 | // 1. if you are using it for a plugin, just include this file in your project, 8 | // and write your plugin using api4. This file will provide api3 entry point 9 | // and automatically translate your api4 calls into equivalent api3 calls. 10 | // 11 | // 2. Alternatively, you can build this file in standalone mode (by defining 12 | // the STANDALONE macro), and then place it along side your api4 only plugin 13 | // libplugin.dll/.so/.dylib as libplugin.api3.dll/.so/.dylib, and then api4 14 | // VS will load your plugin directly and api3 VS will load this bridge dll, 15 | // which in turns loads your api4 plugin. 16 | // 17 | // Drawbacks: 18 | // 1. It wastes some memory to provide api3/4 VideoInfo and Format conversion. 19 | // Defining USE_TLS macro will use thread local storage to save the cache 20 | // per filter instance so that the cache will not grow unbounded. 21 | // 2. On Unix, you can't use symlink to save space for various *.api3.so bridges, 22 | // as VS already uses realpath to resolve symlinks when loading plugins. 23 | #define VERSION "v2.1" 24 | #define USE_TLS // use per-filter-instance videoinfo/format cache 25 | #ifndef _GNU_SOURCE 26 | #define _GNU_SOURCE 27 | #endif 28 | #ifdef _WIN32 29 | #define _CRT_SECURE_NO_WARNINGS 30 | #endif 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | // Debug only 44 | #include 45 | #include 46 | static bool debug() { 47 | static bool d = getenv("DEBUG_V3BRIDGE") != nullptr; 48 | return d; 49 | } 50 | 51 | namespace vs4 { 52 | #include "VapourSynth4.h" 53 | 54 | static inline float doubleToFloatS(double d) { 55 | if (!isfinite(d)) 56 | return (float)d; 57 | else if (d > FLT_MAX) 58 | return FLT_MAX; 59 | else if (d < -FLT_MAX) 60 | return -FLT_MAX; 61 | else 62 | return (float)d; 63 | } 64 | 65 | [[maybe_unused]] const int api_version_major = VAPOURSYNTH_API_MAJOR; 66 | [[maybe_unused]] const int api_version_minor = VAPOURSYNTH_API_MINOR; 67 | const int api_version = VAPOURSYNTH_API_VERSION; 68 | #undef VAPOURSYNTH_API_MAJOR 69 | #undef VAPOURSYNTH_API_MINOR 70 | #undef VAPOURSYNTH_API_VERSION 71 | 72 | // copied from vscore.cpp 73 | static bool isValidVideoFormat(int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH) noexcept { 74 | if (colorFamily != cfUndefined && colorFamily != cfGray && colorFamily != cfYUV && colorFamily != cfRGB) 75 | return false; 76 | if (colorFamily == cfUndefined && (subSamplingH != 0 || subSamplingW != 0 || bitsPerSample != 0 || sampleType != stInteger)) 77 | return true; 78 | if (sampleType != stInteger && sampleType != stFloat) 79 | return false; 80 | if (sampleType == stFloat && (bitsPerSample != 16 && bitsPerSample != 32)) 81 | return false; 82 | if (subSamplingH < 0 || subSamplingW < 0 || subSamplingH > 4 || subSamplingW > 4) 83 | return false; 84 | if ((colorFamily == cfRGB || colorFamily == cfGray) && (subSamplingH != 0 || subSamplingW != 0)) 85 | return false; 86 | if (bitsPerSample < 8 || bitsPerSample > 32) 87 | return false; 88 | return true; 89 | } 90 | static bool getVideoFormatName(const VSVideoFormat &format, char *buffer) noexcept { 91 | if (!isValidVideoFormat(format.colorFamily, format.sampleType, format.bitsPerSample, format.subSamplingW, format.subSamplingH)) 92 | return false; 93 | char suffix[16]; 94 | if (format.sampleType == stFloat) 95 | strcpy(suffix, (format.bitsPerSample == 32) ? "S" : "H"); 96 | else 97 | sprintf(suffix, "%d", (format.colorFamily == cfRGB ? 3:1) * format.bitsPerSample); 98 | const char *yuvName = nullptr; 99 | switch (format.colorFamily) { 100 | case cfGray: 101 | snprintf(buffer, 32, "Gray%s", suffix); 102 | break; 103 | case cfRGB: 104 | snprintf(buffer, 32, "RGB%s", suffix); 105 | break; 106 | case cfYUV: 107 | if (format.subSamplingW == 1 && format.subSamplingH == 1) 108 | yuvName = "420"; 109 | else if (format.subSamplingW == 1 && format.subSamplingH == 0) 110 | yuvName = "422"; 111 | else if (format.subSamplingW == 0 && format.subSamplingH == 0) 112 | yuvName = "444"; 113 | else if (format.subSamplingW == 2 && format.subSamplingH == 2) 114 | yuvName = "410"; 115 | else if (format.subSamplingW == 2 && format.subSamplingH == 0) 116 | yuvName = "411"; 117 | else if (format.subSamplingW == 0 && format.subSamplingH == 1) 118 | yuvName = "440"; 119 | if (yuvName) 120 | snprintf(buffer, 32, "YUV%sP%s", yuvName, suffix); 121 | else 122 | snprintf(buffer, 32, "YUVssw%dssh%dP%s", format.subSamplingW, format.subSamplingH, suffix); 123 | break; 124 | case cfUndefined: 125 | snprintf(buffer, 32, "Undefined"); 126 | break; 127 | } 128 | return true; 129 | } 130 | } // namespace vs4 131 | 132 | namespace vs3 { 133 | #define getVapourSynthAPI getVapourSynthAPI3 134 | #include "VapourSynth.h" 135 | #undef getVapourSynthAPI 136 | #include "VSHelper.h" 137 | [[maybe_unused]] const int api_version_major = VAPOURSYNTH_API_MAJOR; 138 | [[maybe_unused]] const int api_version_minor = VAPOURSYNTH_API_MINOR; 139 | const int api_version = VAPOURSYNTH_API_VERSION; 140 | #undef VAPOURSYNTH_API_MAJOR 141 | #undef VAPOURSYNTH_API_MINOR 142 | #undef VAPOURSYNTH_API_VERSION 143 | 144 | #ifdef _WIN32 145 | static vs3::VSAPI *getAPI3(); 146 | #endif // _WIN32 147 | static const vs3::VSAPI &api3 = []() -> const vs3::VSAPI &{ 148 | #ifdef _WIN32 149 | auto p = getAPI3(); 150 | #else // ! _WIN32 151 | auto p = reinterpret_cast(vs4::getVapourSynthAPI(vs3::api_version)); 152 | #endif // _WIN32 153 | if (!p) { 154 | std::cerr << "v3bdg: unable to acquire api3 VSAPI, abort." << std::endl; 155 | abort(); 156 | } 157 | return *p; 158 | }(); 159 | } // namespace vs3 160 | 161 | namespace util { 162 | // Mostly copied from vapoursynth/src/core/vscore.cpp. 163 | struct split1 { 164 | enum empties_t { empties_ok, no_empties }; 165 | }; 166 | 167 | template 168 | static inline Container& split( 169 | Container& result, 170 | const typename Container::value_type& s, 171 | const typename Container::value_type& delimiters, 172 | split1::empties_t empties = split1::empties_ok) { 173 | result.clear(); 174 | size_t current; 175 | size_t next = -1; 176 | do { 177 | if (empties == split1::no_empties) { 178 | next = s.find_first_not_of(delimiters, next + 1); 179 | if (next == Container::value_type::npos) break; 180 | next -= 1; 181 | } 182 | current = next + 1; 183 | next = s.find_first_of(delimiters, current); 184 | result.push_back(s.substr(current, next - current)); 185 | } while (next != Container::value_type::npos); 186 | return result; 187 | } 188 | } // namespace util 189 | 190 | // Type conversion 191 | #define TYPE_PAIR(t3, t4) \ 192 | [[maybe_unused]] static inline vs3::t3 *C(vs4::t4 *p) { return reinterpret_cast(p); } \ 193 | [[maybe_unused]] static inline const vs3::t3 *C(const vs4::t4 *p) { return reinterpret_cast(p); } \ 194 | [[maybe_unused]] static inline vs4::t4 *C(vs3::t3 *p) { return reinterpret_cast(p); } \ 195 | [[maybe_unused]] static inline const vs4::t4 *C(const vs3::t3 *p) { return reinterpret_cast(p); } 196 | TYPE_PAIR(VSCore, VSCore); 197 | TYPE_PAIR(VSMap, VSMap); 198 | TYPE_PAIR(VSFrameRef, VSFrame); 199 | TYPE_PAIR(VSNodeRef, VSNode); 200 | TYPE_PAIR(VSFrameContext, VSFrameContext); 201 | TYPE_PAIR(VSFuncRef, VSFunction); 202 | TYPE_PAIR(VSPlugin, VSPlugin); 203 | #undef TYPE_PAIR 204 | static inline vs3::VSColorFamily C(vs4::VSColorFamily x) { 205 | switch ((int)x) { 206 | // vs4::VSColorFamily 207 | case vs4::cfUndefined: return vs3::VSColorFamily(0); // no api3 equivalent 208 | case vs4::cfGray: return vs3::cmGray; 209 | case vs4::cfRGB: return vs3::cmRGB; 210 | case vs4::cfYUV: return vs3::cmYUV; 211 | // vs3::VSColorFamily 212 | case vs3::cmGray: return vs3::cmGray; 213 | case vs3::cmRGB: return vs3::cmRGB; 214 | case vs3::cmYUV: return vs3::cmYUV; 215 | default: 216 | std::cerr << "unsupported color family " << x << std::endl; abort(); 217 | } 218 | } 219 | static inline vs4::VSColorFamily C(vs3::VSColorFamily x) { 220 | switch (x) { 221 | case vs3::cmGray: return vs4::cfGray; 222 | case vs3::cmRGB: return vs4::cfRGB; 223 | case vs3::cmYUV: return vs4::cfYUV; 224 | case vs3::cmYCoCg: 225 | std::cerr << "unsupported cmYCoCg" << std::endl; abort(); 226 | case vs3::cmCompat: 227 | std::cerr << "unsupported cmCompat" << std::endl; abort(); 228 | } 229 | abort(); // unreachable 230 | } 231 | static inline vs3::VSFilterMode C(vs4::VSFilterMode x) { 232 | switch (x) { 233 | case vs4::fmParallel: return vs3::fmParallel; 234 | case vs4::fmParallelRequests: return vs3::fmParallelRequests; 235 | case vs4::fmUnordered: return vs3::fmUnordered; 236 | case vs4::fmFrameState: return vs3::fmSerial; 237 | } 238 | abort(); // unreachable 239 | } 240 | 241 | struct pluginFunc { 242 | std::string name; 243 | vs4::VSPublicFunction func; 244 | vs4::VSFreeFunctionData free; 245 | void *data; 246 | 247 | pluginFunc(const std::string &name, vs4::VSPublicFunction func, vs4::VSFreeFunctionData free, void *data) : 248 | name(name), func(func), free(free), data(data) {} 249 | ~pluginFunc() { 250 | if (free) free(data); 251 | } 252 | }; 253 | 254 | struct filterData; 255 | 256 | static struct vs4::VSPlugin { 257 | vs3::VSConfigPlugin configFunc; 258 | vs3::VSRegisterFunction registerFunc; 259 | vs3::VSPlugin *plugin; 260 | std::string ns; 261 | std::vector> funcs; 262 | } myself; 263 | 264 | // Format handling 265 | static inline int copyVideoFormat(vs4::VSVideoFormat *format, const vs3::VSFormat *format3) { 266 | if (!format3) return 0; 267 | format->colorFamily = C((vs3::VSColorFamily)format3->colorFamily); 268 | format->sampleType = format3->sampleType; 269 | format->bitsPerSample = format3->bitsPerSample; 270 | format->bytesPerSample = format3->bytesPerSample; 271 | format->subSamplingW = format3->subSamplingW; 272 | format->subSamplingH = format3->subSamplingH; 273 | format->numPlanes = format3->numPlanes; 274 | return 1; 275 | } 276 | static inline int copyVideoInfo(vs4::VSVideoInfo *info, const vs3::VSVideoInfo *info3) { 277 | if (!info3) return 0; 278 | copyVideoFormat(&info->format, info3->format); 279 | info->fpsNum = info3->fpsNum; 280 | info->fpsDen = info3->fpsDen; 281 | info->width = info3->width; 282 | info->height = info3->height; 283 | info->numFrames = info3->numFrames; 284 | (void)info3->flags; 285 | return 1; 286 | } 287 | static inline int copyVideoInfo(vs3::VSVideoInfo *info, const vs4::VSVideoInfo *info4, vs4::VSCore *core) { 288 | if (!info4) return 0; 289 | const vs4::VSVideoFormat &format = info4->format; 290 | const vs3::VSFormat *format3 = vs3::api3.registerFormat(C((vs4::VSColorFamily)format.colorFamily), format.sampleType, format.bitsPerSample, format.subSamplingW, format.subSamplingH, C(core)); 291 | if (!format3) return 0; 292 | info->format = format3; 293 | info->fpsNum = info4->fpsNum; 294 | info->fpsDen = info4->fpsDen; 295 | info->width = info4->width; 296 | info->height = info4->height; 297 | info->numFrames = info4->numFrames; 298 | info->flags = 0; 299 | return 1; 300 | } 301 | 302 | static inline void hash_combine(size_t &seed, size_t v) { 303 | // similar to boost::hash_combine. 304 | seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2); 305 | } 306 | namespace std { 307 | template <> 308 | struct hash { 309 | size_t operator()(const vs3::VSVideoInfo &x) const { 310 | size_t seed = x.format->id; 311 | hash_combine(seed, x.fpsNum); 312 | hash_combine(seed, x.fpsDen); 313 | hash_combine(seed, x.width); 314 | hash_combine(seed, x.height); 315 | hash_combine(seed, x.numFrames); 316 | return seed; 317 | } 318 | }; 319 | template <> 320 | struct equal_to { 321 | bool operator() (const vs3::VSVideoInfo &l, const vs3::VSVideoInfo &r) const { 322 | return l.format->id == r.format->id && l.fpsNum == r.fpsNum && l.fpsDen == r.fpsDen && l.width == r.width && 323 | l.height == r.height && l.numFrames == r.numFrames; 324 | } 325 | }; 326 | } // namespace std 327 | 328 | // VSVideoFormat and VSVideoInfo cache 329 | static class formatCache { 330 | std::mutex lock; 331 | std::unordered_map> formats; 332 | std::unordered_map> videoInfos; 333 | public: 334 | vs4::VSVideoInfo *get(const vs3::VSVideoInfo *info3) { 335 | if (!info3) return nullptr; 336 | std::lock_guard g(lock); 337 | auto it = videoInfos.find(*info3); 338 | if (it != videoInfos.end()) 339 | return it->second.get(); 340 | std::unique_ptr info {new vs4::VSVideoInfo}; 341 | auto p = info.get(); 342 | copyVideoInfo(p, info3); 343 | videoInfos.emplace(*info3, std::move(info)); 344 | return p; 345 | } 346 | vs4::VSVideoFormat *get(const vs3::VSFormat *format3) { 347 | std::lock_guard g(lock); 348 | auto it = formats.find(format3->id); 349 | if (it != formats.end()) 350 | return it->second.get(); 351 | std::unique_ptr format {new vs4::VSVideoFormat}; 352 | auto p = format.get(); 353 | copyVideoFormat(p, format3); 354 | formats.emplace(format3->id, std::move(format)); 355 | return p; 356 | } 357 | } globalCache; 358 | static inline formatCache *currentCache(); 359 | 360 | namespace vs4 { 361 | #define TODO() do { std::cerr << "unimplemented " << __func__ << std::endl; abort(); } while (0) 362 | #define IGNORE() do { std::cerr << myself.ns << ".api3: ignored call " << __func__ << std::endl; abort(); } while (0) 363 | #define WONTIMPL() do { std::cerr << myself.ns << " calls missing function " << __func__ << std::endl; abort(); } while (0) 364 | 365 | /* Audio and video filter related including nodes */ 366 | static std::pair VS_CC createVideoFilter_(const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; 367 | static VSNode *VS_CC createVideoFilter2(const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; 368 | static void VS_CC createVideoFilter(VSMap *out, const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT; 369 | static void VS_CC createAudioFilter(VSMap *out, const char *name, const VSAudioInfo *ai, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT { WONTIMPL(); } 370 | static VSNode *VS_CC createAudioFilter2(const char *name, const VSAudioInfo *ai, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT { WONTIMPL(); } 371 | static int VS_CC setLinearFilter(VSNode *node) VS_NOEXCEPT { return 1; } 372 | static void VS_CC setCacheMode(VSNode *node, int mode) VS_NOEXCEPT { IGNORE(); } 373 | static void VS_CC setCacheOptions(VSNode *node, int fixedSize, int maxSize, int maxHistorySize) VS_NOEXCEPT { IGNORE(); } 374 | 375 | static void VS_CC freeNode(VSNode *node) VS_NOEXCEPT { vs3::api3.freeNode(C(node)); } 376 | static VSNode *VS_CC addNodeRef(VSNode *node) VS_NOEXCEPT { return C(vs3::api3.cloneNodeRef(C(node))); } 377 | static int VS_CC getNodeType(VSNode *node) VS_NOEXCEPT { return vs4::mtVideo; } 378 | static const VSVideoInfo *VS_CC getVideoInfo(VSNode *node) VS_NOEXCEPT { 379 | const vs3::VSVideoInfo *info3 = vs3::api3.getVideoInfo(C(node)); 380 | return currentCache()->get(info3); 381 | } 382 | static const VSAudioInfo *VS_CC getAudioInfo(VSNode *node) VS_NOEXCEPT { WONTIMPL(); } 383 | 384 | /* Frame related functions */ 385 | static VSFrame *VS_CC newVideoFrame(const VSVideoFormat *format, int width, int height, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT { 386 | const vs3::VSFormat *format3 = vs3::api3.registerFormat(C((VSColorFamily)format->colorFamily), format->sampleType, format->bitsPerSample, format->subSamplingW, format->subSamplingH, C(core)); 387 | return C(vs3::api3.newVideoFrame(format3, width, height, C(propSrc), C(core))); 388 | } 389 | static VSFrame *VS_CC newVideoFrame2(const VSVideoFormat *format, int width, int height, const VSFrame **planeSrc, const int *planes, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT { 390 | const vs3::VSFormat *format3 = vs3::api3.registerFormat(C((VSColorFamily)format->colorFamily), format->sampleType, format->bitsPerSample, format->subSamplingW, format->subSamplingH, C(core)); 391 | return C(vs3::api3.newVideoFrame2(format3, width, height, reinterpret_cast(planeSrc), planes, C(propSrc), C(core))); 392 | } 393 | static VSFrame *VS_CC newAudioFrame(const VSAudioFormat *format, int numSamples, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT { WONTIMPL(); } 394 | static VSFrame *VS_CC newAudioFrame2(const VSAudioFormat *format, int numSamples, const VSFrame **channelSrc, const int *channels, const VSFrame *propSrc, VSCore *core) VS_NOEXCEPT { WONTIMPL(); } 395 | static void VS_CC freeFrame(const VSFrame *f) VS_NOEXCEPT { vs3::api3.freeFrame(C(f)); } 396 | static const VSFrame *VS_CC addFrameRef(const VSFrame *f) VS_NOEXCEPT { return C(vs3::api3.cloneFrameRef(C(f))); } 397 | static VSFrame *VS_CC copyFrame(const VSFrame *f, VSCore *core) VS_NOEXCEPT { return C(vs3::api3.copyFrame(C(f), C(core))); } 398 | static const VSMap *VS_CC getFramePropertiesRO(const VSFrame *f) VS_NOEXCEPT { return C(vs3::api3.getFramePropsRO(C(f))); } 399 | static VSMap *VS_CC getFramePropertiesRW(VSFrame *f) VS_NOEXCEPT { return C(vs3::api3.getFramePropsRW(C(f))); } 400 | 401 | static ptrdiff_t VS_CC getStride(const VSFrame *f, int plane) VS_NOEXCEPT { return vs3::api3.getStride(C(f), plane); } 402 | static const uint8_t *VS_CC getReadPtr(const VSFrame *f, int plane) VS_NOEXCEPT { return vs3::api3.getReadPtr(C(f), plane); } 403 | static uint8_t *VS_CC getWritePtr(VSFrame *f, int plane) VS_NOEXCEPT { return vs3::api3.getWritePtr(C(f), plane); } 404 | 405 | static const VSVideoFormat *VS_CC getVideoFrameFormat(const VSFrame *f) VS_NOEXCEPT { 406 | const vs3::VSFormat *format3 = vs3::api3.getFrameFormat(C(f)); 407 | return currentCache()->get(format3); 408 | } 409 | static const VSAudioFormat *VS_CC getAudioFrameFormat(const VSFrame *f) VS_NOEXCEPT { WONTIMPL(); } 410 | static int VS_CC getFrameType(const VSFrame *f) VS_NOEXCEPT { return mtVideo; } 411 | static int VS_CC getFrameWidth(const VSFrame *f, int plane) VS_NOEXCEPT { return vs3::api3.getFrameWidth(C(f), plane); } 412 | static int VS_CC getFrameHeight(const VSFrame *f, int plane) VS_NOEXCEPT { return vs3::api3.getFrameHeight(C(f), plane); } 413 | static int VS_CC getFrameLength(const VSFrame *f) VS_NOEXCEPT { WONTIMPL(); } 414 | 415 | /* General format functions */ 416 | static int VS_CC getVideoFormatName(const VSVideoFormat *format, char *buffer) VS_NOEXCEPT { 417 | if (!format) return 0; 418 | return vs4::getVideoFormatName(*format, buffer); 419 | } 420 | static int VS_CC getAudioFormatName(const VSAudioFormat *format, char *buffer) VS_NOEXCEPT { WONTIMPL(); } 421 | static int VS_CC queryVideoFormat(VSVideoFormat *format, int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, VSCore *core) VS_NOEXCEPT { 422 | const vs3::VSFormat *format3 = vs3::api3.registerFormat(C((VSColorFamily)colorFamily), sampleType, bitsPerSample, subSamplingW, subSamplingH, C(core)); 423 | return copyVideoFormat(format, format3); 424 | } 425 | static int VS_CC queryAudioFormat(VSAudioFormat *format, int sampleType, int bitsPerSample, uint64_t channelLayout, VSCore *core) VS_NOEXCEPT { WONTIMPL(); } 426 | static uint32_t VS_CC queryVideoFormatID(int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, VSCore *core) VS_NOEXCEPT { 427 | if (!isValidVideoFormat(colorFamily, sampleType, bitsPerSample, subSamplingW, subSamplingH)) 428 | return 0; 429 | return ((colorFamily & 0xF) << 28) | ((sampleType & 0xF) << 24) | ((bitsPerSample & 0xFF) << 16) | ((subSamplingW & 0xFF) << 8) | ((subSamplingH & 0xFF) << 0); 430 | } 431 | static int VS_CC getVideoFormatByID(VSVideoFormat *format, uint32_t id, VSCore *core) VS_NOEXCEPT { 432 | if ((id & 0xFF000000) == 0 && (id & 0x00FFFFFF)) { // v3 format id 433 | const vs3::VSFormat *format3 = vs3::api3.getFormatPreset(id, C(core)); 434 | return copyVideoFormat(format, format3); 435 | } 436 | return queryVideoFormat(format, ((id >> 28) & 0xF), ((id >> 24) & 0xF), (id >> 16) & 0xFF, (id >> 8) & 0xFF, (id >> 0) & 0xFF, core); 437 | } 438 | 439 | /* Frame request and filter getframe functions */ 440 | static const VSFrame *VS_CC getFrame(int n, VSNode *node, char *errorMsg, int bufSize) VS_NOEXCEPT { return C(vs3::api3.getFrame(n, C(node), errorMsg, bufSize)); } 441 | static void VS_CC getFrameAsync(int n, VSNode *node, VSFrameDoneCallback callback, void *userData) VS_NOEXCEPT { vs3::api3.getFrameAsync(n, C(node), reinterpret_cast(callback), userData); } 442 | static const VSFrame *VS_CC getFrameFilter(int n, VSNode *node, VSFrameContext *frameCtx) VS_NOEXCEPT { return C(vs3::api3.getFrameFilter(n, C(node), C(frameCtx))); } 443 | static void VS_CC requestFrameFilter(int n, VSNode *node, VSFrameContext *frameCtx) VS_NOEXCEPT { vs3::api3.requestFrameFilter(n, C(node), C(frameCtx)); } 444 | static void VS_CC releaseFrameEarly(VSNode *node, int n, VSFrameContext *frameCtx) VS_NOEXCEPT { vs3::api3.releaseFrameEarly(C(node), n, C(frameCtx)); } 445 | static void VS_CC cacheFrame(const VSFrame *frame, int n, VSFrameContext *frameCtx) VS_NOEXCEPT { IGNORE(); } 446 | static void VS_CC setFilterError(const char *errorMessage, VSFrameContext *frameCtx) VS_NOEXCEPT { vs3::api3.setFilterError(errorMessage, C(frameCtx)); } 447 | 448 | /* External functions */ 449 | static void VS_CC pubFunc(const vs3::VSMap *in, vs3::VSMap *out, void *userData, vs3::VSCore *core, const vs3::VSAPI *vsapi); 450 | static void VS_CC pubFreeFunc(void *userData) { 451 | pluginFunc *pd = reinterpret_cast(userData); 452 | delete pd; 453 | } 454 | 455 | static VSFunction *VS_CC createFunction(VSPublicFunction func, void *userData, VSFreeFunctionData free, VSCore *core) VS_NOEXCEPT { 456 | auto *p = new pluginFunc("runtimeCreated", func, free, userData); 457 | return C(vs3::api3.createFunc(pubFunc, (void *)p, pubFreeFunc, C(core), &vs3::api3)); 458 | } 459 | static void VS_CC freeFunction(VSFunction *f) VS_NOEXCEPT { vs3::api3.freeFunc(C(f)); } 460 | static VSFunction *VS_CC addFunctionRef(VSFunction *f) VS_NOEXCEPT { return C(vs3::api3.cloneFuncRef(C(f))); } 461 | static void VS_CC callFunction(VSFunction *func, const VSMap *in, VSMap *out) VS_NOEXCEPT { vs3::api3.callFunc(C(func), C(in), C(out), nullptr, nullptr); } 462 | 463 | /* Map and property access functions */ 464 | static VSMap *VS_CC createMap(void) VS_NOEXCEPT { return C(vs3::api3.createMap()); } 465 | static void VS_CC freeMap(VSMap *map) VS_NOEXCEPT { vs3::api3.freeMap(C(map)); } 466 | static void VS_CC clearMap(VSMap *map) VS_NOEXCEPT { vs3::api3.clearMap(C(map)); } 467 | static void VS_CC mapSetError(VSMap *map, const char *errorMessage) VS_NOEXCEPT { vs3::api3.setError(C(map), errorMessage); } 468 | static const char *VS_CC mapGetError(const VSMap *map) VS_NOEXCEPT { return vs3::api3.getError(C(map)); } 469 | 470 | static int VS_CC mapNumKeys(const VSMap *map) VS_NOEXCEPT { return vs3::api3.propNumKeys(C(map)); } 471 | static const char *VS_CC mapGetKey(const VSMap *map, int index) VS_NOEXCEPT { return vs3::api3.propGetKey(C(map), index); } 472 | static int VS_CC mapDeleteKey(VSMap *map, const char *key) VS_NOEXCEPT { return vs3::api3.propDeleteKey(C(map), key); } 473 | static int VS_CC mapNumElements(const VSMap *map, const char *key) VS_NOEXCEPT { return vs3::api3.propNumElements(C(map), key); } 474 | static int VS_CC mapGetType(const VSMap *map, const char *key) VS_NOEXCEPT { 475 | int x = vs3::api3.propGetType(C(map), key); 476 | switch (x) { 477 | case vs3::ptUnset: return vs4::ptUnset; 478 | case vs3::ptInt: return vs4::ptInt; 479 | case vs3::ptFloat: return vs4::ptFloat; 480 | case vs3::ptData: return vs4::ptData; 481 | case vs3::ptNode: return vs4::ptVideoNode; 482 | case vs3::ptFrame: return vs4::ptVideoFrame; 483 | case vs3::ptFunction: return vs4::ptFunction; 484 | default: 485 | std::cerr << "mapGetType: unknown map type " << x << " for key " << key << std::endl; 486 | abort(); 487 | } 488 | } 489 | static int VS_CC mapSetEmpty(VSMap *map, const char *key, int type) VS_NOEXCEPT { 490 | if (mapGetType(map, key) != vs4::ptUnset) return 1; 491 | switch ((VSPropertyType)type) { 492 | case vs4::ptInt: vs3::api3.propSetInt(C(map), key, 0, vs3::paTouch); break; 493 | case vs4::ptFloat: vs3::api3.propSetFloat(C(map), key, 0, vs3::paTouch); break; 494 | case vs4::ptData: vs3::api3.propSetData(C(map), key, nullptr, 0, vs3::paTouch); break; 495 | case vs4::ptVideoNode: vs3::api3.propSetNode(C(map), key, nullptr, vs3::paTouch); break; 496 | case vs4::ptVideoFrame: vs3::api3.propSetFrame(C(map), key, nullptr, vs3::paTouch); break; 497 | case vs4::ptFunction: vs3::api3.propSetFunc(C(map), key, nullptr, vs3::paTouch); break; 498 | default: 499 | std::cerr << "mapSetEmpty: unknown map v4type " << type << " for key " << key << std::endl; 500 | abort(); 501 | } 502 | return 0; 503 | } 504 | 505 | #define MAP_GET2(expr, func) do { \ 506 | if (error && mapGetError(map) != nullptr) { \ 507 | *error = peError; \ 508 | return 0; \ 509 | } \ 510 | decltype(vs3::api3.expr) r = (vs3::api3.expr); \ 511 | if (error) { \ 512 | switch (*error) { \ 513 | case 0: *error = peSuccess; break; \ 514 | case peUnset: *error = peUnset; break; \ 515 | case peType: *error = peType; break; \ 516 | case peIndex: *error = peIndex; break; \ 517 | } \ 518 | } \ 519 | return func(r); \ 520 | } while (0) 521 | #define MAP_GET(expr) MAP_GET2(expr,) 522 | 523 | #define MAP_SET2(expr, post) do { \ 524 | switch ((VSMapAppendMode)append) { \ 525 | case maAppend: append = vs3::paAppend; break; \ 526 | case maReplace: append = vs3::paReplace; break; \ 527 | } \ 528 | decltype(vs3::api3.expr) r = vs3::api3.expr; \ 529 | post; \ 530 | return r; \ 531 | } while(0) 532 | #define MAP_SET(expr) MAP_SET2(expr,) 533 | 534 | static int64_t VS_CC mapGetInt(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET(propGetInt(C(map), key, index, error)); } 535 | static int VS_CC mapGetIntSaturated(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET2(propGetInt(C(map), key, index, error), vs3::int64ToIntS); } 536 | static const int64_t *VS_CC mapGetIntArray(const VSMap *map, const char *key, int *error) VS_NOEXCEPT { MAP_GET(propGetIntArray(C(map), key, error)); } 537 | static int VS_CC mapSetInt(VSMap *map, const char *key, int64_t i, int append) VS_NOEXCEPT { MAP_SET(propSetInt(C(map), key, i, append)); } 538 | static int VS_CC mapSetIntArray(VSMap *map, const char *key, const int64_t *i, int size) VS_NOEXCEPT { return vs3::api3.propSetIntArray(C(map), key, i, size); } 539 | 540 | static double VS_CC mapGetFloat(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET(propGetFloat(C(map), key, index, error)); } 541 | static float VS_CC mapGetFloatSaturated(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET2(propGetFloat(C(map), key, index, error), vs4::doubleToFloatS); } 542 | static const double *VS_CC mapGetFloatArray(const VSMap *map, const char *key, int *error) VS_NOEXCEPT { MAP_GET(propGetFloatArray(C(map), key, error)); } 543 | static int VS_CC mapSetFloat(VSMap *map, const char *key, double d, int append) VS_NOEXCEPT { MAP_SET(propSetFloat(C(map), key, d, append)); } 544 | static int VS_CC mapSetFloatArray(VSMap *map, const char *key, const double *d, int size) VS_NOEXCEPT { return vs3::api3.propSetFloatArray(C(map), key, d, size); } 545 | 546 | static const char *VS_CC mapGetData(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET(propGetData(C(map), key, index, error)); } 547 | static int VS_CC mapGetDataSize(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET(propGetDataSize(C(map), key, index, error)); } 548 | static int VS_CC mapGetDataTypeHint(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { return dtUnknown; } 549 | static int VS_CC mapSetData(VSMap *map, const char *key, const char *data, int size, int type, int append) VS_NOEXCEPT { MAP_SET(propSetData(C(map), key, data, size, append)); } 550 | 551 | static VSNode *VS_CC mapGetNode(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET2(propGetNode(C(map), key, index, error), C); } 552 | static int VS_CC mapSetNode(VSMap *map, const char *key, VSNode *node, int append) VS_NOEXCEPT { MAP_SET(propSetNode(C(map), key, C(node), append)); } 553 | static int VS_CC mapConsumeNode(VSMap *map, const char *key, VSNode *node, int append) VS_NOEXCEPT { MAP_SET2(propSetNode(C(map), key, C(node), append), freeNode(node)); } 554 | 555 | static const VSFrame *VS_CC mapGetFrame(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET2(propGetFrame(C(map), key, index, error), C); } 556 | static int VS_CC mapSetFrame(VSMap *map, const char *key, const VSFrame *f, int append) VS_NOEXCEPT { MAP_SET(propSetFrame(C(map), key, C(f), append)); } 557 | static int VS_CC mapConsumeFrame(VSMap *map, const char *key, const VSFrame *f, int append) VS_NOEXCEPT { MAP_SET2(propSetFrame(C(map), key, C(f), append), freeFrame(f)); } 558 | 559 | static VSFunction *VS_CC mapGetFunction(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT { MAP_GET2(propGetFunc(C(map), key, index, error), C); } 560 | static int VS_CC mapSetFunction(VSMap *map, const char *key, VSFunction *func, int append) VS_NOEXCEPT { MAP_SET(propSetFunc(C(map), key, C(func), append)); } 561 | static int VS_CC mapConsumeFunction(VSMap *map, const char *key, VSFunction *func, int append) VS_NOEXCEPT { MAP_SET2(propSetFunc(C(map), key, C(func), append), freeFunction(func)); } 562 | 563 | static void VS_CC copyMap(const VSMap *src, VSMap *dst) VS_NOEXCEPT { 564 | int nkeys = mapNumKeys(src); 565 | for (int i = 0; i < nkeys; i++) { 566 | const char *key = mapGetKey(src, i); 567 | int t = mapGetType(src, key); 568 | switch (t) { 569 | case vs4::ptInt: 570 | mapSetIntArray(dst, key, mapGetIntArray(src, key, nullptr), mapNumElements(src, key)); break; 571 | case vs4::ptFloat: 572 | mapSetFloatArray(dst, key, mapGetFloatArray(src, key, nullptr), mapNumElements(src, key)); break; 573 | case vs4::ptData: { 574 | mapDeleteKey(dst, key); 575 | int n = mapNumElements(src, key); 576 | for (int i = 0; i < n; i++) 577 | mapSetData(dst, key, mapGetData(src, key, i, nullptr), mapGetDataSize(src, key, i, nullptr), dtUnknown, maAppend); 578 | break; 579 | } 580 | case vs4::ptVideoNode: { 581 | mapDeleteKey(dst, key); 582 | int n = mapNumElements(src, key); 583 | for (int i = 0; i < n; i++) 584 | mapConsumeNode(dst, key, mapGetNode(src, key, i, nullptr), maAppend); 585 | break; 586 | } 587 | case vs4::ptVideoFrame: { 588 | mapDeleteKey(dst, key); 589 | int n = mapNumElements(src, key); 590 | for (int i = 0; i < n; i++) 591 | mapConsumeFrame(dst, key, mapGetFrame(src, key, i, nullptr), maAppend); 592 | break; 593 | } 594 | case vs4::ptFunction: { 595 | mapDeleteKey(dst, key); 596 | int n = mapNumElements(src, key); 597 | for (int i = 0; i < n; i++) 598 | mapConsumeFunction(dst, key, mapGetFunction(src, key, i, nullptr), maAppend); 599 | break; 600 | } 601 | default: 602 | std::cerr << "copyMap: unknown map type " << t << " for key " << key << std::endl; 603 | abort(); 604 | } 605 | } 606 | } 607 | 608 | #undef MAP_SET 609 | #undef MAP_SET2 610 | #undef MAP_GET 611 | #undef MAP_GET2 612 | 613 | /* Plugin and plugin function related */ 614 | static int VS_CC registerFunction(const char *name, const char *args, const char *returnType, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin) VS_NOEXCEPT; 615 | 616 | static VSPlugin *VS_CC getPluginByID(const char *identifier, VSCore *core) VS_NOEXCEPT { return C(vs3::api3.getPluginById(identifier, C(core))); } 617 | static VSPlugin *VS_CC getPluginByNamespace(const char *ns, VSCore *core) VS_NOEXCEPT { return C(vs3::api3.getPluginByNs(ns, C(core))); } 618 | static VSPlugin *VS_CC getNextPlugin(VSPlugin *plugin, VSCore *core) VS_NOEXCEPT { TODO(); } 619 | static const char *VS_CC getPluginName(VSPlugin *plugin) VS_NOEXCEPT { TODO(); } 620 | static const char *VS_CC getPluginID(VSPlugin *plugin) VS_NOEXCEPT { TODO(); } 621 | static const char *VS_CC getPluginNamespace(VSPlugin *plugin) VS_NOEXCEPT { TODO(); } 622 | static VSPluginFunction *VS_CC getNextPluginFunction(VSPluginFunction *func, VSPlugin *plugin) VS_NOEXCEPT { TODO(); } 623 | static VSPluginFunction *VS_CC getPluginFunctionByName(const char *name, VSPlugin *plugin) VS_NOEXCEPT { TODO(); } 624 | static const char *VS_CC getPluginFunctionName(VSPluginFunction *func) VS_NOEXCEPT { TODO(); } 625 | static const char *VS_CC getPluginFunctionArguments(VSPluginFunction *func) VS_NOEXCEPT { TODO(); } 626 | static const char *VS_CC getPluginFunctionReturnType(VSPluginFunction *func) VS_NOEXCEPT { TODO(); } 627 | static const char *VS_CC getPluginPath(const VSPlugin *plugin) VS_NOEXCEPT { 628 | return vs3::api3.getPluginPath(C(plugin)); 629 | } 630 | static int VS_CC getPluginVersion(const VSPlugin *plugin) VS_NOEXCEPT { return 1; } 631 | static VSMap *VS_CC invoke(VSPlugin *plugin, const char *name, const VSMap *args) VS_NOEXCEPT { 632 | if (strcmp(name, "ShufflePlanes") == 0) { 633 | int err; 634 | vs4::VSColorFamily cf = (vs4::VSColorFamily)vs3::api3.propGetInt(C(args), "colorfamily", 0, &err); 635 | if (err == 0) 636 | vs3::api3.propSetInt((vs3::VSMap *)C(args), "colorfamily", (int)C(cf), vs3::paReplace); 637 | } 638 | return C(vs3::api3.invoke(C(plugin), name, C(args))); 639 | } 640 | 641 | /* Core and information */ 642 | static VSCore *VS_CC createCore(int flags) VS_NOEXCEPT { WONTIMPL(); } 643 | static void VS_CC freeCore(VSCore *core) VS_NOEXCEPT { WONTIMPL(); } 644 | static int64_t VS_CC setMaxCacheSize(int64_t bytes, VSCore *core) VS_NOEXCEPT { return vs3::api3.setMaxCacheSize(bytes, C(core)); } 645 | static int VS_CC setThreadCount(int threads, VSCore *core) VS_NOEXCEPT { return vs3::api3.setThreadCount(threads, C(core)); } 646 | static void VS_CC getCoreInfo(VSCore *core, VSCoreInfo *info) VS_NOEXCEPT { 647 | vs3::VSCoreInfo info3; 648 | vs3::api3.getCoreInfo2(C(core), &info3); 649 | info->versionString = info3.versionString; 650 | info->core = info3.core; 651 | info->api = info3.api; 652 | info->numThreads = info3.numThreads; 653 | info->maxFramebufferSize = info3.maxFramebufferSize; 654 | info->usedFramebufferSize = info3.usedFramebufferSize; 655 | } 656 | static int VS_CC getAPIVersion(void) VS_NOEXCEPT { return vs4::api_version; } 657 | 658 | /* Message handler */ 659 | static void VS_CC logMessage(int msgType, const char *msg, VSCore *core) VS_NOEXCEPT { 660 | switch ((VSMessageType)msgType) { 661 | case mtDebug: msgType = vs3::mtDebug; break; 662 | case mtInformation: msgType = vs3::mtDebug; break; // no api3 equivalent 663 | case mtWarning: msgType = vs3::mtWarning; break; 664 | case mtCritical: msgType = vs3::mtCritical; break; 665 | case mtFatal: msgType = vs3::mtFatal; break; 666 | } 667 | vs3::api3.logMessage(msgType, msg); // no support for pre-core logging 668 | } 669 | 670 | static VSLogHandle *VS_CC addLogHandler(VSLogHandler handler, VSLogHandlerFree free, void *userData, VSCore *core) VS_NOEXCEPT { 671 | return reinterpret_cast((uintptr_t)vs3::api3.addMessageHandler( 672 | reinterpret_cast(handler), 673 | reinterpret_cast(free), userData)); // no support for per-core logging 674 | } 675 | static int VS_CC removeLogHandler(VSLogHandle *handle, VSCore *core) VS_NOEXCEPT { 676 | return vs3::api3.removeMessageHandler((int)reinterpret_cast(handle)); 677 | } 678 | 679 | static const VSAPI api4 = { 680 | &createVideoFilter, 681 | &createVideoFilter2, 682 | &createAudioFilter, 683 | &createAudioFilter2, 684 | &setLinearFilter, 685 | &setCacheMode, 686 | &setCacheOptions, 687 | &freeNode, 688 | &addNodeRef, 689 | &getNodeType, 690 | &getVideoInfo, 691 | &getAudioInfo, 692 | &newVideoFrame, 693 | &newVideoFrame2, 694 | &newAudioFrame, 695 | &newAudioFrame2, 696 | &freeFrame, 697 | &addFrameRef, 698 | ©Frame, 699 | &getFramePropertiesRO, 700 | &getFramePropertiesRW, 701 | &getStride, 702 | &getReadPtr, 703 | &getWritePtr, 704 | &getVideoFrameFormat, 705 | &getAudioFrameFormat, 706 | &getFrameType, 707 | &getFrameWidth, 708 | &getFrameHeight, 709 | &getFrameLength, 710 | &getVideoFormatName, 711 | &getAudioFormatName, 712 | &queryVideoFormat, 713 | &queryAudioFormat, 714 | &queryVideoFormatID, 715 | &getVideoFormatByID, 716 | &getFrame, 717 | &getFrameAsync, 718 | &getFrameFilter, 719 | &requestFrameFilter, 720 | &releaseFrameEarly, 721 | &cacheFrame, 722 | &setFilterError, 723 | &createFunction, 724 | &freeFunction, 725 | &addFunctionRef, 726 | &callFunction, 727 | &createMap, 728 | &freeMap, 729 | &clearMap, 730 | ©Map, 731 | &mapSetError, 732 | &mapGetError, 733 | &mapNumKeys, 734 | &mapGetKey, 735 | &mapDeleteKey, 736 | &mapNumElements, 737 | &mapGetType, 738 | &mapSetEmpty, 739 | &mapGetInt, 740 | &mapGetIntSaturated, 741 | &mapGetIntArray, 742 | &mapSetInt, 743 | &mapSetIntArray, 744 | &mapGetFloat, 745 | &mapGetFloatSaturated, 746 | &mapGetFloatArray, 747 | &mapSetFloat, 748 | &mapSetFloatArray, 749 | &mapGetData, 750 | &mapGetDataSize, 751 | &mapGetDataTypeHint, 752 | &mapSetData, 753 | &mapGetNode, 754 | &mapSetNode, 755 | &mapConsumeNode, 756 | &mapGetFrame, 757 | &mapSetFrame, 758 | &mapConsumeFrame, 759 | &mapGetFunction, 760 | &mapSetFunction, 761 | &mapConsumeFunction, 762 | ®isterFunction, 763 | &getPluginByID, 764 | &getPluginByNamespace, 765 | &getNextPlugin, 766 | &getPluginName, 767 | &getPluginID, 768 | &getPluginNamespace, 769 | &getNextPluginFunction, 770 | &getPluginFunctionByName, 771 | &getPluginFunctionName, 772 | &getPluginFunctionArguments, 773 | &getPluginFunctionReturnType, 774 | &getPluginPath, 775 | &getPluginVersion, 776 | &invoke, 777 | &createCore, 778 | &freeCore, 779 | &setMaxCacheSize, 780 | &setThreadCount, 781 | &getCoreInfo, 782 | &getAPIVersion, 783 | &logMessage, 784 | &addLogHandler, 785 | &removeLogHandler, 786 | }; 787 | 788 | #undef WONTIMPL 789 | #undef IGNORE 790 | #undef TODO 791 | } // namespace vs4 792 | 793 | // Filter Wrapper 794 | struct filterData { 795 | vs4::VSFilterGetFrame getFrameFunc; 796 | vs4::VSFilterFree freeFunc; 797 | void *data; 798 | vs3::VSMap *tmpMap; 799 | vs3::VSVideoInfo info3; 800 | formatCache cache; 801 | 802 | filterData(vs4::VSFilterGetFrame getFrameFunc, vs4::VSFilterFree freeFunc, void *data): 803 | getFrameFunc(getFrameFunc), freeFunc(freeFunc), data(data) { 804 | tmpMap = vs3::api3.createMap(); 805 | } 806 | ~filterData() { 807 | vs3::api3.freeMap(tmpMap); 808 | } 809 | }; 810 | #ifdef USE_TLS 811 | static thread_local filterData *current; 812 | class setCurrent { 813 | filterData *old; 814 | public: 815 | setCurrent(filterData *d) { old = current; current = d; } 816 | ~setCurrent() { current = old; } 817 | }; 818 | #else 819 | struct setCurrent { 820 | setCurrent(filterData *d) {} 821 | }; 822 | #endif 823 | 824 | static inline formatCache *currentCache() { 825 | #ifdef USE_TLS 826 | if (current) return ¤t->cache; 827 | #endif 828 | return &globalCache; 829 | } 830 | 831 | static void VS_CC initFunc3(vs3::VSMap *in, vs3::VSMap *out, void **instanceData, vs3::VSNode *node, vs3::VSCore *core, const vs3::VSAPI *vsapi) { 832 | filterData *fd = reinterpret_cast(*instanceData); 833 | vs3::api3.setVideoInfo(&fd->info3, 1, node); 834 | } 835 | static const vs3::VSFrameRef * VS_CC getFrameFunc3(int n, int activationReason, void **instanceData, void **frameData, vs3::VSFrameContext *frameCtx, vs3::VSCore *core, const vs3::VSAPI *vsapi) { 836 | filterData *fd = reinterpret_cast(*instanceData); 837 | setCurrent _(fd); 838 | void *ctx[4] = { nullptr, nullptr, nullptr, nullptr }; 839 | void **fdata = *frameData ? reinterpret_cast(*frameData) : &ctx[0]; 840 | switch (activationReason) { 841 | case vs3::arFrameReady: return nullptr; 842 | case vs3::arError: { 843 | auto r = fd->getFrameFunc(n, vs4::arError, fd->data, fdata, C(frameCtx), C(core), &vs4::api4); 844 | if (*frameData) { 845 | free(*frameData); 846 | *frameData = nullptr; 847 | } 848 | return C(r); 849 | } 850 | case vs3::arInitial: { 851 | auto r = fd->getFrameFunc(n, vs4::arInitial, fd->data, fdata, C(frameCtx), C(core), &vs4::api4); 852 | if (fdata[0]) { 853 | *frameData = malloc(4 * sizeof(void *)); 854 | memcpy(*frameData, fdata, 4 * sizeof(void *)); 855 | } 856 | return C(r); 857 | } 858 | case vs3::arAllFramesReady: { 859 | auto r = fd->getFrameFunc(n, vs4::arAllFramesReady, fd->data, fdata, C(frameCtx), C(core), &vs4::api4); 860 | if (r && *frameData) { 861 | free(*frameData); 862 | *frameData = nullptr; 863 | } 864 | return C(r); 865 | } 866 | } 867 | return nullptr; 868 | } 869 | static void VS_CC freeFunc3(void *instanceData, vs3::VSCore *core, const vs3::VSAPI *vsapi) { 870 | filterData *fd = reinterpret_cast(instanceData); 871 | setCurrent _(fd); 872 | fd->freeFunc(fd->data, C(core), &vs4::api4); 873 | delete fd; 874 | } 875 | 876 | namespace vs4 { 877 | static std::pair VS_CC createVideoFilter_(const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT { 878 | (void) dependencies; 879 | auto mode = C((vs4::VSFilterMode)filterMode); 880 | bool isSrcFilter = numDeps == 0 && mode == vs3::fmUnordered; 881 | auto fd = new filterData { getFrame, free, instanceData }; 882 | copyVideoInfo(&fd->info3, vi, core); 883 | std::string name2(name); 884 | name2 += ".api4"; 885 | vs3::api3.createFilter(fd->tmpMap, fd->tmpMap, name2.c_str(), initFunc3, getFrameFunc3, freeFunc3, mode, 886 | isSrcFilter ? vs3::nfMakeLinear : 0, reinterpret_cast(fd), C(core)); 887 | const char *errMsg = vs3::api3.getError(fd->tmpMap); 888 | if (errMsg) { 889 | std::cerr << "api3.createFilter failed: " << errMsg << std::endl; 890 | delete fd; 891 | return {nullptr, nullptr}; 892 | } 893 | auto node = vs3::api3.propGetNode(fd->tmpMap, "clip", 0, nullptr); 894 | vs3::api3.propDeleteKey(fd->tmpMap, "clip"); 895 | return {C(node), fd}; 896 | } 897 | static VSNode *VS_CC createVideoFilter2(const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT { 898 | auto p = createVideoFilter_(name, vi, getFrame, free, filterMode, dependencies, numDeps, instanceData, core); 899 | return p.first; 900 | } 901 | static void VS_CC createVideoFilter(VSMap *out, const char *name, const VSVideoInfo *vi, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, const VSFilterDependency *dependencies, int numDeps, void *instanceData, VSCore *core) VS_NOEXCEPT { 902 | auto p = createVideoFilter_(name, vi, getFrame, free, filterMode, dependencies, numDeps, instanceData, core); 903 | if (!p.first) { 904 | vs4::api4.mapSetError(out, vs3::api3.getError(p.second->tmpMap)); 905 | return; 906 | } 907 | vs4::api4.mapConsumeNode(out, "clip", p.first, vs4::maAppend); 908 | } 909 | } // namespace vs4 910 | 911 | // Plugin API 912 | static int VS_CC getAPIVersion(void) VS_NOEXCEPT { return vs4::api_version; } 913 | static int VS_CC configPlugin(const char *identifier, const char *pluginNamespace, const char *name, int pluginVersion, int apiVersion, int flags, vs4::VSPlugin *plugin) VS_NOEXCEPT { 914 | plugin->configFunc(identifier, pluginNamespace, name, vs3::api_version, flags == 0, plugin->plugin); 915 | plugin->ns = pluginNamespace; 916 | return 1; 917 | } 918 | static void VS_CC vs4::pubFunc(const vs3::VSMap *in, vs3::VSMap *out, void *userData, vs3::VSCore *core, const vs3::VSAPI *vsapi) { 919 | pluginFunc *pd = reinterpret_cast(userData); 920 | pd->func(C(in), C(out), pd->data, C(core), &vs4::api4); 921 | } 922 | 923 | static int VS_CC vs4::registerFunction(const char *name, const char *args, const char *returnType, vs4::VSPublicFunction argsFunc, void *functionData, vs4::VSPlugin *plugin) VS_NOEXCEPT { 924 | std::vector argList, argsOut; 925 | util::split(argList, std::string(args), std::string(";"), util::split1::no_empties); 926 | std::string args3; 927 | for (const std::string &arg : argList) { 928 | std::vector argParts; 929 | util::split(argParts, arg, std::string(":"), util::split1::no_empties); 930 | 931 | if (argParts.size() == 1 && argParts[0] == "any") 932 | return 0; // no support for "any" 933 | 934 | std::string &typeName = argParts[1]; 935 | bool arr = false; 936 | if (typeName.length() > 2 && typeName.substr(typeName.length() - 2) == "[]") { 937 | typeName.resize(typeName.length() - 2); 938 | arr = true; 939 | } 940 | if (typeName == "anode" || typeName == "aframe") 941 | return 0; // no support for audio filters 942 | else if (typeName == "vnode") typeName = "clip"; 943 | else if (typeName == "vframe") typeName = "frame"; 944 | 945 | args3 += argParts[0] + ":"; 946 | args3 += typeName; 947 | if (arr) args3 += "[]"; 948 | if (argParts.size() == 3) 949 | args3 += ":" + argParts[2]; 950 | args3 += ";"; 951 | } 952 | if (debug()) std::cerr << "function " << plugin->ns << "." << name << ": arg4 = " << args << " -> arg3 = " << args3 << std::endl; 953 | std::unique_ptr pf(new pluginFunc(name, argsFunc, nullptr, functionData)); 954 | plugin->funcs.push_back(std::move(pf)); 955 | plugin->registerFunc(name, args3.c_str(), pubFunc, plugin->funcs.back().get(), plugin->plugin); 956 | return 1; 957 | } 958 | static vs4::VSPLUGINAPI pluginAPI4 { 959 | getAPIVersion, 960 | configPlugin, 961 | vs4::registerFunction, 962 | }; 963 | 964 | static void wrap( 965 | vs4::VSInitPlugin init, 966 | vs3::VSConfigPlugin configFunc, vs3::VSRegisterFunction registerFunc, vs3::VSPlugin *plugin) { 967 | myself.configFunc = configFunc; 968 | myself.registerFunc = registerFunc; 969 | myself.plugin = plugin; 970 | init(&myself, &pluginAPI4); 971 | } 972 | 973 | #ifdef _WIN32 974 | #include 975 | static vs3::VSAPI *vs3::getAPI3() { 976 | HMODULE h = LoadLibraryW(L"VapourSynth.dll"); 977 | if (!h) return nullptr; 978 | vs3::VSAPI *(VS_CC *f)(int) = (decltype(f))(void *)GetProcAddress(h, "getVapourSynthAPI"); 979 | if (!f) f = (decltype(f))(void *)GetProcAddress(h, "_getVapourSynthAPI@4"); 980 | return f(vs3::api_version); 981 | } 982 | #endif 983 | 984 | #ifndef STANDALONE 985 | VS_EXTERNAL_API(void) VapourSynthPluginInit2(vs4::VSPlugin *plugin, const vs4::VSPLUGINAPI *vspapi); 986 | VS_EXTERNAL_API(void) VapourSynthPluginInit(vs3::VSConfigPlugin configFunc, vs3::VSRegisterFunction registerFunc, vs3::VSPlugin *plugin) { 987 | wrap(VapourSynthPluginInit2, configFunc, registerFunc, plugin); 988 | } 989 | #else // STANDALONE 990 | #ifdef _WIN32 991 | static vs4::VSInitPlugin getInit4() { 992 | HMODULE mod = 0; 993 | if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (char *)getInit4, &mod) == 0) 994 | throw std::runtime_error("unable to locate myself"); 995 | std::vector buf; 996 | size_t n = 0; 997 | do { 998 | buf.resize(buf.size() + MAX_PATH); 999 | n = GetModuleFileNameW(mod, buf.data(), buf.size()); 1000 | } while (n >= buf.size()); 1001 | buf.resize(n); 1002 | std::wstring path(buf.begin(), buf.end()); 1003 | size_t idx = path.rfind(L'.', path.size() - 5); // skip trailing ".dll" 1004 | if (idx == std::wstring::npos) return nullptr; 1005 | std::wstring dllPath = path.substr(0, idx) + L".dll"; 1006 | if (debug()) 1007 | std::wcerr << L"v3bdg: self = " << path << L", dll = " << dllPath << std::endl; 1008 | HMODULE h = LoadLibraryW(dllPath.c_str()); 1009 | if (h == 0) 1010 | return nullptr; 1011 | void *f = (void *)GetProcAddress(h, "VapourSynthPluginInit2"); 1012 | if (!f) 1013 | f = (void *)GetProcAddress(h, "_VapourSynthPluginInit2@8"); 1014 | if (!f) 1015 | FreeLibrary(h); 1016 | return reinterpret_cast(f); 1017 | } 1018 | #else // !_WIN32 1019 | #include // we require dladdr 1020 | static vs4::VSInitPlugin getInit4() { 1021 | Dl_info info; 1022 | if (dladdr((void *)getInit4, &info) == 0) 1023 | throw std::runtime_error("unable to locate myself"); 1024 | std::string path(info.dli_fname); 1025 | size_t idx = path.rfind('.'); 1026 | std::string suffix = path.substr(idx); 1027 | idx = path.rfind(L'.', idx - 1); 1028 | if (idx == std::wstring::npos) return nullptr; 1029 | std::string dllPath = path.substr(0, idx) + suffix; 1030 | if (debug()) 1031 | std::cerr << "v3bdg: self = " << path << ", dll = " << dllPath << std::endl; 1032 | void *h = dlopen(dllPath.c_str(), RTLD_GLOBAL | RTLD_LAZY); 1033 | if (h == 0) return nullptr; 1034 | void *f = dlsym(h, "VapourSynthPluginInit2"); 1035 | if (!f) 1036 | dlclose(h); 1037 | return reinterpret_cast(f); 1038 | } 1039 | #endif // _WIN32 1040 | VS_EXTERNAL_API(void) VapourSynthPluginInit2(vs4::VSPlugin *plugin, const vs4::VSPLUGINAPI *vspapi) { 1041 | (void) plugin; 1042 | (void) vspapi; 1043 | } 1044 | static void VS_CC versionCreate(const vs3::VSMap *in, vs3::VSMap *out, void *user_data, vs3::VSCore *core, const vs3::VSAPI *vsapi) { 1045 | vsapi->propSetData(out, "version", VERSION, -1, vs3::paAppend); 1046 | } 1047 | VS_EXTERNAL_API(void) VapourSynthPluginInit(vs3::VSConfigPlugin configFunc, vs3::VSRegisterFunction registerFunc, vs3::VSPlugin *plugin) { 1048 | vs4::VSInitPlugin init = getInit4(); 1049 | if (init) 1050 | wrap(init, configFunc, registerFunc, plugin); 1051 | registerFunc("api3_bridged", "", versionCreate, nullptr, plugin); 1052 | } 1053 | #endif // STANDALONE 1054 | --------------------------------------------------------------------------------