#include "../core/x86utils.h"
#include "../core/cpufeatures.h"
#include "../core/filtershared.h"
#include "../core/version.h"
#include "avisynth_compat.h"
#include <algorithm>
#include <cstdarg>
#include <cstddef>
#include <limits>
#include <filesystem>
#include "p2p_api.h"
#include <Windows.h>
extern const AVS_Linkage* const AVS_linkage;
using namespace std::string_literals;
using namespace vsh;
namespace AvisynthCompat {
static inline bool IsSameVideoFormat(const VSVideoFormat &f, unsigned colorFamily, unsigned sampleType, unsigned bitsPerSample, unsigned subSamplingW = 0, unsigned subSamplingH = 0) noexcept {
return f.colorFamily == colorFamily && f.sampleType == sampleType && f.bitsPerSample == bitsPerSample && f.subSamplingW == subSamplingW && f.subSamplingH == subSamplingH;
}
static int VSFormatToAVSPixelType(const VSVideoFormat &fi, bool pack) {
if (IsSameVideoFormat(fi, cfYUV, stInteger, 8, 0, 0))
return VideoInfo::CS_YV24;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 8, 1, 0) && pack)
return VideoInfo::CS_YUY2;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 8, 1, 0))
return VideoInfo::CS_YV16;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 8, 1, 1))
return VideoInfo::CS_YV12;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 8, 2, 2))
return VideoInfo::CS_YUV9;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 8, 2, 0))
return VideoInfo::CS_YV411;
else if (IsSameVideoFormat(fi, cfGray, stInteger, 8))
return VideoInfo::CS_Y8;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 10, 0, 0))
return VideoInfo::CS_YUV444P10;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 10, 1, 0))
return VideoInfo::CS_YUV422P10;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 10, 1, 1))
return VideoInfo::CS_YUV420P10;
else if (IsSameVideoFormat(fi, cfGray, stInteger, 10))
return VideoInfo::CS_Y10;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 12, 0, 0))
return VideoInfo::CS_YUV444P12;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 12, 1, 0))
return VideoInfo::CS_YUV422P12;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 12, 1, 1))
return VideoInfo::CS_YUV420P12;
else if (IsSameVideoFormat(fi, cfGray, stInteger, 12))
return VideoInfo::CS_Y12;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 14, 0, 0))
return VideoInfo::CS_YUV444P14;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 14, 1, 0))
return VideoInfo::CS_YUV422P14;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 14, 1, 1))
return VideoInfo::CS_YUV420P14;
else if (IsSameVideoFormat(fi, cfGray, stInteger, 14))
return VideoInfo::CS_Y14;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 16, 0, 0))
return VideoInfo::CS_YUV444P16;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 16, 1, 0))
return VideoInfo::CS_YUV422P16;
else if (IsSameVideoFormat(fi, cfYUV, stInteger, 16, 1, 1))
return VideoInfo::CS_YUV420P16;
else if (IsSameVideoFormat(fi, cfGray, stInteger, 16))
return VideoInfo::CS_Y16;
else if (IsSameVideoFormat(fi, cfYUV, stFloat, 32, 0, 0))
return VideoInfo::CS_YUV444PS;
else if (IsSameVideoFormat(fi, cfYUV, stFloat, 32, 1, 0))
return VideoInfo::CS_YUV422PS;
else if (IsSameVideoFormat(fi, cfYUV, stFloat, 32, 1, 1))
return VideoInfo::CS_YUV420PS;
else if (IsSameVideoFormat(fi, cfGray, stFloat, 32))
return VideoInfo::CS_Y32;
else if (IsSameVideoFormat(fi, cfRGB, stInteger, 8) && pack)
return VideoInfo::CS_BGR32;
else if (IsSameVideoFormat(fi, cfRGB, stInteger, 8))
return VideoInfo::CS_RGBP;
else if (IsSameVideoFormat(fi, cfRGB, stInteger, 10))
return VideoInfo::CS_RGBP10;
else if (IsSameVideoFormat(fi, cfRGB, stInteger, 12))
return VideoInfo::CS_RGBP12;
else if (IsSameVideoFormat(fi, cfRGB, stInteger, 14))
return VideoInfo::CS_RGBP14;
else if (IsSameVideoFormat(fi, cfRGB, stInteger, 16))
return VideoInfo::CS_RGBP16;
else
return 0;
}
static bool AVSPixelTypeToVSFormat(VSVideoFormat &f, bool &unpack, const VideoInfo &vi, VSCore *core, const VSAPI *vsapi) {
unpack = false;
if (vi.IsYUY2()) {
unpack = true;
return vsapi->getVideoFormatByID(&f, pfGray16, core);
} else if (vi.IsRGB32()) {
unpack = true;
return vsapi->getVideoFormatByID(&f, pfGray32, core);
}
if (vi.IsPlanar()) {
bool hasSubSampling = vi.IsYUV();
unsigned colorFamily = vi.IsYUV() ? cfYUV : (vi.IsRGB() ? cfRGB : (vi.IsY() ? cfGray : 0));
return vsapi->queryVideoFormat(&f, colorFamily, vi.BitsPerComponent() == 32 ? stFloat : stInteger, vi.BitsPerComponent(), hasSubSampling ? vi.GetPlaneWidthSubsampling(PLANAR_U) : 0, hasSubSampling ? vi.GetPlaneHeightSubsampling(PLANAR_U) : 0, core);
}
return false;
}
typedef struct {
VSVideoInfo vi;
} PackYUY2DataExtra;
typedef SingleNodeData<PackYUY2DataExtra> PackYUY2Data;
static const VSFrame *VS_CC packYUY2GetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
PackYUY2Data *d = reinterpret_cast<PackYUY2Data *>(instanceData);
if (activationReason == arInitial) {
vsapi->requestFrameFilter(n, d->node, frameCtx);
} else if (activationReason == arAllFramesReady) {
const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx);
VSFrame *dst = vsapi->newVideoFrame(&d->vi.format, d->vi.width, d->vi.height, src, core);
p2p_buffer_param p = {};
p.packing = p2p_yuy2;
p.width = d->vi.width;
p.height = d->vi.height;
p.dst[0] = vsapi->getWritePtr(dst, 0);
p.dst_stride[0] = vsapi->getStride(dst, 0);
for (int plane = 0; plane < 3; plane++) {
p.src[plane] = vsapi->getReadPtr(src, plane);
p.src_stride[plane] = vsapi->getStride(src, plane);
}
p2p_pack_frame(&p, 0);
vsapi->freeFrame(src);
return dst;
}
return nullptr;
}
static VSNode *VS_CC packYUY2Create(VSNode *node, VSCore *core, const VSAPI *vsapi) {
std::unique_ptr<PackYUY2Data> d(new PackYUY2Data(vsapi));
const VSVideoInfo *vi = vsapi->getVideoInfo(node);
if (vsapi->queryVideoFormatID(vi->format.colorFamily, vi->format.sampleType, vi->format.bitsPerSample, vi->format.subSamplingW, vi->format.subSamplingH, core) != pfYUV422P8)
return nullptr;
d->node = node;
d->vi = *vi;
vsapi->getVideoFormatByID(&d->vi.format, pfGray16, core);
VSFilterDependency deps[] = {{d->node, rpStrictSpatial}};
VSNode *ret = vsapi->createVideoFilter2("PackYUY2", &d->vi, packYUY2GetFrame, filterFree<PackYUY2Data>, fmParallel, deps, 1, d.get(), core);
d.release();
return ret;
}
typedef struct {
VSVideoInfo vi;
} UnpackYUY2DataExtra;
typedef SingleNodeData<UnpackYUY2DataExtra> UnpackYUY2Data;
static const VSFrame *VS_CC unpackYUY2GetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
UnpackYUY2Data *d = reinterpret_cast<UnpackYUY2Data *>(instanceData);
if (activationReason == arInitial) {
vsapi->requestFrameFilter(n, d->node, frameCtx);
} else if (activationReason == arAllFramesReady) {
const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx);
VSFrame *dst = vsapi->newVideoFrame(&d->vi.format, d->vi.width, d->vi.height, src, core);
p2p_buffer_param p = {};
p.packing = p2p_yuy2;
p.width = d->vi.width;
p.height = d->vi.height;
p.src[0] = vsapi->getReadPtr(src, 0);
p.src_stride[0] = vsapi->getStride(src, 0);
for (int plane = 0; plane < 3; plane++) {
p.dst[plane] = vsapi->getWritePtr(dst, plane);
p.dst_stride[plane] = vsapi->getStride(dst, plane);
}
p2p_unpack_frame(&p, 0);
vsapi->freeFrame(src);
return dst;
}
return nullptr;
}
static VSNode *VS_CC unpackYUY2Create(VSNode *node, VSCore *core, const VSAPI *vsapi) {
std::unique_ptr<UnpackYUY2Data> d(new UnpackYUY2Data(vsapi));
const VSVideoInfo *vi = vsapi->getVideoInfo(node);
if (vsapi->queryVideoFormatID(vi->format.colorFamily, vi->format.sampleType, vi->format.bitsPerSample, vi->format.subSamplingW, vi->format.subSamplingH, core) != pfGray16)
return nullptr;
d->node = node;
d->vi = *vi;
vsapi->getVideoFormatByID(&d->vi.format, pfYUV422P8, core);
VSFilterDependency deps[] = {{d->node, rpStrictSpatial}};
VSNode *ret = vsapi->createVideoFilter2("UnpackRGB32", &d->vi, unpackYUY2GetFrame, filterFree<UnpackYUY2Data>, fmParallel, deps, 1, d.get(), core);
d.release();
return ret;
}
typedef struct {
VSVideoInfo vi;
} PackRGB32DataExtra;
typedef SingleNodeData<PackRGB32DataExtra> PackRGB32Data;
static const VSFrame *VS_CC packRGB32GetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
PackRGB32Data *d = reinterpret_cast<PackRGB32Data *>(instanceData);
if (activationReason == arInitial) {
vsapi->requestFrameFilter(n, d->node, frameCtx);
} else if (activationReason == arAllFramesReady) {
const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx);
VSFrame *dst = vsapi->newVideoFrame(&d->vi.format, d->vi.width, d->vi.height, src, core);
p2p_buffer_param p = {};
p.packing = p2p_argb32;
p.width = d->vi.width;
p.height = d->vi.height;
p.dst[0] = vsapi->getWritePtr(dst, 0);
p.dst_stride[0] = vsapi->getStride(dst, 0);
for (int plane = 0; plane < 3; plane++) {
p.src[plane] = vsapi->getReadPtr(src, plane);
p.src_stride[plane] = vsapi->getStride(src, plane);
}
p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
vsapi->freeFrame(src);
return dst;
}
return nullptr;
}
static VSNode *VS_CC packRGB32Create(VSNode *node, VSCore *core, const VSAPI *vsapi) {
std::unique_ptr<PackRGB32Data> d(new PackRGB32Data(vsapi));
const VSVideoInfo *vi = vsapi->getVideoInfo(node);
if (vsapi->queryVideoFormatID(vi->format.colorFamily, vi->format.sampleType, vi->format.bitsPerSample, vi->format.subSamplingW, vi->format.subSamplingH, core) != pfRGB24)
return nullptr;
d->node = node;
d->vi = *vi;
vsapi->getVideoFormatByID(&d->vi.format, pfGray32, core);
VSFilterDependency deps[] = {{d->node, rpStrictSpatial}};
VSNode *ret = vsapi->createVideoFilter2("PackRGB32", &d->vi, packRGB32GetFrame, filterFree<PackRGB32Data>, fmParallel, deps, 1, d.get(), core);
d.release();
return ret;
}
typedef struct {
VSVideoInfo vi;
} UnpackRGB32DataExtra;
typedef SingleNodeData<UnpackRGB32DataExtra> UnpackRGB32Data;
static const VSFrame *VS_CC unpackRGB32GetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
UnpackRGB32Data *d = reinterpret_cast<UnpackRGB32Data *>(instanceData);
if (activationReason == arInitial) {
vsapi->requestFrameFilter(n, d->node, frameCtx);
} else if (activationReason == arAllFramesReady) {
const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx);
VSFrame *dst = vsapi->newVideoFrame(&d->vi.format, d->vi.width, d->vi.height, src, core);
p2p_buffer_param p = {};
p.packing = p2p_argb32;
p.width = d->vi.width;
p.height = d->vi.height;
p.src[0] = vsapi->getReadPtr(src, 0);
p.src_stride[0] = vsapi->getStride(src, 0);
for (int plane = 0; plane < 3; plane++) {
p.dst[plane] = vsapi->getWritePtr(dst, plane);
p.dst_stride[plane] = vsapi->getStride(dst, plane);
}
p2p_unpack_frame(&p, 0);
vsapi->freeFrame(src);
return dst;
}
return nullptr;
}
static VSNode *VS_CC unpackRGB32Create(VSNode *node, VSCore *core, const VSAPI *vsapi) {
std::unique_ptr<UnpackRGB32Data> d(new UnpackRGB32Data(vsapi));
const VSVideoInfo *vi = vsapi->getVideoInfo(node);
if (vsapi->queryVideoFormatID(vi->format.colorFamily, vi->format.sampleType, vi->format.bitsPerSample, vi->format.subSamplingW, vi->format.subSamplingH, core) != pfGray32)
return nullptr;
d->node = node;
d->vi = *vi;
vsapi->getVideoFormatByID(&d->vi.format, pfRGB24, core);
VSFilterDependency deps[] = {{d->node, rpStrictSpatial}};
VSNode *ret = vsapi->createVideoFilter2("UnpackRGB32", &d->vi, unpackRGB32GetFrame, filterFree<UnpackRGB32Data>, fmParallel, deps, 1, d.get(), core);
d.release();
return ret;
}
const VSFrame *FakeAvisynth::avsToVSFrame(VideoFrame *frame) {
const VSFrame *ref = nullptr;
std::map<VideoFrame *, const VSFrame *>::iterator it = ownedFrames.find(frame);
if (it != ownedFrames.end()) {
ref = vsapi->addFrameRef(it->second);
} else {
vsapi->logMessage(mtFatal, "unreachable condition", core);
assert(false);
}
it = ownedFrames.begin();
while (it != ownedFrames.end()) {
if (it->first->refcount == 0 || it->first->refcount == 9000) {
delete it->first;
vsapi->freeFrame(it->second);
it = ownedFrames.erase(it);
} else {
++it;
}
}
assert(ref);
return ref;
}
FakeAvisynth::~FakeAvisynth() {
std::map<VideoFrame *, const VSFrame *>::iterator it = ownedFrames.begin();
while (it != ownedFrames.end()) {
delete it->first;
vsapi->freeFrame(it->second);
it = ownedFrames.erase(it);
}
ownedFrames.clear();
}
int FakeAvisynth::GetCPUFlags() {
const CPUFeatures *cpuf = getCPUFeatures();
int flags = CPUF_FPU | CPUF_MMX | CPUF_INTEGER_SSE | CPUF_SSE | CPUF_SSE2; if (cpuf->sse3) flags |= CPUF_SSE3;
if (cpuf->ssse3) flags |= CPUF_SSSE3;
if (cpuf->sse4_1) flags |= CPUF_SSE4_1;
if (cpuf->sse4_2) flags |= CPUF_SSE4_2;
if (cpuf->avx) flags |= CPUF_AVX;
if (cpuf->avx2) flags |= CPUF_AVX2;
if (cpuf->fma3) flags |= CPUF_FMA3;
if (cpuf->f16c) flags |= CPUF_F16C;
if (cpuf->avx512_f) flags |= CPUF_AVX512F;
if (cpuf->avx512_bw) flags |= CPUF_AVX512BW;
if (cpuf->avx512_dq) flags |= CPUF_AVX512DQ;
if (cpuf->avx512_cd) flags |= CPUF_AVX512CD;
if (cpuf->avx512_vl) flags |= CPUF_AVX512VL;
return flags;
}
char *FakeAvisynth::SaveString(const char *s, int length) {
auto strIter = (length >= 0) ? savedStrings.emplace(s, length) : savedStrings.emplace(s);
return const_cast<char *>(strIter.first->c_str());
}
char *FakeAvisynth::Sprintf(const char *fmt, ...) {
va_list val;
va_start(val, fmt);
char *result = VSprintf(fmt, val);
va_end(val);
return result;
}
char *FakeAvisynth::VSprintf(const char *fmt, void *val) {
std::vector<char> buf;
int size = 0, count = -1;
while (count == -1) {
buf.resize(buf.size() + 4096);
count = vsnprintf(buf.data(), size - 1, fmt, (va_list)val);
}
char *i = SaveString(buf.data());
return i;
}
void FakeAvisynth::ThrowError(const char *fmt, ...) {
va_list val;
va_start(val, fmt);
char buf[8192];
_vsnprintf(buf, sizeof(buf) - 1, fmt, val);
va_end(val);
buf[sizeof(buf)-1] = '\0';
throw AvisynthError(SaveString(buf));
}
std::string FakeAvisynth::charToFilterArgumentString(char c) {
switch (c) {
case 'i':
case 'b':
return "int";
case 'f':
return "float";
case 's':
return "data";
case 'c':
return "vnode";
default:
vsapi->logMessage(mtFatal, "Avisynth Compat: invalid argument type character, I quit", core);
return "";
}
}
VSClip::VSClip(VSNode *inclip, FakeAvisynth *fakeEnv, bool pack, const VSAPI *vsapi)
: clip(inclip), fakeEnv(fakeEnv), vsapi(vsapi), numSlowWarnings(0) {
const VSVideoInfo *srcVi = vsapi->getVideoInfo(clip);
if (pack) {
if (IsSameVideoFormat(srcVi->format, cfRGB, stInteger, 8, 0, 0)) {
clip = packRGB32Create(clip, fakeEnv->core, vsapi);
assert(clip);
} else if (IsSameVideoFormat(srcVi->format, cfYUV, stInteger, 8, 1, 0)) {
clip = packYUY2Create(clip, fakeEnv->core, vsapi);
assert(clip);
}
}
vi = {};
vi.width = srcVi->width;
vi.height = srcVi->height;
vi.pixel_type = VSFormatToAVSPixelType(srcVi->format, pack);
if (!vi.pixel_type)
vsapi->logMessage(mtFatal, "Bad colorspace", fakeEnv->core);
vi.image_type = VideoInfo::IT_BFF;
vi.fps_numerator = int64ToIntS(srcVi->fpsNum);
vi.fps_denominator = int64ToIntS(srcVi->fpsDen);
vi.num_frames = srcVi->numFrames;
vi.sample_type = SAMPLE_INT16;
}
PVideoFrame VSClip::GetFrame(int n, IScriptEnvironment *env) {
const VSFrame *ref;
n = std::min(std::max(0, n), vi.num_frames - 1);
if (fakeEnv->initializing)
ref = vsapi->getFrame(n, clip, nullptr, 0);
else
ref = vsapi->getFrameFilter(n, clip, fakeEnv->uglyCtx);
std::vector<char> buf(1024);
if (!ref) {
if (numSlowWarnings < 200) {
numSlowWarnings++;
std::string s = "Avisynth Compat: requested frame "s + std::to_string(n) + " not prefetched, using slow method";
vsapi->logMessage(mtWarning, s.c_str(), fakeEnv->core);
}
ref = vsapi->getFrame(n, clip, buf.data(), static_cast<int>(buf.size()));
}
if (!ref)
vsapi->logMessage(mtFatal, ("Avisynth Compat: error while getting input frame synchronously: "s + buf.data()).c_str(), fakeEnv->core);
bool isMultiplePlanes = (vi.pixel_type & VideoInfo::CS_PLANAR) && !(vi.pixel_type & VideoInfo::CS_INTERLEAVED);
const uint8_t *firstPlanePtr = vsapi->getReadPtr(ref, 0);
VideoFrame *vfb = new VideoFrame(
(BYTE *)firstPlanePtr,
false,
0,
static_cast<int>(vsapi->getStride(ref, 0)),
vsapi->getFrameWidth(ref, 0) * vsapi->getVideoFrameFormat(ref)->bytesPerSample,
vsapi->getFrameHeight(ref, 0),
isMultiplePlanes ? vsapi->getReadPtr(ref, 1) - firstPlanePtr : 0,
isMultiplePlanes ? vsapi->getReadPtr(ref, 2) - firstPlanePtr : 0,
isMultiplePlanes ? static_cast<int>(vsapi->getStride(ref, 1)) : 0,
vsapi->getFrameWidth(ref, 1) * vsapi->getVideoFrameFormat(ref)->bytesPerSample,
vsapi->getFrameHeight(ref, 1));
PVideoFrame pvf(vfb);
fakeEnv->ownedFrames.insert(std::make_pair(vfb, ref));
return pvf;
}
WrappedClip::WrappedClip(const std::string &filterName, const PClip &clip, const std::vector<VSNode *> &preFetchClips, const PrefetchInfo &prefetchInfo, FakeAvisynth *fakeEnv)
: filterName(filterName), prefetchInfo(prefetchInfo), preFetchClips(preFetchClips), clip(clip), fakeEnv(fakeEnv) {
}
static void prefetchHelper(int n, VSNode *node, const PrefetchInfo &p, VSFrameContext *frameCtx, const VSAPI *vsapi) {
n /= p.div;
n *= p.mul;
for (int i = n + p.from; i <= n + p.to; i++) {
if (i < 0)
continue;
vsapi->requestFrameFilter(i, node, frameCtx);
}
}
#define WARNING(fname, warning) if (name == #fname) vsapi->logMessage(mtWarning, "Avisynth Compat: "s + #fname + " - " + #warning).c_str(), core);
#define BROKEN(fname) if (name == #fname) vsapi->logMessage(mtWarning, ("Avisynth Compat: Invoking known broken function "s + name).c_str(), core);
#define OTHER(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
#define SOURCE(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
#define PREFETCHR0(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
#define PREFETCHR1(fname) if (name == #fname) return PrefetchInfo(1, 1, -1, 1);
#define PREFETCHR2(fname) if (name == #fname) return PrefetchInfo(1, 1, -2, 2);
#define PREFETCHR3(fname) if (name == #fname) return PrefetchInfo(1, 1, -3, 3);
#define PREFETCH(fname, div, mul, from, to) if (name == #fname) return PrefetchInfo(div, mul, from, to);
static PrefetchInfo getPrefetchInfo(const std::string &name, const VSMap *in, VSCore *core, const VSAPI *vsapi) {
int err;
int temp;
OTHER(FFIndex)
SOURCE(FFVideoSource)
PREFETCHR0(SWScale)
OTHER(FFSetLogLevel)
OTHER(FFGetLogLevel)
OTHER(FFGetVersion)
temp = vsapi->mapGetIntSaturated(in, "Az", 0, &err);
PREFETCH(TNLMeans, 1, 1, -temp, temp)
PREFETCHR1(Yadif)
PREFETCHR0(yadifmod)
PREFETCHR0(eedi3)
PREFETCHR0(eedi3_rpow2)
PREFETCHR0(nnedi2)
PREFETCHR0(nnedi2_rpow2)
PREFETCHR0(nnedi3)
PREFETCHR0(nnedi3_rpow2)
temp = vsapi->mapGetIntSaturated(in, "mode", 0, &err);
switch(temp) {
case 0:
case -1:
case -2:
PREFETCHR2(TDeint); break;
case 2:
PREFETCHR3(TDeint); break;
case 1:
PREFETCH(TDeint, 2, 1, -2, 2); break;
}
BROKEN(ColorMatrix)
PREFETCHR1(Cnr2)
temp = vsapi->mapGetIntSaturated(in, "tbsize", 0, &err);
PREFETCH(dfttest, 1, 1, -(temp / 2), temp / 2)
SOURCE(MPEG2Source)
PREFETCHR0(LumaYV12)
PREFETCHR0(BlindPP)
PREFETCHR0(Deblock)
SOURCE(DGSource)
PREFETCHR0(DGDenoise)
PREFETCHR0(DGSharpen)
temp = vsapi->mapGetIntSaturated(in, "mode", 0, &err);
PREFETCH(DGBob, (temp > 0) ? 2 : 1, 1, -2, 2) BROKEN(IsCombed)
PREFETCHR0(FieldDeinterlace)
PREFETCH(Telecide, 1, 1, -2, 10) PREFETCH(DGTelecide, 1, 1, -2, 10) temp = vsapi->mapGetIntSaturated(in, "cycle", 0, &err);
PREFETCH(DGDecimate, temp - 1, temp, -(temp + 3), temp + 3) PREFETCH(Decimate, temp - 1, temp, -(temp + 3), temp + 3)
PREFETCHR0(mt_edge)
PREFETCHR1(mt_motion)
PREFETCHR0(mt_expand)
PREFETCHR0(mt_inpand)
PREFETCHR0(mt_inflate)
PREFETCHR0(mt_lut)
PREFETCHR0(mt_lutxy)
PREFETCHR0(mt_lutf)
PREFETCHR0(mt_luts)
PREFETCHR0(mt_makediff)
PREFETCHR0(mt_adddiff)
PREFETCHR0(mt_average)
PREFETCHR0(mt_clamp)
PREFETCHR0(mt_merge)
PREFETCHR0(mt_logic)
PREFETCHR0(mt_hysteresis)
PREFETCHR0(mt_invert)
PREFETCHR0(mt_binarize)
PREFETCHR0(mt_convolution)
PREFETCHR0(mt_mappedblur)
OTHER(mt_circle)
OTHER(mt_square)
OTHER(mt_diamond)
OTHER(mt_losange)
OTHER(mt_rectangle)
OTHER(mt_ellipse)
OTHER(mt_polish)
BROKEN(RemoveGrain)
BROKEN(Repair)
PREFETCHR0(VagueDenoiser)
PREFETCHR0(UnDot)
PREFETCHR0(SangNom)
PREFETCHR0(gradfun2db)
PREFETCHR0(debilinear)
PREFETCHR0(debilinearY)
SOURCE(dss2)
SOURCE(DirectShowSource)
PREFETCHR0(SCXvid)
PREFETCHR0(ResampleHQ)
PREFETCHR0(AssRender)
PREFETCHR0(TextSub)
PREFETCHR0(VobSub)
PREFETCHR2(fft3dGPU)
PREFETCHR2(FFT3DFilter)
PREFETCHR1(Convolution3D)
PREFETCHR1(deen)
PREFETCHR0(eDeen)
PREFETCHR0(MSuper)
temp = vsapi->mapGetIntSaturated(in, "delta", 0, &err);
if (temp < 1)
temp = 1;
PREFETCH(MAnalyse, 1, 1, -temp, temp)
PREFETCHR3(MDegrain1)
PREFETCHR3(MDegrain2)
PREFETCHR3(MDegrain3)
PREFETCHR3(MCompensate)
PREFETCHR0(MMask)
PREFETCHR0(MSCDetection)
PREFETCHR0(MShow)
PREFETCHR0(MDepan)
PREFETCHR0(MFlow)
PREFETCHR1(MFlowInter)
PREFETCHR1(MFlowFps)
PREFETCHR1(MBlockFps)
PREFETCHR1(MFlowBlur)
PREFETCHR1(MRecalculate)
PREFETCHR0(aBlur)
PREFETCHR0(aSobel)
PREFETCHR0(aWarp)
PREFETCHR0(aWarp4)
PREFETCHR0(aWarpSharp)
PREFETCHR0(aWarpSharp2)
PREFETCHR0(CullBilinearResize)
PREFETCHR0(CullBicubicResize)
PREFETCHR0(CullLanczosResize)
PREFETCHR0(CullLanczos4Resize)
PREFETCHR0(CullBlackmanResize)
PREFETCHR0(CullSpline16Resize)
PREFETCHR0(CullSpline36Resize)
PREFETCHR0(CullSpline64Resize)
PREFETCHR0(CullGaussResize)
PREFETCHR0(Spline100Resize)
PREFETCHR0(Spline144Resize)
temp = int64ToIntS(vsapi->mapGetInt(in, "mode", 0, &err));
PREFETCH(DGBob, (temp > 0) ? 2 : 1, 1, -2, 2)
PREFETCH(PVBob, (temp > 0) ? 2 : 1, 1, -2, 2)
PREFETCH(Bob, 2, 1, 0, 0)
PREFETCH(TemporalSoften, 1, 1, -5, 5)
temp = vsapi->mapGetIntSaturated(in, "temporal_radius", 0, &err);
if (err || temp < 0)
temp = 20;
PREFETCH(AutoAdjust, 1, 1, -temp, temp)
return PrefetchInfo(1, 1, 0, -1);
}
static const VSFrame *VS_CC avisynthFilterGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
WrappedClip *clip = reinterpret_cast<WrappedClip *>(instanceData);
PVideoFrame frame;
n = std::min(n, clip->clip->GetVideoInfo().num_frames - 1);
if (activationReason == arAllFramesReady || (activationReason == arInitial && (clip->preFetchClips.empty() || clip->prefetchInfo.from > clip->prefetchInfo.to))) {
if (!clip->preFetchClips.empty()) {
clip->fakeEnv->uglyN = n;
clip->fakeEnv->uglyCtx = frameCtx;
}
try {
frame = clip->clip->GetFrame(n, clip->fakeEnv);
if (!frame)
vsapi->logMessage(mtFatal, "Avisynth Error: no frame returned", core);
} catch (const AvisynthError &e) {
vsapi->logMessage(mtFatal, ("Avisynth Error: avisynth errors in GetFrame() are unrecoverable, crashing... "s + e.msg).c_str(), core);
} catch (const IScriptEnvironment::NotFound &) {
vsapi->logMessage(mtFatal, "Avisynth Error: escaped IScriptEnvironment::NotFound exceptions are non-recoverable, crashing...", core);
} catch (...) {
vsapi->logMessage(mtFatal, "Avisynth Error: avisynth errors are unrecoverable, crashing...", core);
}
clip->fakeEnv->uglyCtx = nullptr;
} else if (activationReason == arInitial) {
for (VSNode *c : clip->preFetchClips)
prefetchHelper(n, c, clip->prefetchInfo, frameCtx, vsapi);
} else if (activationReason == arError) {
return nullptr;
}
const VSFrame *ref = nullptr;
if (frame)
ref = clip->fakeEnv->avsToVSFrame((VideoFrame *)((void *)frame));
return ref;
}
static void VS_CC avisynthFilterFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
WrappedClip *clip = (WrappedClip *)instanceData;
delete clip;
}
static bool isSupportedPF(const VSVideoFormat &f, int interfaceVersion) {
if (interfaceVersion == 2) {
return IsSameVideoFormat(f, cfYUV, stInteger, 8, 1, 1);
} else {
return !!VSFormatToAVSPixelType(f, false);
}
}
static void VS_CC fakeAvisynthFunctionWrapper(const VSMap *in, VSMap *out, void *userData,
VSCore *core, const VSAPI *vsapi) {
WrappedFunction *wf = (WrappedFunction *)userData;
std::unique_ptr<FakeAvisynth> fakeEnv(new FakeAvisynth(wf->interfaceVersion, core, vsapi));
std::vector<AVSValue> inArgs(wf->parsedArgs.size());
std::vector<VSNode *> preFetchClips;
int err;
bool pack = !!vsapi->mapGetInt(in, "compatpack", 0, &err);
for (size_t i = 0; i < inArgs.size(); i++) {
const AvisynthArgs &parsedArg = wf->parsedArgs.at(i);
if (vsapi->mapNumElements(in, parsedArg.name.data()) > 0) {
switch (parsedArg.type) {
case 'i':
inArgs[i] = vsapi->mapGetIntSaturated(in, parsedArg.name.c_str(), 0, nullptr);
break;
case 'f':
inArgs[i] = vsapi->mapGetFloat(in, parsedArg.name.c_str(), 0, nullptr);
break;
case 'b':
inArgs[i] = !!vsapi->mapGetInt(in, parsedArg.name.c_str(), 0, nullptr);
break;
case 's':
inArgs[i] = vsapi->mapGetData(in, parsedArg.name.c_str(), 0, nullptr);
break;
case 'c':
VSNode *cr = vsapi->mapGetNode(in, parsedArg.name.c_str(), 0, nullptr);
const VSVideoInfo *vi = vsapi->getVideoInfo(cr);
if (!isConstantVideoFormat(vi) || !isSupportedPF(vi->format, wf->interfaceVersion)) {
vsapi->mapSetError(out, "Invalid avisynth colorspace in one of the input clips");
vsapi->freeNode(cr);
return;
}
VSClip *tmpclip = new VSClip(cr, fakeEnv.get(), pack, vsapi);
preFetchClips.push_back(tmpclip->GetVSNode());
inArgs[i] = tmpclip;
break;
}
}
}
AVSValue inArgAVSValue(inArgs.data(), static_cast<int>(wf->parsedArgs.size()));
AVSValue ret;
try {
ret = wf->apply(inArgAVSValue, wf->avsUserData, fakeEnv.get());
} catch (const AvisynthError &e) {
vsapi->mapSetError(out, e.msg);
return;
} catch (const IScriptEnvironment::NotFound &) {
vsapi->logMessage(mtFatal, "Avisynth Error: escaped IScriptEnvironment::NotFound exceptions are non-recoverable, crashing... ", core);
}
fakeEnv->initializing = false;
if (ret.IsClip()) {
PClip clip = ret.AsClip();
PrefetchInfo prefetchInfo = getPrefetchInfo(wf->name, in, core, vsapi);
std::unique_ptr<WrappedClip> filterData(new WrappedClip(wf->name, clip, preFetchClips, prefetchInfo, fakeEnv.get()));
if (!filterData->preFetchClips.empty())
filterData->fakeEnv->uglyNode = filterData->preFetchClips.front();
const VideoInfo &viAvs = filterData->clip->GetVideoInfo();
VSVideoInfo vi;
vi.height = viAvs.height;
vi.width = viAvs.width;
vi.numFrames = viAvs.num_frames;
vi.fpsNum = viAvs.fps_numerator;
vi.fpsDen = viAvs.fps_denominator;
reduceRational(&vi.fpsNum, &vi.fpsDen);
bool unpack;
if (!AVSPixelTypeToVSFormat(vi.format, unpack, viAvs, core, vsapi)) {
vsapi->mapSetError(out, "Avisynth Compat: bad format!");
return;
}
std::vector<VSFilterDependency> deps;
for (size_t i = 0; i < preFetchClips.size(); i++)
deps.push_back({preFetchClips[i], rpGeneral});
VSNode *node = vsapi->createVideoFilter2(
wf->name.c_str(),
&vi,
avisynthFilterGetFrame,
avisynthFilterFree,
(preFetchClips.empty() || prefetchInfo.from > prefetchInfo.to) ? fmFrameState : fmParallelRequests,
deps.data(),
static_cast<int>(preFetchClips.size()),
filterData.release(),
core);
if (unpack) {
const VideoInfo &vi = clip->GetVideoInfo();
if (vi.IsRGB32()) {
node = unpackRGB32Create(node, core, vsapi);
} else if (vi.IsYUY2()) {
node = unpackYUY2Create(node, core, vsapi);
}
}
vsapi->mapConsumeNode(out, "clip", node, maReplace);
} else if (ret.IsBool()) {
vsapi->mapSetInt(out, "val", ret.AsBool() ? 1 : 0, maReplace);
} else if (ret.IsInt()) {
vsapi->mapSetInt(out, "val", ret.AsInt(), maReplace);
} else if (ret.IsFloat()) {
vsapi->mapSetFloat(out, "val", ret.AsFloat(), maReplace);
} else if (ret.IsString()) {
vsapi->mapSetData(out, "val", ret.AsString(), -1, dtUtf8, maReplace);
}
fakeEnv.release();
}
void FakeAvisynth::AddFunction(const char *name, const char *params, ApplyFunc apply, void *user_data) {
size_t paramLength = strlen(params);
size_t paramPos = 0;
int argNum = 1;
std::vector<AvisynthArgs> parsedArgs;
std::string newArgs;
std::string fname(name);
if (fname == "RemoveGrain" || fname == "Repair" || fname == "ColorMatrix" || fname == "IsCombed") {
vsapi->logMessage(mtWarning, ("Avisynth Compat: rejected adding Avisynth function " + fname + "because it is too broken").c_str(), core);
return;
}
if (fname == "FFMS2" || fname == "FFCopyrightInfringement") {
vsapi->logMessage(mtWarning, ("Avisynth Compat: rejected adding Avisynth function " + fname + "because it calls invoke").c_str(), core);
return;
}
while (paramPos < paramLength) {
if (params[paramPos] == '*' || params[paramPos] == '+' || params[paramPos] == '.') {
vsapi->logMessage(mtWarning, ("Avisynth Compat: varargs not implemented so I'm just gonna skip importing " + fname).c_str(), core);
return;
}
if (params[paramPos] == '[') { std::string argName(params);
size_t nameStart = ++paramPos;
while (paramPos < paramLength) {
if (params[paramPos++] == ']') {
argName = argName.substr(nameStart, paramPos - nameStart - 1);
break;
}
}
newArgs += argName + ":" + charToFilterArgumentString(params[paramPos]) + ":opt;";
parsedArgs.push_back(AvisynthArgs(argName, params[paramPos++], false));
} else {
newArgs += params[paramPos] + std::to_string(argNum) + ":" + charToFilterArgumentString(params[paramPos]) + ";";
parsedArgs.push_back(AvisynthArgs((params[paramPos] + std::to_string(argNum)), params[paramPos], true));
paramPos++;
argNum++;
}
}
newArgs += "compatpack:int:opt;";
std::lock_guard<std::mutex> lock(registerFunctionLock);
if (registeredFunctions.count(fname)) {
for (size_t i = 2; i < SIZE_MAX; i++) {
std::string numberedName = fname + "_" + std::to_string(i);
if (!registeredFunctions.count(numberedName)) {
fname = numberedName;
break;
}
}
}
registeredFunctions.insert(fname);
vsapi->registerFunction(fname.c_str(), newArgs.c_str(), "clip:vnode;", fakeAvisynthFunctionWrapper, new WrappedFunction(fname, apply, parsedArgs, user_data, interfaceVersion), vsapi->getPluginByID("com.vapoursynth.avisynth", core));
}
bool FakeAvisynth::FunctionExists(const char *name) {
vsapi->logMessage(mtWarning, "FunctionExists not implemented", core);
return false;
}
AVSValue FakeAvisynth::Invoke(const char *name, const AVSValue args, const char* const* arg_names) {
if (!_stricmp(name, "Cache") || !_stricmp(name, "InternalCache")) {
return args;
}
if (!_stricmp(name, "Crop")) {
vsapi->logMessage(mtWarning, "Invoke not fully implemented, tried to call Crop() but I will do nothing", core);
return args[0];
}
if (!_stricmp(name, "AudioDub")) {
vsapi->logMessage(mtWarning, "Invoke not fully implemented, tried to call AudioDub() but I will do nothing", core);
return args[0];
}
vsapi->logMessage(mtWarning, ("Invoke not fully implemented, tried to call: "s + name + " but I will pretend it doesn't exist").c_str(), core);
throw IScriptEnvironment::NotFound();
return AVSValue();
}
AVSValue FakeAvisynth::GetVar(const char *name) {
return AVSValue();
}
bool FakeAvisynth::SetVar(const char *name, const AVSValue &val) {
return true;
}
bool FakeAvisynth::SetGlobalVar(const char *name, const AVSValue &val) {
return true;
}
void FakeAvisynth::PushContext(int level) {
vsapi->logMessage(mtFatal, "PushContext not implemented", core);
}
void FakeAvisynth::PopContext() {
vsapi->logMessage(mtFatal, "PopContext not implemented", core);
}
PVideoFrame FakeAvisynth::NewVideoFrame(const VideoInfo &vi, int align) {
VSFrame *ref = nullptr;
assert(vi.width > 0);
assert(vi.height > 0);
const VSFrame *propSrc = nullptr;
if (uglyNode && uglyCtx)
propSrc = vsapi->getFrameFilter(uglyN, uglyNode, uglyCtx);
bool isMultiplePlanes = (vi.pixel_type & VideoInfo::CS_PLANAR) && !(vi.pixel_type & VideoInfo::CS_INTERLEAVED);
VSVideoFormat f;
bool unpack;
if (!AVSPixelTypeToVSFormat(f, unpack, vi, core, vsapi))
vsapi->logMessage(mtFatal, "Unsupported frame format in newvideoframe (alpha and/or packed RGB not supported)", core);
ref = vsapi->newVideoFrame(&f, vi.width, vi.height, propSrc, core);
if (propSrc)
vsapi->freeFrame(propSrc);
uint8_t *firstPlanePtr = vsapi->getWritePtr(ref, 0);
VideoFrame *vfb = new VideoFrame(
(BYTE *)firstPlanePtr,
true,
0,
static_cast<int>(vsapi->getStride(ref, 0)),
vi.width * f.bytesPerSample,
vi.height,
isMultiplePlanes ? vsapi->getWritePtr(ref, 1) - firstPlanePtr : 0,
isMultiplePlanes ? vsapi->getWritePtr(ref, 2) - firstPlanePtr : 0,
isMultiplePlanes ? static_cast<int>(vsapi->getStride(ref, 1)) : 0,
vsapi->getFrameWidth(ref, 1) * f.bytesPerSample,
vsapi->getFrameHeight(ref, 1));
PVideoFrame pvf(vfb);
ownedFrames.insert(std::make_pair(vfb, ref));
return pvf;
}
bool FakeAvisynth::MakeWritable(PVideoFrame *pvf) {
VideoFrame *vfb = (VideoFrame *)(void *)(*pvf);
auto it = ownedFrames.find(vfb);
assert(it != ownedFrames.end());
VSFrame *ref = vsapi->copyFrame(it->second, core);
uint8_t *firstPlanePtr = vsapi->getWritePtr(ref, 0);
VideoFrame *newVfb = new VideoFrame(
(BYTE *)firstPlanePtr,
true,
0,
static_cast<int>(vsapi->getStride(ref, 0)),
(*pvf)->row_size,
(*pvf)->height,
vsapi->getWritePtr(ref, 1) - firstPlanePtr,
vsapi->getWritePtr(ref, 2) - firstPlanePtr,
static_cast<int>(vsapi->getStride(ref, 1)),
(*pvf)->row_sizeUV,
(*pvf)->heightUV);
*pvf = PVideoFrame(newVfb);
ownedFrames.insert(std::make_pair(newVfb, ref));
return true;
}
void FakeAvisynth::BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) {
if (src_pitch == dst_pitch && dst_pitch == row_size) {
memcpy(dstp, srcp, row_size * height);
} else {
for (int i = 0; i < height; i++) {
memcpy(dstp, srcp, row_size);
dstp += dst_pitch;
srcp += src_pitch;
}
}
}
void FakeAvisynth::AtExit(ShutdownFunc function, void *user_data) {
}
void FakeAvisynth::CheckVersion(int version) {
if (version > AVISYNTH_INTERFACE_VERSION)
ThrowError("Plugin was designed for a later version of Avisynth (%d)", version);
}
PVideoFrame FakeAvisynth::Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) {
vsapi->logMessage(mtFatal, "Subframe not implemented", core);
if (src->row_size != new_row_size)
vsapi->logMessage(mtFatal, "Subframe only partially implemented (row_size != new_row_size)", core);
const VSFrame *f = avsToVSFrame((VideoFrame *)(void *)src);
VideoInfo vi;
vi.height = new_height;
vi.width = vsapi->getFrameWidth(f, 0);
PVideoFrame dst = NewVideoFrame(vi);
BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr() + rel_offset, new_pitch, new_row_size, new_height);
return dst;
}
int FakeAvisynth::SetMemoryMax(int mem) {
return 0;
}
int FakeAvisynth::SetWorkingDir(const char *newdir) {
vsapi->logMessage(mtFatal, "SetWorkingDir not implemented", core);
return 1;
}
void *FakeAvisynth::ManageCache(int key, void *data) {
vsapi->logMessage(mtFatal, "ManageCache not implemented", core);
return nullptr;
}
bool FakeAvisynth::PlanarChromaAlignment(PlanarChromaAlignmentMode key) {
vsapi->logMessage(mtFatal, "PlanarChromaAlignment not implemented", core);
return true;
}
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) {
vsapi->logMessage(mtFatal, "SubframePlanar not implemented", core);
return nullptr;
}
void FakeAvisynth::DeleteScriptEnvironment() {
vsapi->logMessage(mtFatal, "DeleteScriptEnvironment not implemented", core);
}
void FakeAvisynth::ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size,
int textcolor, int halocolor, int bgcolor) {
vsapi->logMessage(mtFatal, "ApplyMessage not implemented", core);
}
const AVS_Linkage* const FakeAvisynth::GetAVSLinkage() {
return AVS_linkage;
}
AVSValue FakeAvisynth::GetVarDef(const char* name, const AVSValue& def) {
return def;
}
size_t FakeAvisynth::GetProperty(AvsEnvProperty prop) {
switch (prop) {
case AEP_FILTERCHAIN_THREADS:
return 1;
case AEP_PHYSICAL_CPUS:
return 1;
case AEP_LOGICAL_CPUS:
return 1;
case AEP_THREAD_ID:
return 0;
case AEP_THREADPOOL_THREADS:
return 1;
case AEP_VERSION:
return 0;
default:
this->ThrowError("Invalid property request.");
return std::numeric_limits<size_t>::max();
}
assert(0);
}
bool FakeAvisynth::GetVar(const char* name, AVSValue *val) const {
return val;
}
bool FakeAvisynth::GetVar(const char* name, bool def) const {
return def;
}
int FakeAvisynth::GetVar(const char* name, int def) const {
return def;
}
double FakeAvisynth::GetVar(const char* name, double def) const {
return def;
}
const char* FakeAvisynth::GetVar(const char* name, const char* def) const {
return def;
}
bool FakeAvisynth::LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result) {
vsapi->logMessage(mtFatal, "Plugin loading not implemented", core);
return false;
}
void FakeAvisynth::AddAutoloadDir(const char* dirPath, bool toFront) {
vsapi->logMessage(mtFatal, "Autoloading dirs not implemented", core);
}
void FakeAvisynth::ClearAutoloadDirs() {
vsapi->logMessage(mtFatal, "Clearing autoload dirs not implemented", core);
}
void FakeAvisynth::AutoloadPlugins() {
vsapi->logMessage(mtFatal, "Autoloading not implemented", core);
}
void FakeAvisynth::AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data, const char *exportVar) {
AddFunction(name, params, apply, user_data);
}
bool FakeAvisynth::InternalFunctionExists(const char* name) {
return false;
}
void FakeAvisynth::SetFilterMTMode(const char* filter, MtMode mode, bool force) {
}
IJobCompletion* FakeAvisynth::NewCompletion(size_t capacity) {
vsapi->logMessage(mtFatal, "Completions not implemented", core);
return nullptr;
}
void FakeAvisynth::ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion) {
vsapi->logMessage(mtFatal, "Threadpool not implemented", core);
}
bool FakeAvisynth::Invoke(AVSValue *result, const char* name, const AVSValue& args, const char* const* arg_names) {
try {
*result = Invoke(name, args, arg_names);
return true;
} catch (NotFound &) {
return false;
}
}
void* FakeAvisynth::Allocate(size_t nBytes, size_t alignment, AvsAllocType type) {
return vsh_aligned_malloc(nBytes, alignment);
}
void FakeAvisynth::Free(void* ptr) {
vsh_aligned_free(ptr);
}
PVideoFrame FakeAvisynth::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) {
vsapi->logMessage(mtFatal, "SubframePlanarA not implemented", core);
return PVideoFrame();
}
static void VS_CC avsLoadPlugin(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
std::filesystem::path fsPath = std::filesystem::u8path(vsapi->mapGetData(in, "path", 0, nullptr));
HMODULE plugin = LoadLibraryW(fsPath.c_str());
typedef const char *(__stdcall *AvisynthPluginInit2Func)(IScriptEnvironment *env);
typedef const char *(__stdcall *AvisynthPluginInit3Func)(IScriptEnvironment *env, const AVS_Linkage *const vectors);
if (!plugin) {
DWORD lastError = GetLastError();
if (lastError == 126)
vsapi->mapSetError(out, ("Failed to load " + fsPath.u8string() + ". GetLastError() returned " + std::to_string(lastError) + ". The file you tried to load or one of its dependencies is probably missing.").c_str());
else
vsapi->mapSetError(out, ("Failed to load " + fsPath.u8string() + ". GetLastError() returned " + std::to_string(lastError) + ".").c_str());
return;
}
AvisynthPluginInit2Func avisynthPluginInit2 = nullptr;
AvisynthPluginInit3Func avisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "AvisynthPluginInit3");
if (!avisynthPluginInit3)
avisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "_AvisynthPluginInit3@8");
if (!avisynthPluginInit3) {
avisynthPluginInit2 = (AvisynthPluginInit2Func)GetProcAddress(plugin, "AvisynthPluginInit2");
if (!avisynthPluginInit2)
avisynthPluginInit2 = (AvisynthPluginInit2Func)GetProcAddress(plugin, "_AvisynthPluginInit2@4");
}
if (!avisynthPluginInit3 && !avisynthPluginInit2) {
vsapi->mapSetError(out, "Avisynth Loader: no entry point found");
FreeLibrary(plugin);
return;
}
if (avisynthPluginInit3) {
FakeAvisynth *avs = new FakeAvisynth(3, core, vsapi);
avisynthPluginInit3(avs, AVS_linkage);
delete avs;
} else {
#ifdef _WIN64
vsapi->mapSetError(out, "Avisynth Loader: 2.5 plugins can't be loaded on x64");
return;
#else
FakeAvisynth *avs = new FakeAvisynth(2, core, vsapi);
avisynthPluginInit2(avs);
delete avs;
#endif
}
#ifdef VS_TARGET_CPU_X86
if (!vs_isSSEStateOk())
vsapi->logMessage(mtFatal, ("Bad SSE state detected after loading " + fsPath.u8string()).c_str(), core);
#endif
}
WrappedFunction::WrappedFunction(const std::string &name, FakeAvisynth::ApplyFunc apply, const std::vector<AvisynthArgs> &parsedArgs, void *avsUserData, int interfaceVersion) :
name(name), apply(apply), parsedArgs(parsedArgs), avsUserData(avsUserData), interfaceVersion(interfaceVersion) {
}
VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin *plugin, const VSPLUGINAPI *vspapi) {
vspapi->configPlugin("com.vapoursynth.avisynth", "avs", "VapourSynth Avisynth Compatibility", VAPOURSYNTH_INTERNAL_PLUGIN_VERSION, VAPOURSYNTH_API_VERSION, pcModifiable, plugin);
vspapi->registerFunction("LoadPlugin", "path:data;", "", avsLoadPlugin, plugin, plugin);
}
}