From 3093d93e1f0baadf4c1ec866adacb244d10fabbe Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 12 Oct 2014 01:31:20 +0200 Subject: vf_vapoursynth: add standalone Lua scripting --- video/filter/vf.c | 6 +- video/filter/vf_vapoursynth.c | 257 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 1 deletion(-) (limited to 'video/filter') diff --git a/video/filter/vf.c b/video/filter/vf.c index f00e9689c6..4910974d6a 100644 --- a/video/filter/vf.c +++ b/video/filter/vf.c @@ -71,6 +71,7 @@ extern const vf_info_t vf_info_dlopen; extern const vf_info_t vf_info_lavfi; extern const vf_info_t vf_info_vaapi; extern const vf_info_t vf_info_vapoursynth; +extern const vf_info_t vf_info_vapoursynth_lazy; extern const vf_info_t vf_info_vdpaupp; extern const vf_info_t vf_info_buffer; @@ -114,9 +115,12 @@ static const vf_info_t *const filter_list[] = { #if HAVE_DLOPEN &vf_info_dlopen, #endif -#if HAVE_VAPOURSYNTH +#if HAVE_VAPOURSYNTH_CORE && HAVE_VAPOURSYNTH &vf_info_vapoursynth, #endif +#if HAVE_VAPOURSYNTH_CORE && HAVE_VAPOURSYNTH_LAZY + &vf_info_vapoursynth_lazy, +#endif #if HAVE_VAAPI_VPP &vf_info_vaapi, #endif diff --git a/video/filter/vf_vapoursynth.c b/video/filter/vf_vapoursynth.c index f5122df544..1ebf971125 100644 --- a/video/filter/vf_vapoursynth.c +++ b/video/filter/vf_vapoursynth.c @@ -27,6 +27,8 @@ #include +#include "config.h" + #include "common/msg.h" #include "options/m_option.h" @@ -42,7 +44,14 @@ struct vf_priv_s { VSNodeRef *in_node; const struct script_driver *drv; + // drv_vss struct VSScript *se; + // drv_lazy + struct lua_State *ls; + VSNodeRef **gc_noderef; + int num_gc_noderef; + VSMap **gc_map; + int num_gc_map; struct mp_image_params fmt_in; @@ -687,6 +696,8 @@ static const m_option_t vf_opts_fields[] = { {0} }; +#if HAVE_VAPOURSYNTH + #include static int drv_vss_init(struct vf_instance *vf) @@ -763,3 +774,249 @@ const vf_info_t vf_info_vapoursynth = { .priv_size = sizeof(struct vf_priv_s), .options = vf_opts_fields, }; + +#endif + +#if HAVE_VAPOURSYNTH_LAZY + +#include +#include +#include + +#if LUA_VERSION_NUM <= 501 +#define mp_cpcall lua_cpcall +#define FUCKYOUOHGODWHY(L) lua_pushvalue(L, LUA_GLOBALSINDEX) +#else +// Curse whoever had this stupid idea. Curse whoever thought it would be a good +// idea not to include an emulated lua_cpcall() even more. +static int mp_cpcall (lua_State *L, lua_CFunction func, void *ud) +{ + lua_pushcfunction(L, func); // doesn't allocate in 5.2 (but does in 5.1) + lua_pushlightuserdata(L, ud); + return lua_pcall(L, 1, 0, 0); +} +// Hey, let's replace old mechanisms with something slightly different! +#define FUCKYOUOHGODWHY lua_pushglobaltable +#endif + +static int drv_lazy_init(struct vf_instance *vf) +{ + struct vf_priv_s *p = vf->priv; + p->ls = luaL_newstate(); + if (!p->ls) + return -1; + p->vsapi = getVapourSynthAPI(VAPOURSYNTH_API_VERSION); + p->vscore = p->vsapi ? p->vsapi->createCore(0) : NULL; + if (!p->vscore) { + MP_FATAL(vf, "Could not load VapourSynth.\n"); + lua_close(p->ls); + return -1; + } + return 0; +} + +static void drv_lazy_uninit(struct vf_instance *vf) +{ + struct vf_priv_s *p = vf->priv; + lua_close(p->ls); + p->vsapi->freeCore(p->vscore); +} + +static int drv_lazy_load_core(struct vf_instance *vf) +{ + // not needed + return 0; +} + +static struct vf_instance *get_vf(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "p"); // p + struct vf_instance *vf = lua_touserdata(L, -1); // p + lua_pop(L, 1); // - + return vf; +} + +static void vsmap_to_table(lua_State *L, int index, VSMap *map) +{ + struct vf_instance *vf = get_vf(L); + struct vf_priv_s *p = vf->priv; + const VSAPI *vsapi = p->vsapi; + for (int n = 0; n < vsapi->propNumKeys(map); n++) { + const char *key = vsapi->propGetKey(map, n); + VSPropTypes t = vsapi->propGetType(map, key); + switch (t) { + case ptInt: + lua_pushnumber(L, vsapi->propGetInt(map, key, 0, NULL)); + break; + case ptNode: { + VSNodeRef *r = vsapi->propGetNode(map, key, 0, NULL); + MP_TARRAY_APPEND(p, p->gc_noderef, p->num_gc_noderef, r); + lua_pushlightuserdata(L, r); + break; + } + default: + luaL_error(L, "unknown map type"); + } + lua_setfield(L, index, key); + } +} + +static VSMap *table_to_vsmap(lua_State *L, int index) +{ + struct vf_instance *vf = get_vf(L); + struct vf_priv_s *p = vf->priv; + const VSAPI *vsapi = p->vsapi; + assert(index > 0); + VSMap *map = vsapi->createMap(); + MP_TARRAY_APPEND(p, p->gc_map, p->num_gc_map, map); + if (!map) + luaL_error(L, "out of memory"); + lua_pushnil(L); // nil + while (lua_next(L, index) != 0) { // key value + if (lua_type(L, -2) != LUA_TSTRING) { + luaL_error(L, "key must be a string, but got %s", + lua_typename(L, -2)); + } + const char *key = lua_tostring(L, -2); + switch (lua_type(L, -1)) { + case LUA_TNUMBER: { + // gross hack because we hate everything + if (strncmp(key, "i_", 2) == 0) { + vsapi->propSetInt(map, key + 2, lua_tointeger(L, -1), 0); + } else { + vsapi->propSetFloat(map, key, lua_tonumber(L, -1), 0); + } + break; + } + case LUA_TSTRING: { + const char *s = lua_tostring(L, -1); + vsapi->propSetData(map, key, s, strlen(s), 0); + break; + } + case LUA_TLIGHTUSERDATA: { // assume it's VSNodeRef* + VSNodeRef *node = lua_touserdata(L, -1); + vsapi->propSetNode(map, key, node, 0); + break; + } + default: + luaL_error(L, "unknown type"); + break; + } + lua_pop(L, 1); // key + } + return map; +} + +static int l_invoke(lua_State *L) +{ + struct vf_instance *vf = get_vf(L); + struct vf_priv_s *p = vf->priv; + const VSAPI *vsapi = p->vsapi; + + VSPlugin *plugin = vsapi->getPluginByNs(luaL_checkstring(L, 1), p->vscore); + if (!plugin) + luaL_error(L, "plugin not found"); + VSMap *map = table_to_vsmap(L, 3); + VSMap *r = vsapi->invoke(plugin, luaL_checkstring(L, 2), map); + MP_TARRAY_APPEND(p, p->gc_map, p->num_gc_map, r); + if (!r) + luaL_error(L, "?"); + const char *err = vsapi->getError(r); + if (err) + luaL_error(L, "error calling invoke(): %s", err); + int err2 = 0; + VSNodeRef *node = vsapi->propGetNode(r, "clip", 0, &err2); + MP_TARRAY_APPEND(p, p->gc_noderef, p->num_gc_noderef, node); + if (!node) + luaL_error(L, "invoke() didn't return clip (error %d)", err2); + lua_pushlightuserdata(L, node); + return 1; +} + +struct load_ctx { + struct vf_instance *vf; + VSMap *vars; + int status; +}; + +static int load_stuff(lua_State *L) +{ + struct load_ctx *ctx = lua_touserdata(L, -1); + lua_pop(L, 1); // - + struct vf_instance *vf = ctx->vf; + struct vf_priv_s *p = vf->priv; + + // setup stuff; should be idempotent + lua_pushlightuserdata(L, vf); + lua_setfield(L, LUA_REGISTRYINDEX, "p"); // - + lua_pushcfunction(L, l_invoke); + lua_setglobal(L, "invoke"); + + FUCKYOUOHGODWHY(L); + vsmap_to_table(L, lua_gettop(L), ctx->vars); + if (luaL_dofile(L, p->cfg_file)) + lua_error(L); + lua_pop(L, 1); + + lua_getglobal(L, "video_out"); // video_out + if (!lua_islightuserdata(L, -1)) + luaL_error(L, "video_out not set or has wrong type"); + p->out_node = p->vsapi->cloneNodeRef(lua_touserdata(L, -1)); + return 0; +} + +static int drv_lazy_load(struct vf_instance *vf, VSMap *vars) +{ + struct vf_priv_s *p = vf->priv; + struct load_ctx ctx = {vf, vars, 0}; + if (mp_cpcall(p->ls, load_stuff, &ctx)) { + MP_FATAL(vf, "filter creation failed: %s\n", lua_tostring(p->ls, -1)); + lua_pop(p->ls, 1); + ctx.status = -1; + } + assert(lua_gettop(p->ls) == 0); + return ctx.status; +} + +static void drv_lazy_unload(struct vf_instance *vf) +{ + struct vf_priv_s *p = vf->priv; + + for (int n = 0; n < p->num_gc_noderef; n++) { + VSNodeRef *ref = p->gc_noderef[n]; + if (ref) + p->vsapi->freeNode(ref); + } + p->num_gc_noderef = 0; + for (int n = 0; n < p->num_gc_map; n++) { + VSMap *map = p->gc_map[n]; + if (map) + p->vsapi->freeMap(map); + } + p->num_gc_map = 0; +} + +static const struct script_driver drv_lazy = { + .init = drv_lazy_init, + .uninit = drv_lazy_uninit, + .load_core = drv_lazy_load_core, + .load = drv_lazy_load, + .unload = drv_lazy_unload, +}; + +static int vf_open_lazy(vf_instance_t *vf) +{ + struct vf_priv_s *p = vf->priv; + p->drv = &drv_lazy; + return vf_open(vf); +} + +const vf_info_t vf_info_vapoursynth_lazy = { + .description = "VapourSynth bridge (Lua)", + .name = "vapoursynth-lazy", + .open = vf_open_lazy, + .priv_size = sizeof(struct vf_priv_s), + .options = vf_opts_fields, +}; + +#endif -- cgit v1.2.3