diff --git a/include/vapoursynth/VSHelper.h b/include/vapoursynth/VSHelper.h new file mode 100644 index 000000000..c34457721 --- /dev/null +++ b/include/vapoursynth/VSHelper.h @@ -0,0 +1,174 @@ +/***************************************************************************** +* Copyright (c) 2012-2015 Fredrik Mellbin +* --- Legal stuff --- +* This program is free software. It comes without any warranty, to +* the extent permitted by applicable law. You can redistribute it +* and/or modify it under the terms of the Do What The Fuck You Want +* To Public License, Version 2, as published by Sam Hocevar. See +* http://sam.zoy.org/wtfpl/COPYING for more details. +*****************************************************************************/ + +#ifndef VSHELPER_H +#define VSHELPER_H + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif +#include "VapourSynth.h" + +/* Visual Studio doesn't recognize inline in c mode */ +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline _inline +#endif + +/* A kinda portable definition of the C99 restrict keyword (or its unofficial C++ equivalent) */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* Available in C99 */ +#define VS_RESTRICT restrict +#elif defined(__cplusplus) || defined(_MSC_VER) /* Almost all relevant C++ compilers support it so just assume it works */ +#define VS_RESTRICT __restrict +#else /* Not supported */ +#define VS_RESTRICT +#endif + +#ifdef _WIN32 +#define VS_ALIGNED_MALLOC(pptr, size, alignment) do { *(pptr) = _aligned_malloc((size), (alignment)); } while (0) +#define VS_ALIGNED_FREE(ptr) do { _aligned_free((ptr)); } while (0) +#else +#define VS_ALIGNED_MALLOC(pptr, size, alignment) do { if(posix_memalign((void**)(pptr), (alignment), (size))) *((void**)pptr) = NULL; } while (0) +#define VS_ALIGNED_FREE(ptr) do { free((ptr)); } while (0) +#endif + +#define VSMAX(a,b) ((a) > (b) ? (a) : (b)) +#define VSMIN(a,b) ((a) > (b) ? (b) : (a)) + +#ifdef __cplusplus +/* A nicer templated malloc for all the C++ users out there */ +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +template +#else +template +#endif +static inline T* vs_aligned_malloc(size_t size, size_t alignment) { +#ifdef _WIN32 + return (T*)_aligned_malloc(size, alignment); +#else + void *tmp = NULL; + if (posix_memalign(&tmp, alignment, size)) + tmp = 0; + return (T*)tmp; +#endif +} + +static inline void vs_aligned_free(void *ptr) { + VS_ALIGNED_FREE(ptr); +} +#endif /* __cplusplus */ + +/* convenience function for checking if the format never changes between frames */ +static inline int isConstantFormat(const VSVideoInfo *vi) { + return vi->height > 0 && vi->width > 0 && vi->format; +} + +/* convenience function to check for if two clips have the same format (unknown/changeable will be considered the same too) */ +static inline int isSameFormat(const VSVideoInfo *v1, const VSVideoInfo *v2) { + return v1->height == v2->height && v1->width == v2->width && v1->format == v2->format; +} + +/* multiplies and divides a rational number, such as a frame duration, in place and reduces the result */ +static inline void muldivRational(int64_t *num, int64_t *den, int64_t mul, int64_t div) { + /* do nothing if the rational number is invalid */ + if (!*den) + return; + + /* nobody wants to accidentally divide by zero */ + assert(div); + + int64_t a, b; + *num *= mul; + *den *= div; + a = *num; + b = *den; + while (b != 0) { + int64_t t = a; + a = b; + b = t % b; + } + if (a < 0) + a = -a; + *num /= a; + *den /= a; +} + +/* reduces a rational number */ +static inline void vs_normalizeRational(int64_t *num, int64_t *den) { + muldivRational(num, den, 1, 1); +} + +/* add two rational numbers and reduces the result */ +static inline void vs_addRational(int64_t *num, int64_t *den, int64_t addnum, int64_t addden) { + /* do nothing if the rational number is invalid */ + if (!*den) + return; + + /* nobody wants to accidentally add an invalid rational number */ + assert(addden); + + if (*den == addden) { + *num += addnum; + } else { + int64_t temp = addden; + addnum *= *den; + addden *= *den; + *num *= temp; + *den *= temp; + + *num += addnum; + + vs_normalizeRational(num, den); + } +} + +/* converts an int64 to int with saturation, useful to silence warnings when reading int properties among other things */ +static inline int int64ToIntS(int64_t i) { + if (i > INT_MAX) + return INT_MAX; + else if (i < INT_MIN) + return INT_MIN; + else return (int)i; +} + +static inline void vs_bitblt(void *dstp, int dst_stride, const void *srcp, int src_stride, size_t row_size, size_t height) { + if (height) { + if (src_stride == dst_stride && src_stride == (int)row_size) { + memcpy(dstp, srcp, row_size * height); + } else { + const uint8_t *srcp8 = (const uint8_t *)srcp; + uint8_t *dstp8 = (uint8_t *)dstp; + size_t i; + for (i = 0; i < height; i++) { + memcpy(dstp8, srcp8, row_size); + srcp8 += src_stride; + dstp8 += dst_stride; + } + } + } +} + +/* check if the frame dimensions are valid for a given format */ +/* returns non-zero for valid width and height */ +static inline int areValidDimensions(const VSFormat *fi, int width, int height) { + return !(width % (1 << fi->subSamplingW) || height % (1 << fi->subSamplingH)); +} + +/* Visual Studio doesn't recognize inline in c mode */ +#if defined(_MSC_VER) && !defined(__cplusplus) +#undef inline +#endif + +#endif diff --git a/include/vapoursynth/VapourSynth.h b/include/vapoursynth/VapourSynth.h new file mode 100644 index 000000000..cdbecacfe --- /dev/null +++ b/include/vapoursynth/VapourSynth.h @@ -0,0 +1,359 @@ +/* +* Copyright (c) 2012-2017 Fredrik Mellbin +* +* This file is part of VapourSynth. +* +* VapourSynth is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* VapourSynth is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with VapourSynth; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef VAPOURSYNTH_H +#define VAPOURSYNTH_H + +#include + +#define VAPOURSYNTH_API_MAJOR 3 +#define VAPOURSYNTH_API_MINOR 6 +#define VAPOURSYNTH_API_VERSION ((VAPOURSYNTH_API_MAJOR << 16) | (VAPOURSYNTH_API_MINOR)) + +/* Convenience for C++ users. */ +#ifdef __cplusplus +# define VS_EXTERN_C extern "C" +# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +# define VS_NOEXCEPT noexcept +# else +# define VS_NOEXCEPT +# endif +# if __cplusplus >= 201402L || (defined(_MSC_VER) && _MSC_VER >= 1900) +# define VS_DEPRECATE(REASON) [[deprecated(REASON)]] +# else +# define VS_DEPRECATE(REASON) +# endif +#else +# define VS_EXTERN_C +# define VS_NOEXCEPT +# define VS_DEPRECATE(REASON) +#endif + +#if defined(_WIN32) && !defined(_WIN64) +# define VS_CC __stdcall +#else +# define VS_CC +#endif + +/* And now for some symbol hide-and-seek... */ +#if defined(_WIN32) /* Windows being special */ +# define VS_EXTERNAL_API(ret) VS_EXTERN_C __declspec(dllexport) ret VS_CC +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define VS_EXTERNAL_API(ret) VS_EXTERN_C __attribute__((visibility("default"))) ret VS_CC +#else +# define VS_EXTERNAL_API(ret) VS_EXTERN_C ret VS_CC +#endif + +#if !defined(VS_CORE_EXPORTS) && defined(_WIN32) +# define VS_API(ret) VS_EXTERN_C __declspec(dllimport) ret VS_CC +#else +# define VS_API(ret) VS_EXTERNAL_API(ret) +#endif + +typedef struct VSFrameRef VSFrameRef; +typedef struct VSNodeRef VSNodeRef; +typedef struct VSCore VSCore; +typedef struct VSPlugin VSPlugin; +typedef struct VSNode VSNode; +typedef struct VSFuncRef VSFuncRef; +typedef struct VSMap VSMap; +typedef struct VSAPI VSAPI; +typedef struct VSFrameContext VSFrameContext; + +typedef enum VSColorFamily { + /* all planar formats */ + cmGray = 1000000, + cmRGB = 2000000, + cmYUV = 3000000, + cmYCoCg = 4000000, + /* special for compatibility */ + cmCompat = 9000000 +} VSColorFamily; + +typedef enum VSSampleType { + stInteger = 0, + stFloat = 1 +} VSSampleType; + +/* The +10 is so people won't be using the constants interchangeably "by accident" */ +typedef enum VSPresetFormat { + pfNone = 0, + + pfGray8 = cmGray + 10, + pfGray16, + + pfGrayH, + pfGrayS, + + pfYUV420P8 = cmYUV + 10, + pfYUV422P8, + pfYUV444P8, + pfYUV410P8, + pfYUV411P8, + pfYUV440P8, + + pfYUV420P9, + pfYUV422P9, + pfYUV444P9, + + pfYUV420P10, + pfYUV422P10, + pfYUV444P10, + + pfYUV420P16, + pfYUV422P16, + pfYUV444P16, + + pfYUV444PH, + pfYUV444PS, + + pfYUV420P12, + pfYUV422P12, + pfYUV444P12, + + pfYUV420P14, + pfYUV422P14, + pfYUV444P14, + + pfRGB24 = cmRGB + 10, + pfRGB27, + pfRGB30, + pfRGB48, + + pfRGBH, + pfRGBS, + + /* special for compatibility, if you implement these in any filter I'll personally kill you */ + /* I'll also change their ids around to break your stuff regularly */ + pfCompatBGR32 = cmCompat + 10, + pfCompatYUY2 +} VSPresetFormat; + +typedef enum VSFilterMode { + fmParallel = 100, /* completely parallel execution */ + fmParallelRequests = 200, /* for filters that are serial in nature but can request one or more frames they need in advance */ + fmUnordered = 300, /* for filters that modify their internal state every request */ + fmSerial = 400 /* for source filters and compatibility with other filtering architectures */ +} VSFilterMode; + +typedef struct VSFormat { + char name[32]; + int id; + int colorFamily; /* see VSColorFamily */ + int sampleType; /* see VSSampleType */ + int bitsPerSample; /* number of significant bits */ + int bytesPerSample; /* actual storage is always in a power of 2 and the smallest possible that can fit the number of bits used per sample */ + + int subSamplingW; /* log2 subsampling factor, applied to second and third plane */ + int subSamplingH; + + int numPlanes; /* implicit from colorFamily */ +} VSFormat; + +typedef enum VSNodeFlags { + nfNoCache = 1, + nfIsCache = 2, + nfMakeLinear = 4 /* api 3.3 */ +} VSNodeFlags; + +typedef enum VSPropTypes { + ptUnset = 'u', + ptInt = 'i', + ptFloat = 'f', + ptData = 's', + ptNode = 'c', + ptFrame = 'v', + ptFunction = 'm' +} VSPropTypes; + +typedef enum VSGetPropErrors { + peUnset = 1, + peType = 2, + peIndex = 4 +} VSGetPropErrors; + +typedef enum VSPropAppendMode { + paReplace = 0, + paAppend = 1, + paTouch = 2 +} VSPropAppendMode; + +typedef struct VSCoreInfo { + const char *versionString; + int core; + int api; + int numThreads; + int64_t maxFramebufferSize; + int64_t usedFramebufferSize; +} VSCoreInfo; + +typedef struct VSVideoInfo { + const VSFormat *format; + int64_t fpsNum; + int64_t fpsDen; + int width; + int height; + int numFrames; /* api 3.2 - no longer allowed to be 0 */ + int flags; +} VSVideoInfo; + +typedef enum VSActivationReason { + arInitial = 0, + arFrameReady = 1, + arAllFramesReady = 2, + arError = -1 +} VSActivationReason; + +typedef enum VSMessageType { + mtDebug = 0, + mtWarning = 1, + mtCritical = 2, + mtFatal = 3 +} VSMessageType; + +/* core entry point */ +typedef const VSAPI *(VS_CC *VSGetVapourSynthAPI)(int version); + +/* plugin function and filter typedefs */ +typedef void (VS_CC *VSPublicFunction)(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi); +typedef void (VS_CC *VSRegisterFunction)(const char *name, const char *args, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin); +typedef void (VS_CC *VSConfigPlugin)(const char *identifier, const char *defaultNamespace, const char *name, int apiVersion, int readonly, VSPlugin *plugin); +typedef void (VS_CC *VSInitPlugin)(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin); +typedef void (VS_CC *VSFreeFuncData)(void *userData); +typedef void (VS_CC *VSFilterInit)(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi); +typedef const VSFrameRef *(VS_CC *VSFilterGetFrame)(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi); +typedef void (VS_CC *VSFilterFree)(void *instanceData, VSCore *core, const VSAPI *vsapi); + +/* other */ +typedef void (VS_CC *VSFrameDoneCallback)(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg); +typedef void (VS_CC *VSMessageHandler)(int msgType, const char *msg, void *userData); +typedef void (VS_CC *VSMessageHandlerFree)(void *userData); + +struct VSAPI { + VSCore *(VS_CC *createCore)(int threads) VS_NOEXCEPT; + void (VS_CC *freeCore)(VSCore *core) VS_NOEXCEPT; + + VS_DEPRECATE("getCoreInfo has been deprecated as of api 3.6, use getCoreInfo2 instead") + const VSCoreInfo *(VS_CC *getCoreInfo)(VSCore *core) VS_NOEXCEPT; + + const VSFrameRef *(VS_CC *cloneFrameRef)(const VSFrameRef *f) VS_NOEXCEPT; + VSNodeRef *(VS_CC *cloneNodeRef)(VSNodeRef *node) VS_NOEXCEPT; + VSFuncRef *(VS_CC *cloneFuncRef)(VSFuncRef *f) VS_NOEXCEPT; + + void (VS_CC *freeFrame)(const VSFrameRef *f) VS_NOEXCEPT; + void (VS_CC *freeNode)(VSNodeRef *node) VS_NOEXCEPT; + void (VS_CC *freeFunc)(VSFuncRef *f) VS_NOEXCEPT; + + VSFrameRef *(VS_CC *newVideoFrame)(const VSFormat *format, int width, int height, const VSFrameRef *propSrc, VSCore *core) VS_NOEXCEPT; + VSFrameRef *(VS_CC *copyFrame)(const VSFrameRef *f, VSCore *core) VS_NOEXCEPT; + void (VS_CC *copyFrameProps)(const VSFrameRef *src, VSFrameRef *dst, VSCore *core) VS_NOEXCEPT; + + void (VS_CC *registerFunction)(const char *name, const char *args, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin) VS_NOEXCEPT; + VSPlugin *(VS_CC *getPluginById)(const char *identifier, VSCore *core) VS_NOEXCEPT; + VSPlugin *(VS_CC *getPluginByNs)(const char *ns, VSCore *core) VS_NOEXCEPT; + VSMap *(VS_CC *getPlugins)(VSCore *core) VS_NOEXCEPT; + VSMap *(VS_CC *getFunctions)(VSPlugin *plugin) VS_NOEXCEPT; + void (VS_CC *createFilter)(const VSMap *in, VSMap *out, const char *name, VSFilterInit init, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, int flags, void *instanceData, VSCore *core) VS_NOEXCEPT; + void (VS_CC *setError)(VSMap *map, const char *errorMessage) VS_NOEXCEPT; /* use to signal errors outside filter getframe functions */ + const char *(VS_CC *getError)(const VSMap *map) VS_NOEXCEPT; /* use to query errors, returns 0 if no error */ + void (VS_CC *setFilterError)(const char *errorMessage, VSFrameContext *frameCtx) VS_NOEXCEPT; /* use to signal errors in the filter getframe function */ + VSMap *(VS_CC *invoke)(VSPlugin *plugin, const char *name, const VSMap *args) VS_NOEXCEPT; + + const VSFormat *(VS_CC *getFormatPreset)(int id, VSCore *core) VS_NOEXCEPT; + const VSFormat *(VS_CC *registerFormat)(int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, VSCore *core) VS_NOEXCEPT; + + const VSFrameRef *(VS_CC *getFrame)(int n, VSNodeRef *node, char *errorMsg, int bufSize) VS_NOEXCEPT; /* do never use inside a filter's getframe function, for external applications using the core as a library or for requesting frames in a filter constructor */ + void (VS_CC *getFrameAsync)(int n, VSNodeRef *node, VSFrameDoneCallback callback, void *userData) VS_NOEXCEPT; /* do never use inside a filter's getframe function, for external applications using the core as a library or for requesting frames in a filter constructor */ + const VSFrameRef *(VS_CC *getFrameFilter)(int n, VSNodeRef *node, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + void (VS_CC *requestFrameFilter)(int n, VSNodeRef *node, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + void (VS_CC *queryCompletedFrame)(VSNodeRef **node, int *n, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + void (VS_CC *releaseFrameEarly)(VSNodeRef *node, int n, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + + int (VS_CC *getStride)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + const uint8_t *(VS_CC *getReadPtr)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + uint8_t *(VS_CC *getWritePtr)(VSFrameRef *f, int plane) VS_NOEXCEPT; + + VSFuncRef *(VS_CC *createFunc)(VSPublicFunction func, void *userData, VSFreeFuncData free, VSCore *core, const VSAPI *vsapi) VS_NOEXCEPT; + void (VS_CC *callFunc)(VSFuncRef *func, const VSMap *in, VSMap *out, VSCore *core, const VSAPI *vsapi) VS_NOEXCEPT; /* core and vsapi arguments are completely ignored, they only remain to preserve ABI */ + + /* property access functions */ + VSMap *(VS_CC *createMap)(void) VS_NOEXCEPT; + void (VS_CC *freeMap)(VSMap *map) VS_NOEXCEPT; + void (VS_CC *clearMap)(VSMap *map) VS_NOEXCEPT; + + const VSVideoInfo *(VS_CC *getVideoInfo)(VSNodeRef *node) VS_NOEXCEPT; + void (VS_CC *setVideoInfo)(const VSVideoInfo *vi, int numOutputs, VSNode *node) VS_NOEXCEPT; + const VSFormat *(VS_CC *getFrameFormat)(const VSFrameRef *f) VS_NOEXCEPT; + int (VS_CC *getFrameWidth)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + int (VS_CC *getFrameHeight)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + const VSMap *(VS_CC *getFramePropsRO)(const VSFrameRef *f) VS_NOEXCEPT; + VSMap *(VS_CC *getFramePropsRW)(VSFrameRef *f) VS_NOEXCEPT; + + int (VS_CC *propNumKeys)(const VSMap *map) VS_NOEXCEPT; + const char *(VS_CC *propGetKey)(const VSMap *map, int index) VS_NOEXCEPT; + int (VS_CC *propNumElements)(const VSMap *map, const char *key) VS_NOEXCEPT; + char (VS_CC *propGetType)(const VSMap *map, const char *key) VS_NOEXCEPT; + + int64_t(VS_CC *propGetInt)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + double(VS_CC *propGetFloat)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + const char *(VS_CC *propGetData)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + int (VS_CC *propGetDataSize)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + VSNodeRef *(VS_CC *propGetNode)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + const VSFrameRef *(VS_CC *propGetFrame)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + VSFuncRef *(VS_CC *propGetFunc)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + + int (VS_CC *propDeleteKey)(VSMap *map, const char *key) VS_NOEXCEPT; + int (VS_CC *propSetInt)(VSMap *map, const char *key, int64_t i, int append) VS_NOEXCEPT; + int (VS_CC *propSetFloat)(VSMap *map, const char *key, double d, int append) VS_NOEXCEPT; + int (VS_CC *propSetData)(VSMap *map, const char *key, const char *data, int size, int append) VS_NOEXCEPT; + int (VS_CC *propSetNode)(VSMap *map, const char *key, VSNodeRef *node, int append) VS_NOEXCEPT; + int (VS_CC *propSetFrame)(VSMap *map, const char *key, const VSFrameRef *f, int append) VS_NOEXCEPT; + int (VS_CC *propSetFunc)(VSMap *map, const char *key, VSFuncRef *func, int append) VS_NOEXCEPT; + + int64_t (VS_CC *setMaxCacheSize)(int64_t bytes, VSCore *core) VS_NOEXCEPT; + int (VS_CC *getOutputIndex)(VSFrameContext *frameCtx) VS_NOEXCEPT; + VSFrameRef *(VS_CC *newVideoFrame2)(const VSFormat *format, int width, int height, const VSFrameRef **planeSrc, const int *planes, const VSFrameRef *propSrc, VSCore *core) VS_NOEXCEPT; + + VS_DEPRECATE("setMessageHandler has been deprecated as of api 3.6, use addMessageHandler and removeMessageHandler instead") + void (VS_CC *setMessageHandler)(VSMessageHandler handler, void *userData) VS_NOEXCEPT; + + int (VS_CC *setThreadCount)(int threads, VSCore *core) VS_NOEXCEPT; + + const char *(VS_CC *getPluginPath)(const VSPlugin *plugin) VS_NOEXCEPT; + + /* api 3.1 */ + const int64_t *(VS_CC *propGetIntArray)(const VSMap *map, const char *key, int *error) VS_NOEXCEPT; + const double *(VS_CC *propGetFloatArray)(const VSMap *map, const char *key, int *error) VS_NOEXCEPT; + + int (VS_CC *propSetIntArray)(VSMap *map, const char *key, const int64_t *i, int size) VS_NOEXCEPT; + int (VS_CC *propSetFloatArray)(VSMap *map, const char *key, const double *d, int size) VS_NOEXCEPT; + + /* api 3.4 */ + void (VS_CC *logMessage)(int msgType, const char *msg) VS_NOEXCEPT; + + /* api 3.6 */ + int (VS_CC *addMessageHandler)(VSMessageHandler handler, VSMessageHandlerFree free, void *userData) VS_NOEXCEPT; + int (VS_CC *removeMessageHandler)(int id) VS_NOEXCEPT; + void (VS_CC *getCoreInfo2)(VSCore *core, VSCoreInfo *info) VS_NOEXCEPT; +}; + +VS_API(const VSAPI *) getVapourSynthAPI(int version) VS_NOEXCEPT; + +#endif /* VAPOURSYNTH_H */ diff --git a/src/filters/transform/vsfilter/plugins.cpp b/src/filters/transform/vsfilter/plugins.cpp index fa3cb80a7..faa0c5987 100644 --- a/src/filters/transform/vsfilter/plugins.cpp +++ b/src/filters/transform/vsfilter/plugins.cpp @@ -1183,6 +1183,272 @@ class CTextSubFilter : virtual public CFilter } } + // + // VapourSynth interface + // + + namespace VapourSynth { +#include +#include + + class CTextSubVapourSynthFilter : public CTextSubFilter { + public: + CTextSubVapourSynthFilter(const wchar_t *file, const int charset, const float fps, int *error) : CTextSubFilter(CString(file), charset, fps) { + *error = !m_pSubPicProvider ? 1 : 0; + } + }; + + class CVobSubVapourSynthFilter : public CVobSubFilter { + public: + CVobSubVapourSynthFilter(const wchar_t *file, int *error) : CVobSubFilter(CString(file)) { + *error = !m_pSubPicProvider ? 1 : 0; + } + }; + + struct VSFilterData { + VSNodeRef *node; + const VSVideoInfo *vi; + float fps; + VFRTranslator *vfr; + std::unique_ptr textsub; + std::unique_ptr vobsub; + std::unique_ptr buffer16; + std::unique_ptr buffer8; + }; + + static void VS_CC vsfilterInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) { + const VSFilterData *d = static_cast(*instanceData); + vsapi->setVideoInfo(d->vi, 1, node); + } + + static const VSFrameRef *VS_CC vsfilterGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { + const VSFilterData *d = static_cast(*instanceData); + + if (activationReason == arInitial) { + vsapi->requestFrameFilter(n, d->node, frameCtx); + } + else if (activationReason == arAllFramesReady) { + const VSFrameRef *src = vsapi->getFrameFilter(n, d->node, frameCtx); + VSFrameRef *dst = vsapi->copyFrame(src, core); + VSFrameRef *bgr = nullptr; + + SubPicDesc subpic; + subpic.w = d->vi->width; + subpic.h = d->vi->height; + + if (d->vi->format->id == pfYUV420P8) { + subpic.pitch = vsapi->getStride(dst, 0); + subpic.pitchUV = vsapi->getStride(dst, 1); + subpic.bits = vsapi->getWritePtr(dst, 0); + subpic.bitsU = vsapi->getWritePtr(dst, 1); + subpic.bitsV = vsapi->getWritePtr(dst, 2); + subpic.bpp = 8; + subpic.type = MSP_YV12; + } + else if (d->vi->format->id == pfYUV420P16) { + const int uvStride = vsapi->getStride(src, 1) / sizeof(uint16_t); + const int bufStride = d->vi->width; + const uint16_t *srcpY = reinterpret_cast(vsapi->getReadPtr(src, 0)); + const uint16_t *srcpU = reinterpret_cast(vsapi->getReadPtr(src, 1)); + const uint16_t *srcpV = reinterpret_cast(vsapi->getReadPtr(src, 2)); + uint16_t *VS_RESTRICT buffer = d->buffer16.get(); + + vs_bitblt(buffer, d->vi->width * sizeof(uint16_t), srcpY, vsapi->getStride(src, 0), d->vi->width * sizeof(uint16_t), d->vi->height); + buffer += bufStride * d->vi->height; + + for (int y = 0; y < vsapi->getFrameHeight(src, 1); y++) { + for (int x = 0; x < vsapi->getFrameWidth(src, 1); x++) { + buffer[x * 2 + 0] = srcpU[x]; + buffer[x * 2 + 1] = srcpV[x]; + } + + srcpU += uvStride; + srcpV += uvStride; + buffer += bufStride; + } + + subpic.pitch = d->vi->width * sizeof(uint16_t); + subpic.bits = reinterpret_cast(d->buffer16.get()); + subpic.bpp = 16; + subpic.type = MSP_P016; + } + else { + const int srcStride = vsapi->getStride(src, 0); + const int bgrStride = d->vi->width * 4; + const uint8_t *srcpR = vsapi->getReadPtr(src, 0); + const uint8_t *srcpG = vsapi->getReadPtr(src, 1); + const uint8_t *srcpB = vsapi->getReadPtr(src, 2); + uint8_t *VS_RESTRICT bgrp = d->buffer8.get(); + + bgrp += bgrStride * (d->vi->height - 1); + + for (int y = 0; y < d->vi->height; y++) { + for (int x = 0; x < d->vi->width; x++) { + bgrp[x * 4 + 0] = srcpB[x]; + bgrp[x * 4 + 1] = srcpG[x]; + bgrp[x * 4 + 2] = srcpR[x]; + bgrp[x * 4 + 3] = 0; + } + + srcpR += srcStride; + srcpG += srcStride; + srcpB += srcStride; + bgrp -= bgrStride; + } + + subpic.pitch = bgrStride; + subpic.bits = d->buffer8.get(); + subpic.bpp = 32; + subpic.type = MSP_RGB32; + } + + REFERENCE_TIME timestamp; + if (!d->vfr) + timestamp = static_cast(10000000i64 * n / d->fps); + else + timestamp = static_cast(10000000 * d->vfr->TimeStampFromFrameNumber(n)); + + if (d->textsub) + d->textsub->Render(subpic, timestamp, d->fps); + else + d->vobsub->Render(subpic, timestamp, d->fps); + + if (d->vi->format->id == pfYUV420P16) { + const int bufStride = d->vi->width; + const int uvStride = vsapi->getStride(dst, 1) / sizeof(uint16_t); + const uint16_t *buffer = d->buffer16.get(); + uint16_t *VS_RESTRICT dstpY = reinterpret_cast(vsapi->getWritePtr(dst, 0)); + uint16_t *VS_RESTRICT dstpU = reinterpret_cast(vsapi->getWritePtr(dst, 1)); + uint16_t *VS_RESTRICT dstpV = reinterpret_cast(vsapi->getWritePtr(dst, 2)); + + vs_bitblt(dstpY, vsapi->getStride(dst, 0), buffer, d->vi->width * sizeof(uint16_t), d->vi->width * sizeof(uint16_t), d->vi->height); + buffer += bufStride * d->vi->height; + + for (int y = 0; y < vsapi->getFrameHeight(dst, 1); y++) { + for (int x = 0; x < vsapi->getFrameWidth(dst, 1); x++) { + dstpU[x] = buffer[x * 2 + 0]; + dstpV[x] = buffer[x * 2 + 1]; + } + + buffer += bufStride; + dstpU += uvStride; + dstpV += uvStride; + } + } + else if (d->vi->format->id == pfRGB24) { + const int bgrStride = d->vi->width * 4; + const int dstStride = vsapi->getStride(dst, 0); + const uint8_t *bgrp = d->buffer8.get(); + uint8_t *VS_RESTRICT dstpR = vsapi->getWritePtr(dst, 0); + uint8_t *VS_RESTRICT dstpG = vsapi->getWritePtr(dst, 1); + uint8_t *VS_RESTRICT dstpB = vsapi->getWritePtr(dst, 2); + + bgrp += bgrStride * (d->vi->height - 1); + + for (int y = 0; y < d->vi->height; y++) { + for (int x = 0; x < d->vi->width; x++) { + dstpB[x] = bgrp[x * 4 + 0]; + dstpG[x] = bgrp[x * 4 + 1]; + dstpR[x] = bgrp[x * 4 + 2]; + } + + bgrp -= bgrStride; + dstpR += dstStride; + dstpG += dstStride; + dstpB += dstStride; + } + } + + vsapi->freeFrame(src); + vsapi->freeFrame(bgr); + return dst; + } + + return nullptr; + } + + static void VS_CC vsfilterFree(void *instanceData, VSCore *core, const VSAPI *vsapi) { + VSFilterData *d = static_cast(instanceData); + vsapi->freeNode(d->node); + delete d; + } + + static void VS_CC vsfilterCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) { + std::unique_ptr d = std::make_unique(); + const std::string filterName{ static_cast(userData) }; + int err{}; + + d->node = vsapi->propGetNode(in, "clip", 0, nullptr); + d->vi = vsapi->getVideoInfo(d->node); + + try { + if (!isConstantFormat(d->vi) || + (d->vi->format->id != pfYUV420P8 && d->vi->format->id != pfYUV420P16 && d->vi->format->id != pfRGB24)) + throw std::string{ "only constant format YUV420P8, YUV420P16, and RGB24 input supported" }; + + const char *_file = vsapi->propGetData(in, "file", 0, nullptr); + const int size = MultiByteToWideChar(CP_UTF8, 0, _file, -1, nullptr, 0); + std::unique_ptr file = std::make_unique(size); + MultiByteToWideChar(CP_UTF8, 0, _file, -1, file.get(), size); + + int charset = int64ToIntS(vsapi->propGetInt(in, "charset", 0, &err)); + if (err) + charset = DEFAULT_CHARSET; + + float fps = static_cast(vsapi->propGetFloat(in, "fps", 0, &err)); + if (err) + fps = -1.0f; + d->fps = (fps > 0.0f || !d->vi->fpsNum) ? fps : static_cast(d->vi->fpsNum) / d->vi->fpsDen; + + const char *vfr = vsapi->propGetData(in, "vfr", 0, &err); + if (!err) + d->vfr = GetVFRTranslator(vfr); + + if (!d->vi->fpsNum && fps <= 0.0f && !d->vfr) + throw std::string{ "variable framerate clip must have fps or vfr specified" }; + + if (filterName == "TextSub") + d->textsub = std::make_unique(file.get(), charset, fps, &err); + else + d->vobsub = std::make_unique(file.get(), &err); + if (err) + throw std::string{ "can't open " } + _file; + + if (d->vi->format->id == pfYUV420P16) + d->buffer16 = std::make_unique(d->vi->width * (d->vi->height + d->vi->height / 2)); + else if (d->vi->format->id == pfRGB24) + d->buffer8 = std::make_unique(d->vi->width * d->vi->height * 4); + } + catch (const std::string &error) { + vsapi->setError(out, (filterName + ": " + error).c_str()); + vsapi->freeNode(d->node); + return; + } + + vsapi->createFilter(in, out, static_cast(userData), vsfilterInit, vsfilterGetFrame, vsfilterFree, fmParallelRequests, 0, d.release(), core); + } + + ////////////////////////////////////////// + // Init + + VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) { + configFunc("com.xy.vsfilter", "xyvsf", "xy-VSFilter", VAPOURSYNTH_API_VERSION, 1, plugin); + + registerFunc("TextSub", + "clip:clip;" + "file:data;" + "charset:int:opt;" + "fps:float:opt;" + "vfr:data:opt;", + vsfilterCreate, const_cast("TextSub"), plugin); + + registerFunc("VobSub", + "clip:clip;" + "file:data;", + vsfilterCreate, const_cast("VobSub"), plugin); + } + } + } UINT_PTR CALLBACK OpenHookProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)