Last active
December 2, 2019 08:02
-
-
Save stuxcrystal/f6a1722acb12d8ce6bef51b0ceb42d11 to your computer and use it in GitHub Desktop.
I noticed writing plugins in c++ is way too boilerplaty...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* C++ corutine based Vapoursynth-Filter-API | |
* Copyright (C) 2019 StuxCrystal | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program 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 General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |
*/ | |
#ifndef _VSXA_HPP_ | |
#define _VSXA_HPP_ | |
#include "VapourSynth.h" | |
#include "VSHelper.h" | |
#include <functional> | |
#include <stdexcept> | |
#include <variant> | |
#include <memory> | |
namespace vsxa | |
{ | |
template<typename T> | |
class bounded { | |
size_t size; | |
T* data; | |
public: | |
bounded() : data(nullptr), size(0) {} | |
bounded(T* d, size_t s) : data(d), size(s) {} | |
bounded(size_t s) : data(new (std::nothrow) T[s]), size(s) {}; | |
bounded(T&& d) : data(new (std::nothrow) T(std::move(d))), size(1) {}; | |
bounded(const T&& d) : data(new (std::nothrow) T(d)), size(1) {}; | |
bounded(bounded<T>&& b) : size(b.size), data(b.detach()) {}; | |
bounded(const bounded<T>&& b) : bounded(b.length()) { | |
T* ary = b; | |
if (ary == nullptr) { | |
delete[] this->data; | |
this->data = nullptr; | |
return; | |
} | |
std::copy(b.begin(), b.end(), this->begin()); | |
} | |
bounded(std::initializer_list<T> values) : size(values.size()) { | |
std::move(values.begin(), values.end(), this->begin()); | |
} | |
bounded<T> operator=(const bounded<T>&& other) { | |
this->destroy(); | |
size = other.length(); | |
if (size == 0) return; | |
data = new (std::nothrow) T[size]; | |
std::copy(other.begin(), other.end(), this->begin()); | |
} | |
inline operator T* () { | |
return data; | |
} | |
inline T& operator[](size_t n) { | |
if (n >= length()) throw std::out_of_range("Specified index out of range."); | |
return data[n]; | |
} | |
inline T* get(size_t n) noexcept { | |
if (n >= length()) return nullptr; | |
return &data[n]; | |
} | |
inline size_t length() { | |
return data == nullptr ? 0 : size; | |
} | |
inline T* detach() { | |
T* data = this->data; | |
this->data = nullptr; | |
return data; | |
} | |
inline void destroy() { | |
if (this->data != nullptr) { | |
for (T* obj = begin(); obj != end(); obj++) { | |
obj->~T(); | |
} | |
delete this->data; | |
} | |
this->data = nullptr; | |
this->size = 0; | |
} | |
inline T* begin() { | |
return this->data; | |
} | |
inline T* end() { | |
return &this->data[length()]; | |
} | |
~bounded() { | |
this->destroy(); | |
} | |
}; | |
template<typename T, typename... Args> | |
bounded<T> make_bounded(Args... v) { | |
bounded<T> result = bounded<T>(T(std::forward<Args>(v)...)); | |
return result; | |
} | |
class callctx | |
{ | |
VSCore* core; | |
const VSAPI* vsapi; | |
public: | |
callctx() : core(nullptr), vsapi(nullptr) {} | |
callctx(VSCore* c, const VSAPI* a) : core(c), vsapi(a) {} | |
const VSAPI* operator->() { return vsapi; } | |
operator VSCore* () { return core; } | |
}; | |
template<typename F> | |
class filter | |
{ | |
public: | |
virtual const char* name() = 0; | |
virtual VSFilterMode mode() { return fmParallel; }; | |
virtual VSNodeFlags flags() { return (VSNodeFlags) 0; }; | |
virtual const VSVideoInfo* info() = 0; | |
virtual F* init(int n, VSFrameContext* fctx, callctx cctx) = 0; | |
virtual VSFrameRef* advance(F* state, VSActivationReason r) = 0; | |
virtual void finish(F* state) = 0; | |
virtual ~filter() = 0; | |
}; | |
namespace interop | |
{ | |
template<typename F> | |
struct VSXAFilterInstanceData { | |
std::shared_ptr<bounded<F>> ary; | |
size_t position; | |
inline F* get() { | |
return this->ary->get(position); | |
} | |
}; | |
template<typename T, typename F> | |
using VSXAFilterFunction = std::function<bounded<F>(const VSMap* in, VSMap* out, T userData, callctx ctx)>; | |
template<typename T, typename F> | |
struct VSXAFunctionData { | |
VSXAFilterFunction<T, F> func; | |
const char* name; | |
T data; | |
}; | |
template<typename F> | |
static void VS_CC vsxaInit(VSMap* in, VSMap* out, void** instanceData, VSNode* node, VSCore* core, const VSAPI* vsapi) noexcept { | |
F* d = static_cast<VSXAFilterInstanceData<F>*>(*instanceData)->get(); | |
VSVideoInfo vi; | |
d->info(vi); | |
vsapi->setVideoInfo(&vi, 1, node); | |
} | |
template<typename F> | |
static const VSFrameRef* VS_CC vsxaGetFrame(int n, int activationReason, void** instanceData, void** frameData, VSFrameContext* frameCtx, VSCore* core, const VSAPI* vsapi) noexcept { | |
F* d = static_cast<VSXAFilterInstanceData<F>*>(*instanceData)->get(); | |
using request_data = typename F::request_data; | |
request_data* value = nullptr; | |
const VSFrameRef* ref; | |
try { | |
if (activationReason == arInitial) { | |
value = d->init(n, frameCtx, callctx(core, vsapi)); | |
*frameData = value; | |
} | |
else { | |
value = static_cast<typename F::request_data*>(*frameData); | |
} | |
ref = d->advance(value, static_cast<VSActivationReason>(activationReason), frameCtx); | |
if (ref != nullptr) { | |
d->finish(value, frameCtx); | |
} | |
} catch (std::exception &exc) { | |
vsapi->setFilterError(exc.what(), frameCtx); | |
if (value != nullptr) | |
d->finish(value, frameCtx); | |
return nullptr; | |
} catch (...) { | |
vsapi->setFilterError("An unknown exception occured.", frameCtx); | |
if (value != nullptr) | |
d->finish(value, frameCtx); | |
return nullptr; | |
} | |
return ref; | |
} | |
template<typename F> | |
static void VS_CC vsxaFree(void* instanceData, VSCore* core, const VSAPI* vsapi) noexcept { | |
VSXAFilterInstanceData<F>* d = (VSXAFilterInstanceData<F>*)instanceData; | |
delete d; | |
} | |
template<typename T, typename F> | |
static void VS_CC vsxaCreate(const VSMap* in, VSMap* out, void* userData, VSCore* core, const VSAPI* vsapi) noexcept { | |
VSXAFunctionData<T, F>* funcData = static_cast<VSXAFunctionData<T, F>*>(userData); | |
callctx ctx{ core, vsapi }; | |
std::shared_ptr<bounded<F>> result; | |
try { | |
result = std::make_shared<bounded<F>>(std::move(funcData->func(in, out, funcData->data, ctx))); | |
} | |
catch (std::exception & exc) { | |
vsapi->setError(out, exc.what()); | |
return; | |
} | |
catch (...) { | |
vsapi->setError(out, "Unknown exception occured."); | |
return; | |
} | |
for (size_t i = 0; i < result->length(); i++) { | |
F* filter = result->get(i); | |
vsapi->createFilter(in, out, funcData->name, vsxaInit<F>, vsxaGetFrame<F>, vsxaFree<F>, filter->mode(), filter->flags(), new VSXAFilterInstanceData<F>{ result, i }, core); | |
} | |
} | |
template<typename T, typename F> | |
static bounded<F> makeFilterByConstructor(const VSMap* in, VSMap* out, T userData, callctx ctx) { | |
return make_bounded<F>(in, out, userData, ctx); | |
} | |
template<typename T, typename F> | |
inline void vsxaRegister(VSRegisterFunction reg, VSPlugin* plugin, const char* name, const char* args, T data, VSXAFilterFunction<T, F> func) { | |
reg(name, args, vsxaCreate<T, F>, new VSXAFunctionData<T, F>{func, name, std::move(data) }, plugin); | |
} | |
template<typename T, typename F> | |
inline void vsxaRegister(VSRegisterFunction reg, VSPlugin* plugin, const char* name, const char* args, T data) { | |
vsxaRegister<T, F>(reg, plugin, name, args, data, makeFilterByConstructor<T, F>); | |
} | |
template<typename F> | |
inline void vsxaRegister(VSRegisterFunction reg, VSPlugin* plugin, const char* name, const char* args, VSXAFilterFunction<std::monostate, F> func) { | |
vsxaRegister<std::monostate, F>(reg, plugin, name, args, std::monostate(), func); | |
} | |
template<typename F> | |
inline void vsxaRegister(VSRegisterFunction reg, VSPlugin* plugin, const char* name, const char* args) { | |
vsxaRegister<std::monostate, F>(reg, plugin, name, args, std::monostate(), makeFilterByConstructor<std::monostate, F>); | |
} | |
} | |
} | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <memory> | |
#include "VapourSynth.h" | |
#include "VSHelper.h" | |
#include "vsxa.hpp" | |
#include "vsxacoro.hpp" | |
#include "vsxautils.hpp" | |
using namespace vsxa; | |
class generic_error : public std::exception { | |
public: | |
generic_error(const char* msg) : std::exception(msg) {}; | |
}; | |
bounded<clip<async>> PropToClip(const VSMap* in, VSMap* out, std::monostate unused, vsxa::callctx ctx) { | |
VSNodeRef* clip = ctx->propGetNode(in, "clip", 0, 0); | |
VSVideoInfo vi = *ctx->getVideoInfo(clip); | |
vi.format = ctx->registerFormat(cmGray, stFloat, 32, 0, 0, ctx); | |
vi.width = 1; | |
vi.height = 1; | |
std::string propName = ctx->propGetData(in, "prop", 0, 0); | |
float def = 0; | |
if (ctx->propGetType(in, "default") == ptFloat) | |
def = static_cast<float>(ctx->propGetFloat(in, "prop", 0, 0)); | |
return vsxa::clip<async>("PropToClip", vi, async([=](int n) -> async::renderer* { | |
vsxa::callctx ctx = co_await get_callctx(); | |
vsxa::frame frame = co_await get_frame(n, clip); | |
const VSMap* props = ctx->getFramePropsRO(frame); | |
VSFrameRef* newFrame = ctx->newVideoFrame(vi.format, 1, 1, frame, ctx); | |
float* data = reinterpret_cast<float*>(ctx->getWritePtr(newFrame, 0)); | |
switch (ctx->propGetType(props, propName.c_str())) { | |
case ptInt: | |
data[0] = (float)ctx->propGetInt(props, propName.c_str(), 0, 0); | |
break; | |
case ptFloat: | |
data[0] = (float)ctx->propGetFloat(props, propName.c_str(), 0, 0); | |
break; | |
default: | |
data[0] = def; | |
break; | |
} | |
co_return newFrame; | |
})); | |
} | |
bounded<clip<async>> ImportProp(const VSMap* in, VSMap* out, std::monostate unused, vsxa::callctx ctx) { | |
VSNodeRef* dst = ctx->propGetNode(in, "clip", 0, 0); | |
VSNodeRef* src = ctx->propGetNode(in, "src", 0, 0); | |
std::string dstPropName = ctx->propGetData(in, "dstprop", 0, 0); | |
std::string srcPropName = ctx->propGetData(in, "srcprop", 0, 0); | |
return vsxa::clip<async>("ImportProp", ctx->getVideoInfo(dst), async([=](int n) -> async::renderer* { | |
vsxa::callctx ctx = co_await get_callctx(); | |
bounded<vsxa::frame> frames = co_await (get_frame(n, src) + get_frame(n, dst)); | |
const VSMap* srcProps = ctx->getFramePropsRO(frames[0]); | |
VSPropTypes type = static_cast<VSPropTypes>(ctx->propGetType(srcProps, srcPropName.c_str())); | |
if (type == ptUnset) | |
return frames[1]; | |
VSFrameRef* newFrame = ctx->copyFrame(frames[1], ctx); | |
VSMap* dstProps = ctx->getFramePropsRW(newFrame); | |
if (ctx->propGetType(dstProps, dstPropName.c_str()) != ptUnset) | |
ctx->propDeleteKey(dstProps, dstPropName.c_str()); | |
auto size = ctx->propNumElements(srcProps, srcPropName.c_str()); | |
switch (type) { | |
case ptFloat: | |
ctx->propSetFloatArray(dstProps, dstPropName.c_str(), ctx->propGetFloatArray(srcProps, srcPropName.c_str(), 0), size); | |
break; | |
case ptInt: | |
ctx->propSetIntArray(dstProps, dstPropName.c_str(), ctx->propGetIntArray(srcProps, srcPropName.c_str(), 0), size); | |
break; | |
case ptData: | |
for (int i = 0; i < size; i++) | |
ctx->propSetData(dstProps, dstPropName.c_str(), ctx->propGetData(srcProps, srcPropName.c_str(), i, 0), ctx->propGetDataSize(srcProps, srcPropName.c_str(), i, 0), paAppend); | |
break; | |
case ptFunction: | |
for (int i = 0; i < size; i++) | |
ctx->propSetFunc(dstProps, dstPropName.c_str(), ctx->propGetFunc(srcProps, srcPropName.c_str(), i, 0), paAppend); | |
break; | |
case ptFrame: | |
for (int i = 0; i < size; i++) | |
ctx->propSetFrame(dstProps, dstPropName.c_str(), ctx->propGetFrame(srcProps, srcPropName.c_str(), i, 0), paAppend); | |
break; | |
case ptNode: | |
for (int i = 0; i < size; i++) | |
ctx->propSetNode(dstProps, dstPropName.c_str(), ctx->propGetNode(srcProps, srcPropName.c_str(), i, 0), paAppend); | |
break; | |
default: | |
throw generic_error("Unknown prop type detected. Cannot copy prop."); | |
} | |
return newFrame; | |
})); | |
} | |
VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin* plugin) { | |
configFunc("moe.encode.proputils", "proputils", "Prop Utils", VAPOURSYNTH_API_VERSION, 1, plugin); | |
vsxa::interop::vsxaRegister<clip<async>>(registerFunc, plugin, "PropToClip", "clip:clip;prop:data;default:float:opt;", PropToClip); | |
vsxa::interop::vsxaRegister<clip<async>>(registerFunc, plugin, "ImportProp", "clip:clip;src:clip;dstprop:data;srcprop:data;", ImportProp); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _VSXA_CORO_HPP_ | |
#define _VSXA_CORO_HPP_ | |
#include "vsxa.hpp" | |
#include "vsxautils.hpp" | |
#include <functional> | |
#include <variant> | |
#include <experimental/coroutine> | |
#define coroutines std::experimental | |
namespace vsxa | |
{ | |
class _request_failed : public std::exception {}; | |
struct get_frames { | |
struct get_frame { | |
int n; | |
VSNodeRef* node; | |
public: | |
get_frame() : n(0), node(nullptr) {} | |
get_frame(int n, VSNodeRef* node) : n(n), node(node) {} | |
get_frames operator+(get_frame other) { | |
return get_frames(get_frame(n, node), other); | |
} | |
}; | |
bounded<get_frame> f; | |
get_frames(get_frame a, get_frame b) : f(bounded<get_frame>(2)) { | |
f[0] = std::move(a); | |
f[1] = std::move(b); | |
} | |
get_frames(get_frames a, get_frame b) : f(bounded<get_frame>(a.f.length() + 1)) { | |
std::copy(a.f.begin(), a.f.end(), f.begin()); | |
f[a.f.length()] = std::move(b); | |
} | |
get_frames(bounded<get_frame>&& frames) : f(std::move(frames)) {}; | |
get_frames(get_frames& mv) : f(std::move(mv.f)) {}; | |
get_frames(const get_frames&& cp) : f(cp.f) {}; | |
get_frames operator+(get_frame& other) { | |
return get_frames(get_frames(std::move(f)), other); | |
} | |
}; | |
using get_frame = get_frames::get_frame; | |
class get_callctx {}; | |
class get_frame_context {}; | |
struct async | |
{ | |
using _promise_result = std::variant<std::monostate, const VSFrameRef*, std::exception_ptr>; | |
class renderer { | |
coroutines::coroutine_handle<> handle; | |
_promise_result* result; | |
public: | |
callctx ctx; | |
VSFrameContext* currentContext = nullptr; | |
VSActivationReason currentAr = arError; | |
public: | |
renderer(coroutines::coroutine_handle<> handle, _promise_result* result) : handle(handle), result(result) {} | |
const VSFrameRef* resume(VSActivationReason ar, VSFrameContext* ctx) { | |
currentContext = ctx; | |
currentAr = ar; | |
if (ar == arAllFramesReady || ar == arError || ar == arInitial) | |
handle.resume(); | |
else | |
return nullptr; | |
switch (result->index()) { | |
case 1: | |
return std::get<const VSFrameRef*>(*result); | |
case 0: | |
break; | |
case 2: | |
try { | |
std::rethrow_exception(std::get<std::exception_ptr>(*result)); | |
} | |
catch (_request_failed &) { | |
} | |
catch (std::exception & e) { | |
this->ctx->setFilterError(e.what(), ctx); | |
} | |
catch (...) { | |
this->ctx->setFilterError("A unknown exception occured.", ctx); | |
} | |
break; | |
default: | |
this->ctx->setFilterError("This should not happen.", ctx); | |
} | |
return nullptr; | |
} | |
~renderer() { | |
handle.destroy(); | |
} | |
}; | |
struct promise { | |
template<typename T> | |
class _value_awaiter { | |
T value; | |
public: | |
_value_awaiter(T v) : value(v) {} | |
bool await_ready() { return true; } | |
void await_suspend(coroutines::coroutine_handle<vsxa::async::promise> coro) {} | |
T await_resume() { | |
return value; | |
} | |
}; | |
_promise_result result = std::monostate{}; | |
renderer* r = new renderer{ coroutines::coroutine_handle<promise>::from_promise(this), &result }; | |
renderer* get_renderer() { | |
return r; | |
}; | |
renderer* get_return_object() { | |
return r; | |
} | |
coroutines::suspend_always initial_suspend() { return {}; }; | |
coroutines::suspend_always final_suspend() { return {}; }; | |
void return_value(const VSFrameRef* ref) { | |
result.emplace<const VSFrameRef*>(ref); | |
} | |
void unhandled_exception() { | |
result.emplace<std::exception_ptr>(std::move(std::current_exception())); | |
} | |
template<typename U> | |
coroutines::suspend_always yield_value(U& v) = delete; | |
auto await_transform(get_callctx ctx) { | |
return _value_awaiter<callctx>(r->ctx); | |
} | |
auto await_transform(get_frame_context ctx) { | |
return _value_awaiter<VSFrameContext*>(r->currentContext); | |
} | |
auto await_transform(get_frame frame) { | |
class _awaiter | |
{ | |
promise* p; | |
get_frame f; | |
public: | |
_awaiter(promise* p, get_frame f) : p(p), f(f) {} | |
bool await_ready() { return false; } | |
void await_suspend(coroutines::coroutine_handle<vsxa::async::promise> coro) { | |
p->r->ctx->requestFrameFilter(f.n, f.node, p->r->currentContext); | |
} | |
vsxa::frame await_resume() { | |
if (p->r->currentAr == arAllFramesReady) { | |
return vsxa::frame(p->r->ctx->getFrameFilter(f.n, f.node, p->r->currentContext), p->r->ctx); | |
} else { | |
throw _request_failed{}; | |
} | |
} | |
}; | |
return _awaiter(this, frame); | |
} | |
auto await_transform(get_frames frames) { | |
class _awaiter | |
{ | |
promise* p; | |
get_frames f; | |
public: | |
_awaiter(promise* p, get_frames f) : p(p), f(f) {} | |
bool await_ready() { return false; } | |
void await_suspend(coroutines::coroutine_handle<vsxa::async::promise> coro) { | |
for (get_frame* current = f.f.begin(); current != f.f.end(); current++) | |
p->r->ctx->requestFrameFilter(current->n, current->node, p->r->currentContext); | |
} | |
bounded<frame> await_resume() { | |
if (p->r->currentAr == arAllFramesReady) { | |
bounded<frame> result = bounded<frame>(f.f.length()); | |
for (size_t i = 0; i < result.length(); i++) { | |
get_frame* current = f.f.get(i); | |
result[i] = frame(p->r->ctx->getFrameFilter(current->n, current->node, p->r->currentContext), p->r->ctx); | |
} | |
return result; | |
} | |
else { | |
throw _request_failed{}; | |
} | |
} | |
}; | |
return _awaiter(this, frames); | |
} | |
}; | |
using renderfunc = std::function<renderer*(int n)>; | |
private: | |
renderfunc func; | |
public: | |
async(renderfunc func) : func(func) {} | |
const char* name() { return "_async_"; }; | |
VSFilterMode mode() { return fmParallel; }; | |
VSNodeFlags flags() { return static_cast<VSNodeFlags>(0); }; | |
void info(VSVideoInfo& vi) {}; | |
using request_data = renderer; | |
renderer* init(int n, VSFrameContext* fctx, callctx cctx) { | |
renderer* render = func(n); | |
render->ctx = cctx; | |
return render; | |
}; | |
const VSFrameRef* advance(renderer* state, VSActivationReason r, VSFrameContext* fctx) { | |
return state->resume(r, fctx); | |
}; | |
void finish(renderer* state, VSFrameContext* fctx) { | |
delete state; | |
}; | |
}; | |
} | |
template<> | |
struct coroutines::coroutine_traits<vsxa::async::renderer*, int> { | |
using promise_type = vsxa::async::promise; | |
}; | |
template<typename T> | |
struct coroutines::coroutine_traits<vsxa::async::renderer*, T, int> { | |
using promise_type = vsxa::async::promise; | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _VSXA_FUNCTIONAL_HPP_ | |
#define _VSXA_FUNCTIONAL_HPP_ | |
#include "vsxa.hpp" | |
#include "VapourSynth.h" | |
#include <functional> | |
namespace vsxa | |
{ | |
class frame | |
{ | |
const VSFrameRef* ref; | |
callctx cctx; | |
public: | |
frame() : ref(nullptr) {}; | |
frame(const VSFrameRef* ref, callctx cctx) : ref(ref), cctx(cctx) {} | |
frame(frame&& orig) noexcept : ref(orig.detach()), cctx(orig.cctx) {} | |
frame(const frame&& frame) noexcept : cctx(frame.cctx) { | |
if (frame.ref == nullptr) | |
ref = nullptr; | |
else | |
ref = cctx->cloneFrameRef(frame.ref); | |
} | |
frame& operator=(frame&& src) noexcept { | |
_destroy(); | |
ref = src.ref; | |
cctx = src.cctx; | |
} | |
operator const VSFrameRef* () { return ref; } | |
const VSFrameRef* detach() noexcept { const VSFrameRef* r = ref; ref = nullptr; return r; } | |
~frame() { _destroy(); } | |
private: | |
void _destroy() { if (ref != nullptr) cctx->freeFrame(ref); } | |
}; | |
template<typename T> | |
class clip | |
{ | |
const char* _name; | |
VSFilterMode _mode; | |
VSNodeFlags _flags; | |
VSVideoInfo _vi; | |
T filter; | |
public: | |
clip(const char* name, VSFilterMode mode, VSNodeFlags flags, VSVideoInfo vi, T filter) : _name(name), _mode(mode), _flags(flags), _vi(vi), filter(std::move(filter)) {}; | |
clip(const char* name, VSFilterMode mode, VSVideoInfo vi, T filter) : _name(name), _mode(mode), _flags((VSNodeFlags)0), _vi(vi), filter(std::move(filter)) {}; | |
clip(const char* name, VSVideoInfo vi, T filter) : _name(name), _mode(fmParallel), _flags((VSNodeFlags)0), _vi(vi), filter(std::move(filter)) {}; | |
clip(const char* name, VSFilterMode mode, VSNodeFlags flags, const VSVideoInfo* vi, T filter) : _name(name), _mode(mode), _flags(flags), _vi(*vi), filter(std::move(filter)) {}; | |
clip(const char* name, VSFilterMode mode, const VSVideoInfo* vi, T filter) : _name(name), _mode(mode), _flags((VSNodeFlags)0), _vi(*vi), filter(std::move(filter)) {}; | |
clip(const char* name, const VSVideoInfo* vi, T filter) : _name(name), _mode(fmParallel), _flags((VSNodeFlags)0), _vi(*vi), filter(std::move(filter)) {}; | |
const char* name() { return _name; }; | |
VSFilterMode mode() { return _mode; }; | |
VSNodeFlags flags() { return _flags; }; | |
void info(VSVideoInfo& vi) { | |
vi = _vi; | |
}; | |
using request_data = typename T::request_data; | |
request_data* init(int n, VSFrameContext* fctx, callctx cctx) { | |
return filter.init(n, fctx, cctx); | |
}; | |
const VSFrameRef* advance(request_data* state, VSActivationReason r, VSFrameContext* fctx) { | |
return filter.advance(state, r, fctx); | |
}; | |
void finish(request_data* state, VSFrameContext* fctx) { | |
filter.finish(state, fctx); | |
}; | |
}; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment