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 "../core/x86utils.h"
22 #include "../core/cpufeatures.h"
23 #include "avisynth_compat.h"
24 #include <algorithm>
25 #include <cstdarg>
26 #include <limits>
27 #include "../common/vsutf16.h"
28
29 #define NOMINMAX
30 #include <Windows.h>
31
32 extern const AVS_Linkage* const AVS_linkage;
33
34 namespace {
operator ""_s(const char * str,size_t len)35 std::string operator""_s(const char *str, size_t len) { return{ str, len }; }
36 } // namespace
37
38
39 namespace AvisynthCompat {
40
VSFormatToAVSPixelType(const VSFormat * fi)41 static int VSFormatToAVSPixelType(const VSFormat *fi) {
42 if (fi->id == pfCompatBGR32)
43 return VideoInfo::CS_BGR32;
44 else if (fi->id == pfCompatYUY2)
45 return VideoInfo::CS_YUY2;
46 else if (fi->id == pfYUV444P8)
47 return VideoInfo::CS_YV24;
48 else if (fi->id == pfYUV422P8)
49 return VideoInfo::CS_YV16;
50 else if (fi->id == pfYUV420P8)
51 return VideoInfo::CS_YV12;
52 else if (fi->id == pfYUV410P8)
53 return VideoInfo::CS_YUV9;
54 else if (fi->id == pfYUV411P8)
55 return VideoInfo::CS_YV411;
56 else if (fi->id == pfGray8)
57 return VideoInfo::CS_Y8;
58 else if (fi->id == pfYUV444P10)
59 return VideoInfo::CS_YUV444P10;
60 else if (fi->id == pfYUV422P10)
61 return VideoInfo::CS_YUV422P10;
62 else if (fi->id == pfYUV420P10)
63 return VideoInfo::CS_YUV420P10;
64 else if (fi->bitsPerSample == 10 && fi->colorFamily == cmGray && fi->sampleType == stInteger)
65 return VideoInfo::CS_Y10;
66 else if (fi->id == pfYUV444P12)
67 return VideoInfo::CS_YUV444P12;
68 else if (fi->id == pfYUV422P12)
69 return VideoInfo::CS_YUV422P12;
70 else if (fi->id == pfYUV420P12)
71 return VideoInfo::CS_YUV420P12;
72 else if (fi->bitsPerSample == 12 && fi->colorFamily == cmGray && fi->sampleType == stInteger)
73 return VideoInfo::CS_Y12;
74 else if (fi->id == pfYUV444P14)
75 return VideoInfo::CS_YUV444P14;
76 else if (fi->id == pfYUV422P14)
77 return VideoInfo::CS_YUV422P14;
78 else if (fi->id == pfYUV420P14)
79 return VideoInfo::CS_YUV420P14;
80 else if (fi->bitsPerSample == 14 && fi->colorFamily == cmGray && fi->sampleType == stInteger)
81 return VideoInfo::CS_Y14;
82 else if (fi->id == pfYUV444P16)
83 return VideoInfo::CS_YUV444P16;
84 else if (fi->id == pfYUV422P16)
85 return VideoInfo::CS_YUV422P16;
86 else if (fi->id == pfYUV420P16)
87 return VideoInfo::CS_YUV420P16;
88 else if (fi->id == pfGray16)
89 return VideoInfo::CS_Y16;
90 else if (fi->id == pfYUV444PS)
91 return VideoInfo::CS_YUV444PS;
92 else if (fi->bitsPerSample == 32 && fi->colorFamily == cmYUV && fi->sampleType == stFloat && fi->subSamplingH == 0 && fi->subSamplingW == 1)
93 return VideoInfo::CS_YUV422PS;
94 else if (fi->bitsPerSample == 32 && fi->colorFamily == cmYUV && fi->sampleType == stFloat && fi->subSamplingH == 1 && fi->subSamplingW == 1)
95 return VideoInfo::CS_YUV420PS;
96 else if (fi->id == pfGrayS)
97 return VideoInfo::CS_Y32;
98 else if (fi->id == pfRGB24)
99 return VideoInfo::CS_RGBP;
100 else if (fi->id == pfRGB30)
101 return VideoInfo::CS_RGBP10;
102 else if (fi->bitsPerSample == 12 && fi->colorFamily == cmRGB)
103 return VideoInfo::CS_RGBP12;
104 else if (fi->bitsPerSample == 14 && fi->colorFamily == cmRGB)
105 return VideoInfo::CS_RGBP14;
106 else if (fi->id == pfRGB48)
107 return VideoInfo::CS_RGBP16;
108 else if (fi->id == pfGrayS)
109 return VideoInfo::CS_Y32;
110 else
111 return 0;
112 }
113
AVSPixelTypeToVSFormat(const VideoInfo & vi,VSCore * core,const VSAPI * vsapi)114 static const VSFormat *AVSPixelTypeToVSFormat(const VideoInfo &vi, VSCore *core, const VSAPI *vsapi) {
115 if (vi.IsYUY2())
116 return vsapi->getFormatPreset(pfCompatYUY2, core);
117 else if (vi.IsRGB32())
118 return vsapi->getFormatPreset(pfCompatBGR32, core);
119
120 if (vi.IsPlanar()) {
121 bool hasSubSampling = vi.IsYUV();
122 int colorspace = vi.IsYUV() ? cmYUV : (vi.IsRGB() ? cmRGB : (vi.IsY() ? cmGray : 0));
123 if (colorspace)
124 return vsapi->registerFormat(colorspace, vi.BitsPerComponent() == 32 ? stFloat : stInteger, vi.BitsPerComponent(), hasSubSampling ? vi.GetPlaneWidthSubsampling(PLANAR_U) : 0, hasSubSampling ? vi.GetPlaneHeightSubsampling(PLANAR_U) : 0, core);
125 }
126
127 return nullptr;
128 }
129
avsToVSFrame(VideoFrame * frame)130 const VSFrameRef *FakeAvisynth::avsToVSFrame(VideoFrame *frame) {
131 const VSFrameRef *ref = nullptr;
132 std::map<VideoFrame *, const VSFrameRef *>::iterator it = ownedFrames.find(frame);
133
134 if (it != ownedFrames.end()) {
135 ref = vsapi->cloneFrameRef(it->second);
136 } else {
137 vsapi->logMessage(mtFatal, "unreachable condition");
138 assert(false);
139 }
140
141 it = ownedFrames.begin();
142
143 while (it != ownedFrames.end()) {
144 if (it->first->refcount == 0 || it->first->refcount == 9000) {
145 delete it->first;
146 vsapi->freeFrame(it->second);
147 it = ownedFrames.erase(it);
148 } else {
149 ++it;
150 }
151 }
152
153 assert(ref);
154 return ref;
155 }
156
~FakeAvisynth()157 FakeAvisynth::~FakeAvisynth() {
158 std::map<VideoFrame *, const VSFrameRef *>::iterator it = ownedFrames.begin();
159
160 while (it != ownedFrames.end()) {
161 delete it->first;
162 vsapi->freeFrame(it->second);
163 it = ownedFrames.erase(it);
164 }
165
166 ownedFrames.clear();
167 }
168
GetCPUFlags()169 int FakeAvisynth::GetCPUFlags() {
170 const CPUFeatures *cpuf = getCPUFeatures();
171 int flags = CPUF_FPU | CPUF_MMX | CPUF_INTEGER_SSE | CPUF_SSE | CPUF_SSE2; // minimum to run VS
172 if (cpuf->sse3) flags |= CPUF_SSE3;
173 if (cpuf->ssse3) flags |= CPUF_SSSE3;
174 if (cpuf->sse4_1) flags |= CPUF_SSE4_1;
175 if (cpuf->sse4_2) flags |= CPUF_SSE4_2;
176 if (cpuf->avx) flags |= CPUF_AVX;
177 if (cpuf->avx2) flags |= CPUF_AVX2;
178 if (cpuf->fma3) flags |= CPUF_FMA3;
179 if (cpuf->f16c) flags |= CPUF_F16C;
180 if (cpuf->avx512_f) flags |= CPUF_AVX512F;
181 if (cpuf->avx512_bw) flags |= CPUF_AVX512BW;
182 if (cpuf->avx512_dq) flags |= CPUF_AVX512DQ;
183 if (cpuf->avx512_cd) flags |= CPUF_AVX512CD;
184 if (cpuf->avx512_vl) flags |= CPUF_AVX512VL;
185 return flags;
186 }
187
SaveString(const char * s,int length)188 char *FakeAvisynth::SaveString(const char *s, int length) {
189 auto strIter = (length >= 0) ? savedStrings.emplace(s, length) : savedStrings.emplace(s);
190 // UGLY
191 // Cast away the const because nobody would actually try to write to a saved string (except possibly an avisynth plugin developer)
192 return const_cast<char *>(strIter.first->c_str());
193 }
194
Sprintf(const char * fmt,...)195 char *FakeAvisynth::Sprintf(const char *fmt, ...) {
196 va_list val;
197 va_start(val, fmt);
198 char *result = VSprintf(fmt, val);
199 va_end(val);
200 return result;
201 }
202
VSprintf(const char * fmt,void * val)203 char *FakeAvisynth::VSprintf(const char *fmt, void *val) {
204 std::vector<char> buf;
205 int size = 0, count = -1;
206
207 while (count == -1) {
208 buf.resize(buf.size() + 4096);
209 count = vsnprintf(buf.data(), size - 1, fmt, (va_list)val);
210 }
211
212 char *i = SaveString(buf.data());
213 return i;
214 }
215
ThrowError(const char * fmt,...)216 void FakeAvisynth::ThrowError(const char *fmt, ...) {
217 va_list val;
218 va_start(val, fmt);
219 char buf[8192];
220 _vsnprintf(buf, sizeof(buf) - 1, fmt, val);
221 va_end(val);
222 buf[sizeof(buf)-1] = '\0';
223 throw AvisynthError(SaveString(buf));
224 }
225
charToFilterArgumentString(char c)226 std::string FakeAvisynth::charToFilterArgumentString(char c) {
227 switch (c) {
228 case 'i':
229 case 'b':
230 return "int";
231 case 'f':
232 return "float";
233 case 's':
234 return "data";
235 case 'c':
236 return "clip";
237 default:
238 vsapi->logMessage(mtFatal, "Avisynth Compat: invalid argument type character, I quit");
239 return "";
240 }
241 }
242
VSClip(VSNodeRef * clip,FakeAvisynth * fakeEnv,const VSAPI * vsapi)243 VSClip::VSClip(VSNodeRef *clip, FakeAvisynth *fakeEnv, const VSAPI *vsapi)
244 : clip(clip), fakeEnv(fakeEnv), vsapi(vsapi), numSlowWarnings(0) {
245 const ::VSVideoInfo *srcVi = vsapi->getVideoInfo(clip);
246 vi = {};
247 vi.width = srcVi->width;
248 vi.height = srcVi->height;
249
250 vi.pixel_type = VSFormatToAVSPixelType(srcVi->format);
251 if (!vi.pixel_type)
252 vsapi->logMessage(mtFatal, "Bad colorspace");
253
254 vi.image_type = VideoInfo::IT_BFF;
255 vi.fps_numerator = int64ToIntS(srcVi->fpsNum);
256 vi.fps_denominator = int64ToIntS(srcVi->fpsDen);
257 vi.num_frames = srcVi->numFrames;
258 vi.sample_type = SAMPLE_INT16;
259 }
260
GetFrame(int n,IScriptEnvironment * env)261 PVideoFrame VSClip::GetFrame(int n, IScriptEnvironment *env) {
262 const VSFrameRef *ref;
263 n = std::min(std::max(0, n), vi.num_frames - 1);
264
265 if (fakeEnv->initializing)
266 ref = vsapi->getFrame(n, clip, nullptr, 0);
267 else
268 ref = vsapi->getFrameFilter(n, clip, fakeEnv->uglyCtx);
269
270 std::vector<char> buf(1024);
271
272 if (!ref) {
273 if (numSlowWarnings < 200) {
274 numSlowWarnings++;
275 std::string s = "Avisynth Compat: requested frame "_s + std::to_string(n) + " not prefetched, using slow method";
276 vsapi->logMessage(mtWarning, s.c_str());
277 }
278 ref = vsapi->getFrame(n, clip, buf.data(), static_cast<int>(buf.size()));
279 }
280
281 if (!ref)
282 vsapi->logMessage(mtFatal, ("Avisynth Compat: error while getting input frame synchronously: "_s + buf.data()).c_str());
283
284 bool isMultiplePlanes = (vi.pixel_type & VideoInfo::CS_PLANAR) && !(vi.pixel_type & VideoInfo::CS_INTERLEAVED);
285
286 const uint8_t *firstPlanePtr = vsapi->getReadPtr(ref, 0);
287
288 VideoFrame *vfb = new VideoFrame(
289 // the data will never be modified due to the writable protections embedded in this mess
290 (BYTE *)firstPlanePtr,
291 false,
292 0,
293 vsapi->getStride(ref, 0),
294 vsapi->getFrameWidth(ref, 0) * vsapi->getFrameFormat(ref)->bytesPerSample,
295 vsapi->getFrameHeight(ref, 0),
296 isMultiplePlanes ? vsapi->getReadPtr(ref, 1) - firstPlanePtr : 0,
297 isMultiplePlanes ? vsapi->getReadPtr(ref, 2) - firstPlanePtr : 0,
298 isMultiplePlanes ? vsapi->getStride(ref, 1) : 0,
299 vsapi->getFrameWidth(ref, 1) * vsapi->getFrameFormat(ref)->bytesPerSample,
300 vsapi->getFrameHeight(ref, 1));
301 PVideoFrame pvf(vfb);
302 fakeEnv->ownedFrames.insert(std::make_pair(vfb, ref));
303 return pvf;
304 }
305
WrappedClip(const std::string & filterName,const PClip & clip,const std::vector<VSNodeRef * > & preFetchClips,const PrefetchInfo & prefetchInfo,FakeAvisynth * fakeEnv)306 WrappedClip::WrappedClip(const std::string &filterName, const PClip &clip, const std::vector<VSNodeRef *> &preFetchClips, const PrefetchInfo &prefetchInfo, FakeAvisynth *fakeEnv)
307 : filterName(filterName), prefetchInfo(prefetchInfo), preFetchClips(preFetchClips), clip(clip), fakeEnv(fakeEnv) {
308 }
309
prefetchHelper(int n,VSNodeRef * node,const PrefetchInfo & p,VSFrameContext * frameCtx,const VSAPI * vsapi)310 static void prefetchHelper(int n, VSNodeRef *node, const PrefetchInfo &p, VSFrameContext *frameCtx, const VSAPI *vsapi) {
311 n /= p.div;
312 n *= p.mul;
313
314 for (int i = n + p.from; i <= n + p.to; i++) {
315 if (i < 0)
316 continue;
317
318 vsapi->requestFrameFilter(i, node, frameCtx);
319 }
320 }
321
322 #define WARNING(fname, warning) if (name == #fname) vsapi->logMessage(mtWarning, "Avisynth Compat: "_s + #fname + " - " + #warning).c_str());
323 #define BROKEN(fname) if (name == #fname) vsapi->logMessage(mtWarning, ("Avisynth Compat: Invoking known broken function "_s + name).c_str());
324 #define OTHER(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
325 #define SOURCE(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
326 #define PREFETCHR0(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
327 #define PREFETCHR1(fname) if (name == #fname) return PrefetchInfo(1, 1, -1, 1);
328 #define PREFETCHR2(fname) if (name == #fname) return PrefetchInfo(1, 1, -2, 2);
329 #define PREFETCHR3(fname) if (name == #fname) return PrefetchInfo(1, 1, -3, 3);
330 #define PREFETCH(fname, div, mul, from, to) if (name == #fname) return PrefetchInfo(div, mul, from, to);
331
getPrefetchInfo(const std::string & name,const VSMap * in,const VSAPI * vsapi)332 static PrefetchInfo getPrefetchInfo(const std::string &name, const VSMap *in, const VSAPI *vsapi) {
333 int err;
334 int temp;
335 // FFMS2
336 OTHER(FFIndex)
337 SOURCE(FFVideoSource)
338 PREFETCHR0(SWScale)
339 OTHER(FFSetLogLevel)
340 OTHER(FFGetLogLevel)
341 OTHER(FFGetVersion)
342 // TNLMeans
343 temp = int64ToIntS(vsapi->propGetInt(in, "Az", 0, &err));
344 PREFETCH(TNLMeans, 1, 1, -temp, temp)
345 // yadif*
346 PREFETCHR1(Yadif)
347 PREFETCHR0(yadifmod)
348 // *EDI*
349 PREFETCHR0(eedi3)
350 PREFETCHR0(eedi3_rpow2)
351 PREFETCHR0(nnedi2)
352 PREFETCHR0(nnedi2_rpow2)
353 PREFETCHR0(nnedi3)
354 PREFETCHR0(nnedi3_rpow2)
355 // mixed Tritical
356 temp = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
357 switch(temp) {
358 case 0:
359 case -1:
360 case -2:
361 PREFETCHR2(TDeint); break;
362 case 2:
363 PREFETCHR3(TDeint); break;
364 case 1:
365 PREFETCH(TDeint, 2, 1, -2, 2); break;
366 }
367 BROKEN(ColorMatrix)
368 PREFETCHR1(Cnr2)
369 temp = int64ToIntS(vsapi->propGetInt(in, "tbsize", 0, &err));
370 PREFETCH(dfttest, 1, 1, -(temp / 2), temp / 2)
371
372 // MPEG2DEC
373 SOURCE(MPEG2Source)
374 PREFETCHR0(LumaYV12)
375 PREFETCHR0(BlindPP)
376 PREFETCHR0(Deblock)
377
378 // Meow
379 SOURCE(DGSource)
380 PREFETCHR0(DGDenoise)
381 PREFETCHR0(DGSharpen)
382 temp = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
383 PREFETCH(DGBob, (temp > 0) ? 2 : 1, 1, -2, 2) // close enough?
384 BROKEN(IsCombed)
385 PREFETCHR0(FieldDeinterlace)
386 PREFETCH(Telecide, 1, 1, -2, 10) // not good
387 PREFETCH(DGTelecide, 1, 1, -2, 10) // also not good
388 temp = int64ToIntS(vsapi->propGetInt(in, "cycle", 0, &err));
389 PREFETCH(DGDecimate, temp - 1, temp, -(temp + 3), temp + 3) // probably suboptimal
390 PREFETCH(Decimate, temp - 1, temp, -(temp + 3), temp + 3) // probably suboptimal too
391
392 // Masktools2
393 PREFETCHR0(mt_edge)
394 PREFETCHR1(mt_motion)
395 PREFETCHR0(mt_expand)
396 PREFETCHR0(mt_inpand)
397 PREFETCHR0(mt_inflate)
398 PREFETCHR0(mt_lut)
399 PREFETCHR0(mt_lutxy)
400 PREFETCHR0(mt_lutf)
401 PREFETCHR0(mt_luts)
402 PREFETCHR0(mt_makediff)
403 PREFETCHR0(mt_adddiff)
404 PREFETCHR0(mt_average)
405 PREFETCHR0(mt_clamp)
406 PREFETCHR0(mt_merge)
407 PREFETCHR0(mt_logic)
408 PREFETCHR0(mt_hysteresis)
409 PREFETCHR0(mt_invert)
410 PREFETCHR0(mt_binarize)
411 PREFETCHR0(mt_convolution)
412 PREFETCHR0(mt_mappedblur)
413 OTHER(mt_circle)
414 OTHER(mt_square)
415 OTHER(mt_diamond)
416 OTHER(mt_losange)
417 OTHER(mt_rectangle)
418 OTHER(mt_ellipse)
419 OTHER(mt_polish)
420 // Mixed
421 BROKEN(RemoveGrain)
422 BROKEN(Repair)
423 PREFETCHR0(VagueDenoiser)
424 PREFETCHR0(UnDot)
425 PREFETCHR0(SangNom)
426 PREFETCHR0(gradfun2db)
427 PREFETCHR0(debilinear)
428 PREFETCHR0(debilinearY)
429 SOURCE(dss2)
430 SOURCE(DirectShowSource)
431 PREFETCHR0(SCXvid)
432 PREFETCHR0(ResampleHQ)
433 PREFETCHR0(AssRender)
434 PREFETCHR0(TextSub)
435 PREFETCHR0(VobSub)
436 PREFETCHR2(fft3dGPU)
437 PREFETCHR2(FFT3DFilter)
438 PREFETCHR1(Convolution3D)
439 PREFETCHR1(deen)
440 PREFETCHR0(eDeen)
441 // Mvtools
442 PREFETCHR0(MSuper)
443 temp = int64ToIntS(vsapi->propGetInt(in, "delta", 0, &err));
444 if (temp < 1)
445 temp = 1;
446 PREFETCH(MAnalyse, 1, 1, -temp, temp)
447 PREFETCHR3(MDegrain1)
448 PREFETCHR3(MDegrain2)
449 PREFETCHR3(MDegrain3)
450 PREFETCHR3(MCompensate)
451 PREFETCHR0(MMask)
452 PREFETCHR0(MSCDetection)
453 PREFETCHR0(MShow)
454 PREFETCHR0(MDepan)
455 PREFETCHR0(MFlow)
456 PREFETCHR1(MFlowInter)
457 PREFETCHR1(MFlowFps)
458 PREFETCHR1(MBlockFps)
459 PREFETCHR1(MFlowBlur)
460 PREFETCHR1(MRecalculate)
461
462 // aWarpShit
463 PREFETCHR0(aBlur)
464 PREFETCHR0(aSobel)
465 PREFETCHR0(aWarp)
466 PREFETCHR0(aWarp4)
467 PREFETCHR0(aWarpSharp)
468 PREFETCHR0(aWarpSharp2)
469
470 // CullResize
471 PREFETCHR0(CullBilinearResize)
472 PREFETCHR0(CullBicubicResize)
473 PREFETCHR0(CullLanczosResize)
474 PREFETCHR0(CullLanczos4Resize)
475 PREFETCHR0(CullBlackmanResize)
476 PREFETCHR0(CullSpline16Resize)
477 PREFETCHR0(CullSpline36Resize)
478 PREFETCHR0(CullSpline64Resize)
479 PREFETCHR0(CullGaussResize)
480
481 // Spline resize
482 PREFETCHR0(Spline100Resize)
483 PREFETCHR0(Spline144Resize)
484
485 // PVBob
486 temp = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
487 PREFETCH(DGBob, (temp > 0) ? 2 : 1, 1, -2, 2)
488 PREFETCH(PVBob, (temp > 0) ? 2 : 1, 1, -2, 2)
489
490 // Avisynth internal
491 PREFETCH(Bob, 2, 1, 0, 0)
492 PREFETCH(TemporalSoften, 1, 1, -5, 5)
493
494 // AutoAdjust
495 temp = int64ToIntS(vsapi->propGetInt(in, "temporal_radius", 0, &err));
496 if (err || temp < 0)
497 temp = 20;
498 PREFETCH(AutoAdjust, 1, 1, -temp, temp)
499
500 // prefetch nothing by default
501 return PrefetchInfo(1, 1, 0, -1);
502 }
503
avisynthFilterInit(VSMap * in,VSMap * out,void ** instanceData,VSNode * node,VSCore * core,const VSAPI * vsapi)504 static void VS_CC avisynthFilterInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
505 WrappedClip *clip = (WrappedClip *) * instanceData;
506
507 if (!clip->preFetchClips.empty())
508 clip->fakeEnv->uglyNode = clip->preFetchClips.front();
509
510 const VideoInfo &viAvs = clip->clip->GetVideoInfo();
511 ::VSVideoInfo vi;
512 vi.height = viAvs.height;
513 vi.width = viAvs.width;
514 vi.numFrames = viAvs.num_frames;
515 vi.fpsNum = viAvs.fps_numerator;
516 vi.fpsDen = viAvs.fps_denominator;
517 vs_normalizeRational(&vi.fpsNum, &vi.fpsDen);
518
519 vi.format = AVSPixelTypeToVSFormat(viAvs, core, vsapi);
520
521 if (!vi.format)
522 vsapi->setError(out, "Avisynth Compat: bad format!");
523
524 vi.flags = 0;
525 vsapi->setVideoInfo(&vi, 1, node);
526 }
527
avisynthFilterGetFrame(int n,int activationReason,void ** instanceData,void ** frameData,VSFrameContext * frameCtx,VSCore * core,const VSAPI * vsapi)528 static const VSFrameRef *VS_CC avisynthFilterGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
529 WrappedClip *clip = (WrappedClip *) * instanceData;
530 PVideoFrame frame;
531 n = std::min(n, clip->clip->GetVideoInfo().num_frames - 1);
532
533 if (activationReason == arAllFramesReady || (activationReason == arInitial && (clip->preFetchClips.empty() || clip->prefetchInfo.from > clip->prefetchInfo.to))) {
534 // Ready the global stuff needed to make things work behind the scenes, the locking model makes this technically safe but quite ugly.
535 // The frame number is needed to pass through frame attributes for filters that create a new frame to return, the context is for GetFrame().
536 if (!clip->preFetchClips.empty()) {
537 clip->fakeEnv->uglyN = n;
538 clip->fakeEnv->uglyCtx = frameCtx;
539 }
540
541 try {
542 frame = clip->clip->GetFrame(n, clip->fakeEnv);
543
544 if (!frame)
545 vsapi->logMessage(mtFatal, "Avisynth Error: no frame returned");
546 } catch (const AvisynthError &e) {
547 vsapi->logMessage(mtFatal, ("Avisynth Error: avisynth errors in GetFrame() are unrecoverable, crashing... "_s + e.msg).c_str());
548 } catch (const IScriptEnvironment::NotFound &) {
549 vsapi->logMessage(mtFatal, "Avisynth Error: escaped IScriptEnvironment::NotFound exceptions are non-recoverable, crashing... ");
550 } catch (...) {
551 vsapi->logMessage(mtFatal, "Avisynth Error: avisynth errors are unrecoverable, crashing...");
552 }
553
554 clip->fakeEnv->uglyCtx = nullptr;
555 } else if (activationReason == arInitial) {
556 for (VSNodeRef *c : clip->preFetchClips)
557 prefetchHelper(n, c, clip->prefetchInfo, frameCtx, vsapi);
558 } else if (activationReason == arError) {
559 return nullptr;
560 }
561
562 // Enjoy the casting to trigger the void * operator. Please contact me if you can make it pretty.
563
564 const VSFrameRef *ref = nullptr;
565
566 if (frame)
567 ref = clip->fakeEnv->avsToVSFrame((VideoFrame *)((void *)frame));
568
569 return ref;
570 }
571
avisynthFilterFree(void * instanceData,VSCore * core,const VSAPI * vsapi)572 static void VS_CC avisynthFilterFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
573 WrappedClip *clip = (WrappedClip *)instanceData;
574 delete clip;
575 }
576
isSupportedPF(const VSFormat * f,int interfaceVersion)577 static bool isSupportedPF(const VSFormat *f, int interfaceVersion) {
578 if (interfaceVersion == 2)
579 return (f->id == pfYUV420P8) || (f->id == pfCompatYUY2) || (f->id == pfCompatBGR32);
580 else
581 return !!VSFormatToAVSPixelType(f);
582 }
583
fakeAvisynthFunctionWrapper(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)584 static void VS_CC fakeAvisynthFunctionWrapper(const VSMap *in, VSMap *out, void *userData,
585 VSCore *core, const VSAPI *vsapi) {
586 WrappedFunction *wf = (WrappedFunction *)userData;
587 FakeAvisynth *fakeEnv = new FakeAvisynth(wf->interfaceVersion, core, vsapi);
588 std::vector<AVSValue> inArgs(wf->parsedArgs.size());
589 std::vector<VSNodeRef *> preFetchClips;
590
591 for (size_t i = 0; i < inArgs.size(); i++) {
592 const AvisynthArgs &parsedArg = wf->parsedArgs.at(i);
593
594 if (vsapi->propNumElements(in, parsedArg.name.data()) > 0) {
595 switch (parsedArg.type) {
596 case 'i':
597 inArgs[i] = int64ToIntS(vsapi->propGetInt(in, parsedArg.name.c_str(), 0, nullptr));
598 break;
599 case 'f':
600 inArgs[i] = vsapi->propGetFloat(in, parsedArg.name.c_str(), 0, nullptr);
601 break;
602 case 'b':
603 inArgs[i] = !!vsapi->propGetInt(in, parsedArg.name.c_str(), 0, nullptr);
604 break;
605 case 's':
606 inArgs[i] = vsapi->propGetData(in, parsedArg.name.c_str(), 0, nullptr);
607 break;
608 case 'c':
609 VSNodeRef *cr = vsapi->propGetNode(in, parsedArg.name.c_str(), 0, nullptr);
610 const VSVideoInfo *vi = vsapi->getVideoInfo(cr);
611 if (!isConstantFormat(vi) || !isSupportedPF(vi->format, wf->interfaceVersion)) {
612 vsapi->setError(out, "Invalid avisynth colorspace in one of the input clips");
613 vsapi->freeNode(cr);
614 delete fakeEnv;
615 return;
616 }
617
618 preFetchClips.push_back(cr);
619 inArgs[i] = new VSClip(cr, fakeEnv, vsapi);
620 break;
621 }
622 }
623 }
624
625 AVSValue inArgAVSValue(inArgs.data(), static_cast<int>(wf->parsedArgs.size()));
626 AVSValue ret;
627
628 try {
629 ret = wf->apply(inArgAVSValue, wf->avsUserData, fakeEnv);
630 } catch (const AvisynthError &e) {
631 vsapi->setError(out, e.msg);
632 return;
633 } catch (const IScriptEnvironment::NotFound &) {
634 vsapi->logMessage(mtFatal, "Avisynth Error: escaped IScriptEnvironment::NotFound exceptions are non-recoverable, crashing... ");
635 }
636
637 fakeEnv->initializing = false;
638
639 if (ret.IsClip()) {
640 PrefetchInfo prefetchInfo = getPrefetchInfo(wf->name, in, vsapi);
641 WrappedClip *filterData = new WrappedClip(wf->name, ret.AsClip(), preFetchClips, prefetchInfo, fakeEnv);
642 vsapi->createFilter(
643 in,
644 out,
645 wf->name.c_str(),
646 avisynthFilterInit,
647 avisynthFilterGetFrame,
648 avisynthFilterFree,
649 (preFetchClips.empty() || prefetchInfo.from > prefetchInfo.to) ? fmSerial : fmParallelRequests,
650 0,
651 filterData,
652 core);
653 } else if (ret.IsBool()) {
654 vsapi->propSetInt(out, "val", ret.AsBool() ? 1 : 0, paReplace);
655 } else if (ret.IsInt()) {
656 vsapi->propSetInt(out, "val", ret.AsInt(), paReplace);
657 } else if (ret.IsFloat()) {
658 vsapi->propSetFloat(out, "val", ret.AsFloat(), paReplace);
659 } else if (ret.IsString()) {
660 vsapi->propSetData(out, "val", ret.AsString(), -1, paReplace);
661 }
662 }
663
AddFunction(const char * name,const char * params,ApplyFunc apply,void * user_data)664 void FakeAvisynth::AddFunction(const char *name, const char *params, ApplyFunc apply, void *user_data) {
665 size_t paramLength = strlen(params);
666 size_t paramPos = 0;
667 int argNum = 1;
668 int numArgs = 0;
669 std::vector<AvisynthArgs> parsedArgs;
670 std::string newArgs;
671 std::string fname(name);
672
673 if (fname == "RemoveGrain" || fname == "Repair" || fname == "ColorMatrix" || fname == "IsCombed") {
674 vsapi->logMessage(mtWarning, ("Avisynth Compat: rejected adding Avisynth function " + fname + "because it is too broken").c_str());
675 return;
676 }
677
678 if (fname == "FFMS2" || fname == "FFCopyrightInfringement") {
679 vsapi->logMessage(mtWarning, ("Avisynth Compat: rejected adding Avisynth function " + fname + "because it calls invoke").c_str());
680 return;
681 }
682
683 while (paramPos < paramLength) {
684 if (params[paramPos] == '*' || params[paramPos] == '+' || params[paramPos] == '.') {
685 vsapi->logMessage(mtWarning, ("Avisynth Compat: varargs not implemented so I'm just gonna skip importing " + fname).c_str());
686 return;
687 }
688
689 if (params[paramPos] == '[') { // named argument start
690 std::string argName(params);
691 size_t nameStart = ++paramPos;
692
693 while (paramPos < paramLength) {
694 if (params[paramPos++] == ']') {
695 argName = argName.substr(nameStart, paramPos - nameStart - 1);
696 break;
697 }
698 }
699
700 newArgs += argName + ":" + charToFilterArgumentString(params[paramPos]) + ":opt;";
701 parsedArgs.push_back(AvisynthArgs(argName, params[paramPos++], false));
702 } else {
703 newArgs += params[paramPos] + std::to_string(argNum) + ":" + charToFilterArgumentString(params[paramPos]) + ";";
704 parsedArgs.push_back(AvisynthArgs((params[paramPos] + std::to_string(argNum)), params[paramPos], true));
705 paramPos++;
706 argNum++;
707 }
708
709 numArgs++;
710 }
711
712 std::lock_guard<std::mutex> lock(registerFunctionLock);
713
714 if (registeredFunctions.count(fname)) {
715 for (size_t i = 2; i < SIZE_MAX; i++) {
716 std::string numberedName = fname + "_" + std::to_string(i);
717 if (!registeredFunctions.count(numberedName)) {
718 fname = numberedName;
719 break;
720 }
721 }
722 }
723
724 registeredFunctions.insert(fname);
725 vsapi->registerFunction(fname.c_str(), newArgs.c_str(), fakeAvisynthFunctionWrapper, new WrappedFunction(fname, apply, parsedArgs, user_data, interfaceVersion), vsapi->getPluginById("com.vapoursynth.avisynth", core));
726 }
727
FunctionExists(const char * name)728 bool FakeAvisynth::FunctionExists(const char *name) {
729 vsapi->logMessage(mtWarning, "FunctionExists not implemented");
730 return false;
731 }
732
Invoke(const char * name,const AVSValue args,const char * const * arg_names)733 AVSValue FakeAvisynth::Invoke(const char *name, const AVSValue args, const char* const* arg_names) {
734 if (!_stricmp(name, "Cache") || !_stricmp(name, "InternalCache")) {
735 return args;
736 }
737
738 if (!_stricmp(name, "Crop")) {
739 vsapi->logMessage(mtWarning, "Invoke not fully implemented, tried to call Crop() but I will do nothing");
740 return args[0];
741 }
742
743 if (!_stricmp(name, "AudioDub")) {
744 vsapi->logMessage(mtWarning, "Invoke not fully implemented, tried to call AudioDub() but I will do nothing");
745 return args[0];
746 }
747
748 vsapi->logMessage(mtWarning, ("Invoke not fully implemented, tried to call: "_s + name + " but I will pretend it doesn't exist").c_str());
749 throw IScriptEnvironment::NotFound();
750 return AVSValue();
751 }
752
GetVar(const char * name)753 AVSValue FakeAvisynth::GetVar(const char *name) {
754 return AVSValue();
755 }
756
SetVar(const char * name,const AVSValue & val)757 bool FakeAvisynth::SetVar(const char *name, const AVSValue &val) {
758 return true;
759 }
760
SetGlobalVar(const char * name,const AVSValue & val)761 bool FakeAvisynth::SetGlobalVar(const char *name, const AVSValue &val) {
762 return true;
763 }
764
PushContext(int level)765 void FakeAvisynth::PushContext(int level) {
766 vsapi->logMessage(mtFatal, "PushContext not implemented");
767 }
768
PopContext()769 void FakeAvisynth::PopContext() {
770 vsapi->logMessage(mtFatal, "PopContext not implemented");
771 }
772
NewVideoFrame(const VideoInfo & vi,int align)773 PVideoFrame FakeAvisynth::NewVideoFrame(const VideoInfo &vi, int align) {
774 VSFrameRef *ref = nullptr;
775 assert(vi.width > 0);
776 assert(vi.height > 0);
777
778 // attempt to copy over the right set of properties, assuming that frame n in is also n out
779 const VSFrameRef *propSrc = nullptr;
780
781 if (uglyNode && uglyCtx)
782 propSrc = vsapi->getFrameFilter(uglyN, uglyNode, uglyCtx);
783
784 bool isMultiplePlanes = (vi.pixel_type & VideoInfo::CS_PLANAR) && !(vi.pixel_type & VideoInfo::CS_INTERLEAVED);
785
786 const VSFormat *f = AVSPixelTypeToVSFormat(vi, core, vsapi);
787
788 if (!f)
789 vsapi->logMessage(mtFatal, "Unsupported frame format in newvideoframe (alpha and/or packed RGB not supported)");
790
791 ref = vsapi->newVideoFrame(f, vi.width, vi.height, propSrc, core);
792
793 if (propSrc)
794 vsapi->freeFrame(propSrc);
795
796 uint8_t *firstPlanePtr = vsapi->getWritePtr(ref, 0);
797 VideoFrame *vfb = new VideoFrame(
798 (BYTE *)firstPlanePtr,
799 true,
800 0,
801 vsapi->getStride(ref, 0),
802 vi.width * vsapi->getFrameFormat(ref)->bytesPerSample,
803 vi.height,
804 isMultiplePlanes ? vsapi->getWritePtr(ref, 1) - firstPlanePtr : 0,
805 isMultiplePlanes ? vsapi->getWritePtr(ref, 2) - firstPlanePtr : 0,
806 isMultiplePlanes ? vsapi->getStride(ref, 1) : 0,
807 vsapi->getFrameWidth(ref, 1) * vsapi->getFrameFormat(ref)->bytesPerSample,
808 vsapi->getFrameHeight(ref, 1));
809
810 PVideoFrame pvf(vfb);
811 ownedFrames.insert(std::make_pair(vfb, ref));
812 return pvf;
813 }
814
MakeWritable(PVideoFrame * pvf)815 bool FakeAvisynth::MakeWritable(PVideoFrame *pvf) {
816 // Find the backing frame, copy it, wrap the new frame into a avisynth PVideoFrame
817 VideoFrame *vfb = (VideoFrame *)(void *)(*pvf);
818 auto it = ownedFrames.find(vfb);
819 assert(it != ownedFrames.end());
820 VSFrameRef *ref = vsapi->copyFrame(it->second, core);
821 uint8_t *firstPlanePtr = vsapi->getWritePtr(ref, 0);
822 VideoFrame *newVfb = new VideoFrame(
823 // the data will never be modified due to the writable protections embedded in this mess
824 (BYTE *)firstPlanePtr,
825 true,
826 0,
827 vsapi->getStride(ref, 0),
828 (*pvf)->row_size,
829 (*pvf)->height,
830 vsapi->getWritePtr(ref, 1) - firstPlanePtr,
831 vsapi->getWritePtr(ref, 2) - firstPlanePtr,
832 vsapi->getStride(ref, 1),
833 (*pvf)->row_sizeUV,
834 (*pvf)->heightUV);
835 *pvf = PVideoFrame(newVfb);
836 ownedFrames.insert(std::make_pair(newVfb, ref));
837 return true;
838 }
839
BitBlt(BYTE * dstp,int dst_pitch,const BYTE * srcp,int src_pitch,int row_size,int height)840 void FakeAvisynth::BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) {
841 if (src_pitch == dst_pitch && dst_pitch == row_size) {
842 memcpy(dstp, srcp, row_size * height);
843 } else {
844 for (int i = 0; i < height; i++) {
845 memcpy(dstp, srcp, row_size);
846 dstp += dst_pitch;
847 srcp += src_pitch;
848 }
849 }
850 }
851
AtExit(ShutdownFunc function,void * user_data)852 void FakeAvisynth::AtExit(ShutdownFunc function, void *user_data) {
853 // intentionally ignored to prevent issues when multiple cores load the sample plugin in the same process
854 }
855
CheckVersion(int version)856 void FakeAvisynth::CheckVersion(int version) {
857 if (version > AVISYNTH_INTERFACE_VERSION)
858 ThrowError("Plugin was designed for a later version of Avisynth (%d)", version);
859 }
860
Subframe(PVideoFrame src,int rel_offset,int new_pitch,int new_row_size,int new_height)861 PVideoFrame FakeAvisynth::Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) {
862 if (src->row_size != new_row_size)
863 vsapi->logMessage(mtFatal, "Subframe only partially implemented (row_size != new_row_size)");
864 // not pretty at all, but the underlying frame has to be fished out to have any idea what the input really is
865 const VSFrameRef *f = avsToVSFrame((VideoFrame *)(void *)src);
866 const VSFormat *fi = vsapi->getFrameFormat(f);
867 VideoInfo vi;
868 vi.height = new_height;
869 vi.width = vsapi->getFrameWidth(f, 0);
870
871 if (fi->id == pfCompatYUY2)
872 vi.pixel_type = VideoInfo::CS_YUY2;
873 else if (fi->id == pfCompatBGR32)
874 vi.pixel_type = VideoInfo::CS_BGR32;
875 else
876 vsapi->logMessage(mtFatal, "Bad colorspace");
877
878 PVideoFrame dst = NewVideoFrame(vi);
879 BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr() + rel_offset, new_pitch, new_row_size, new_height);
880
881 return dst;
882 }
883
SetMemoryMax(int mem)884 int FakeAvisynth::SetMemoryMax(int mem) {
885 // ignore
886 return 0;
887 }
888
SetWorkingDir(const char * newdir)889 int FakeAvisynth::SetWorkingDir(const char *newdir) {
890 vsapi->logMessage(mtFatal, "SetWorkingDir not implemented");
891 return 1;
892 }
893
ManageCache(int key,void * data)894 void *FakeAvisynth::ManageCache(int key, void *data) {
895 vsapi->logMessage(mtFatal, "ManageCache not implemented");
896 return nullptr;
897 }
898
PlanarChromaAlignment(PlanarChromaAlignmentMode key)899 bool FakeAvisynth::PlanarChromaAlignment(PlanarChromaAlignmentMode key) {
900 vsapi->logMessage(mtFatal, "PlanarChromaAlignment not implemented");
901 return true;
902 }
903
SubframePlanar(PVideoFrame src,int rel_offset,int new_pitch,int new_row_size,int new_height,int rel_offsetU,int rel_offsetV,int new_pitchUV)904 PVideoFrame FakeAvisynth::SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) {
905 vsapi->logMessage(mtFatal, "SubframePlanar not implemented");
906 if (src->row_size != new_row_size)
907 vsapi->logMessage(mtFatal, "SubframePlanar only partially implemented");
908 // not pretty at all, but the underlying frame has to be fished out to have any idea what the input really is
909 const VSFrameRef *f = avsToVSFrame((VideoFrame *)(void *)src);
910 const VSFormat *fi = vsapi->getFrameFormat(f);
911 VideoInfo vi;
912 vi.height = new_height;
913 vi.width = vsapi->getFrameWidth(f, 0);
914
915 vi.pixel_type = VSFormatToAVSPixelType(fi);
916
917 if (!vi.pixel_type)
918 vsapi->logMessage(mtFatal, "Bad colorspace, bad!");
919
920 PVideoFrame dst = NewVideoFrame(vi);
921
922 vsapi->logMessage(mtWarning, "Subframeplanar only partially implemented, report if it crashed or not (especially if not using YV12)");
923 BitBlt(dst->GetWritePtr(PLANAR_Y), dst->GetPitch(PLANAR_Y), src->GetReadPtr(PLANAR_Y) + rel_offset, new_pitch, new_row_size, new_height);
924 BitBlt(dst->GetWritePtr(PLANAR_U), dst->GetPitch(PLANAR_U), src->GetReadPtr(PLANAR_U) + rel_offsetU, new_pitchUV, new_row_size/2, new_height/2);
925 BitBlt(dst->GetWritePtr(PLANAR_V), dst->GetPitch(PLANAR_V), src->GetReadPtr(PLANAR_V) + rel_offsetV, new_pitchUV, new_row_size/2, new_height/2);
926 return dst;
927 }
928
DeleteScriptEnvironment()929 void FakeAvisynth::DeleteScriptEnvironment() {
930 vsapi->logMessage(mtFatal, "DeleteScriptEnvironment not implemented");
931 }
932
ApplyMessage(PVideoFrame * frame,const VideoInfo & vi,const char * message,int size,int textcolor,int halocolor,int bgcolor)933 void FakeAvisynth::ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size,
934 int textcolor, int halocolor, int bgcolor) {
935 vsapi->logMessage(mtFatal, "ApplyMessage not implemented");
936 }
937
GetAVSLinkage()938 const AVS_Linkage* const FakeAvisynth::GetAVSLinkage() {
939 return AVS_linkage;
940 }
941
GetVarDef(const char * name,const AVSValue & def)942 AVSValue FakeAvisynth::GetVarDef(const char* name, const AVSValue& def) {
943 return def;
944 }
945
GetProperty(AvsEnvProperty prop)946 size_t FakeAvisynth::GetProperty(AvsEnvProperty prop) {
947 switch (prop) {
948 case AEP_FILTERCHAIN_THREADS:
949 return 1;
950 case AEP_PHYSICAL_CPUS:
951 return 1;
952 case AEP_LOGICAL_CPUS:
953 return 1;
954 case AEP_THREAD_ID:
955 return 0;
956 case AEP_THREADPOOL_THREADS:
957 return 1;
958 case AEP_VERSION:
959 return 0;
960 default:
961 this->ThrowError("Invalid property request.");
962 return std::numeric_limits<size_t>::max();
963 }
964
965 assert(0);
966 }
967
GetVar(const char * name,AVSValue * val) const968 bool FakeAvisynth::GetVar(const char* name, AVSValue *val) const {
969 return val;
970 }
971
GetVar(const char * name,bool def) const972 bool FakeAvisynth::GetVar(const char* name, bool def) const {
973 return def;
974 }
975
GetVar(const char * name,int def) const976 int FakeAvisynth::GetVar(const char* name, int def) const {
977 return def;
978 }
979
GetVar(const char * name,double def) const980 double FakeAvisynth::GetVar(const char* name, double def) const {
981 return def;
982 }
983
GetVar(const char * name,const char * def) const984 const char* FakeAvisynth::GetVar(const char* name, const char* def) const {
985 return def;
986 }
987
LoadPlugin(const char * filePath,bool throwOnError,AVSValue * result)988 bool FakeAvisynth::LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result) {
989 vsapi->logMessage(mtFatal, "Plugin loading not implemented");
990 return false;
991 }
992
AddAutoloadDir(const char * dirPath,bool toFront)993 void FakeAvisynth::AddAutoloadDir(const char* dirPath, bool toFront) {
994 vsapi->logMessage(mtFatal, "Autoloading dirs not implemented");
995 }
996
ClearAutoloadDirs()997 void FakeAvisynth::ClearAutoloadDirs() {
998 vsapi->logMessage(mtFatal, "Clearing autoload dirs not implemented");
999 }
1000
AutoloadPlugins()1001 void FakeAvisynth::AutoloadPlugins() {
1002 vsapi->logMessage(mtFatal, "Autoloading not implemented");
1003 }
1004
AddFunction(const char * name,const char * params,ApplyFunc apply,void * user_data,const char * exportVar)1005 void FakeAvisynth::AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data, const char *exportVar) {
1006 AddFunction(name, params, apply, user_data);
1007 }
1008
InternalFunctionExists(const char * name)1009 bool FakeAvisynth::InternalFunctionExists(const char* name) {
1010 return false;
1011 }
1012
SetFilterMTMode(const char * filter,MtMode mode,bool force)1013 void FakeAvisynth::SetFilterMTMode(const char* filter, MtMode mode, bool force) {
1014 // do nothing
1015 }
1016
NewCompletion(size_t capacity)1017 IJobCompletion* FakeAvisynth::NewCompletion(size_t capacity) {
1018 vsapi->logMessage(mtFatal, "Completions not implemented");
1019 return nullptr;
1020 }
1021
ParallelJob(ThreadWorkerFuncPtr jobFunc,void * jobData,IJobCompletion * completion)1022 void FakeAvisynth::ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion) {
1023 vsapi->logMessage(mtFatal, "Threadpool not implemented");
1024 }
1025
1026 // This version of Invoke will return false instead of throwing NotFound().
Invoke(AVSValue * result,const char * name,const AVSValue & args,const char * const * arg_names)1027 bool FakeAvisynth::Invoke(AVSValue *result, const char* name, const AVSValue& args, const char* const* arg_names) {
1028 try {
1029 *result = Invoke(name, args, arg_names);
1030 return true;
1031 } catch (NotFound &) {
1032 return false;
1033 }
1034 }
1035
1036 // Support functions
Allocate(size_t nBytes,size_t alignment,AvsAllocType type)1037 void* FakeAvisynth::Allocate(size_t nBytes, size_t alignment, AvsAllocType type) {
1038 return vs_aligned_malloc(nBytes, alignment);
1039 }
1040
Free(void * ptr)1041 void FakeAvisynth::Free(void* ptr) {
1042 vs_aligned_free(ptr);
1043 }
1044
SubframePlanarA(PVideoFrame src,int rel_offset,int new_pitch,int new_row_size,int new_height,int rel_offsetU,int rel_offsetV,int new_pitchUV,int rel_offsetA)1045 PVideoFrame FakeAvisynth::SubframePlanarA(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size,
1046 int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA) {
1047 vsapi->logMessage(mtFatal, "SubframePlanarA not implemented");
1048 return PVideoFrame();
1049 }
1050
avsLoadPlugin(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)1051 static void VS_CC avsLoadPlugin(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
1052 const char *rawPath = vsapi->propGetData(in, "path", 0, nullptr);
1053 std::wstring wPath = utf16_from_utf8(rawPath);
1054
1055 HMODULE plugin = LoadLibraryW(wPath.c_str());
1056
1057 typedef const char *(__stdcall *AvisynthPluginInit2Func)(IScriptEnvironment *env);
1058 typedef const char *(__stdcall *AvisynthPluginInit3Func)(IScriptEnvironment *env, const AVS_Linkage *const vectors);
1059
1060 if (!plugin) {
1061 vsapi->setError(out, "Avisynth Loader: failed to load module");
1062 return;
1063 }
1064
1065 AvisynthPluginInit2Func avisynthPluginInit2 = nullptr;
1066 AvisynthPluginInit3Func avisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "AvisynthPluginInit3");
1067
1068 if (!avisynthPluginInit3)
1069 avisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "_AvisynthPluginInit3@8");
1070
1071 if (!avisynthPluginInit3) {
1072 avisynthPluginInit2 = (AvisynthPluginInit2Func)GetProcAddress(plugin, "AvisynthPluginInit2");
1073
1074 if (!avisynthPluginInit2)
1075 avisynthPluginInit2 = (AvisynthPluginInit2Func)GetProcAddress(plugin, "_AvisynthPluginInit2@4");
1076 }
1077
1078 if (!avisynthPluginInit3 && !avisynthPluginInit2) {
1079 vsapi->setError(out, "Avisynth Loader: no entry point found");
1080 FreeLibrary(plugin);
1081 return;
1082 }
1083
1084 if (avisynthPluginInit3) {
1085 FakeAvisynth *avs = new FakeAvisynth(3, core, vsapi);
1086 avisynthPluginInit3(avs, AVS_linkage);
1087 delete avs;
1088 } else {
1089 #ifdef _WIN64
1090 vsapi->setError(out, "Avisynth Loader: 2.5 plugins can't be loaded on x64");
1091 return;
1092 #else
1093 FakeAvisynth *avs = new FakeAvisynth(2, core, vsapi);
1094 avisynthPluginInit2(avs);
1095 delete avs;
1096 #endif
1097 }
1098
1099 #ifdef VS_TARGET_OS_WINDOWS
1100 if (!vs_isSSEStateOk())
1101 vsapi->logMessage(mtFatal, ("Bad SSE state detected after loading "_s + rawPath).c_str());
1102 #endif
1103 }
1104
WrappedFunction(const std::string & name,FakeAvisynth::ApplyFunc apply,const std::vector<AvisynthArgs> & parsedArgs,void * avsUserData,int interfaceVersion)1105 WrappedFunction::WrappedFunction(const std::string &name, FakeAvisynth::ApplyFunc apply, const std::vector<AvisynthArgs> &parsedArgs, void *avsUserData, int interfaceVersion) :
1106 name(name), apply(apply), parsedArgs(parsedArgs), avsUserData(avsUserData), interfaceVersion(interfaceVersion) {
1107 }
1108
VapourSynthPluginInit(VSConfigPlugin configFunc,VSRegisterFunction registerFunc,VSPlugin * plugin)1109 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) {
1110 configFunc("com.vapoursynth.avisynth", "avs", "VapourSynth Avisynth Compatibility", VAPOURSYNTH_API_VERSION, 0, plugin);
1111 registerFunc("LoadPlugin", "path:data;", &avsLoadPlugin, plugin, plugin);
1112 }
1113
1114 }
1115