summaryrefslogtreecommitdiffstats
path: root/video/filter
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-10-12 01:31:20 +0200
committerwm4 <wm4@nowhere>2014-10-12 01:33:10 +0200
commit3093d93e1f0baadf4c1ec866adacb244d10fabbe (patch)
tree7a9d90f0eabaa0388636d874dde8ac2a09498967 /video/filter
parent1b4f51ae73a41a6c8b335d76b3dab3814cebaaa8 (diff)
downloadmpv-3093d93e1f0baadf4c1ec866adacb244d10fabbe.tar.bz2
mpv-3093d93e1f0baadf4c1ec866adacb244d10fabbe.tar.xz
vf_vapoursynth: add standalone Lua scripting
Diffstat (limited to 'video/filter')
-rw-r--r--video/filter/vf.c6
-rw-r--r--video/filter/vf_vapoursynth.c257
2 files changed, 262 insertions, 1 deletions
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 <libavutil/rational.h>
+#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 <VSScript.h>
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 <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#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