summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/vf.rst25
-rwxr-xr-xold-configure23
-rw-r--r--old-makefile2
-rw-r--r--video/filter/vf.c6
-rw-r--r--video/filter/vf_vapoursynth.c257
-rw-r--r--wscript30
-rw-r--r--wscript_build.py2
7 files changed, 326 insertions, 19 deletions
diff --git a/DOCS/man/vf.rst b/DOCS/man/vf.rst
index b8a258bbd9..6e7418c0c3 100644
--- a/DOCS/man/vf.rst
+++ b/DOCS/man/vf.rst
@@ -910,6 +910,31 @@ Available filters are:
making it higher than the number of cores can actually make it
slower.
+``vapoursynth-lazy``
+ The same as ``vapoursynth``, but doesn't load Python scripts. Instead, a
+ custom backend using Lua and the raw VapourSynth API is used. The syntax
+ is completely different, and absolutely no conveniencve features are
+ provided. There's no type checking either, and you can trigger crashes.
+
+ .. admonition:: Example:
+
+ ::
+
+ video_out = invoke("morpho", "Open", {clip = video_in})
+
+ The special variable ``video_in`` is the mpv video source, while the
+ special variable ``video_out`` is used to read video from. The 1st argument
+ is the plugin (queried with ``getPluginByNs``), the 2nd is the filter name,
+ and the 3rd argument is a table with the arguments. Positional arguments
+ are not supported. The types must match exactly. Since Lua is terrible and
+ can't distinguish integers and floats, integer arguments must be prefixed
+ with ``i_``, in which case the prefix is removed and the argument is cast
+ to an integer. Should the argument's name start with ``i_``, you're out of
+ luck.
+
+ Clips (VSNodeRef) are passed as light userdata, so trying to pass any
+ other userdata type will result in hard crashes.
+
``vavpp``
VA-AP-API video post processing. Works with ``--vo=vaapi`` and ``--vo=opengl``
only. Currently deinterlaces. This filter is automatically inserted if
diff --git a/old-configure b/old-configure
index bd31bfc1c1..cf2c4c9cde 100755
--- a/old-configure
+++ b/old-configure
@@ -186,7 +186,6 @@ options_state_machine() {
opt_yes_no _libpostproc "postprocess filter (vf_pp)"
opt_yes_no _libavdevice "libavdevice demuxers"
opt_yes_no _libavfilter "libavfilter"
- opt_yes_no _vapoursynth "VapourSynth filter bridge"
opt_yes_no _jpeg "support for writing JPEG screenshots"
opt_yes_no _libcdio "libcdio support"
opt_yes_no _ffmpeg "skip FFmpeg/Libav autodetection"
@@ -215,6 +214,8 @@ options_state_machine() {
opt_yes_no _openal "OpenAL audio output"
opt_yes_no _shm "X11/Xv shared memory"
opt_yes_no _lua "Lua scripting"
+ opt_yes_no _vapoursynth "VapourSynth filter bridge (Python)"
+ opt_yes_no _vapoursynth_lazy "VapourSynth filter bridge (Lua)"
opt_yes_no _encoding "encoding functionality" yes
opt_yes_no _build_man "building manpage"
}
@@ -759,8 +760,6 @@ check_pkg_config "libbs2b audio filter support" $_libbs2b LIBBS2B 'libbs2b'
check_pkg_config "LCMS2 support" $_lcms2 LCMS2 'lcms2 >= 2.6'
-check_pkg_config "VapourSynth support" $_vapoursynth VAPOURSYNTH 'vapoursynth >= 23 vapoursynth-script >= 23'
-
check_pkg_config "FFmpeg/Libav" $_ffmpeg FFMPEG \
"libavutil >= 52.48.101 libavcodec >= 55.34.1 libavformat >= 55.12.0 libswscale >= 2.1.2"
test $(defretval) = no && die "Unable to find development files for some of the required Libav libraries above. Aborting."
@@ -883,6 +882,24 @@ test_lua "lua5.2 >= 5.2.0" # debian
test "$_lua" != yes && check_yes_no no LUA
+if ! ( $_pkg_config 'vapoursynth >= 23' ) ; then
+ _vapoursynth=no
+ _vapoursynth_lazy=no
+fi
+check_pkg_config "VapourSynth support (Python)" $_vapoursynth VAPOURSYNTH 'vapoursynth >= 23 vapoursynth-script >= 23'
+_vapoursynth=$(defretval)
+if test "$_lua" = no ; then
+ _vapoursynth_lazy=no
+fi
+check_pkg_config "VapourSynth support (Lua)" $_vapoursynth_lazy VAPOURSYNTH_LAZY 'vapoursynth >= 23'
+_vapoursynth_lazy=$(defretval)
+
+_vapoursynth_core=yes
+if test "$_vapoursynth" = no && test "$_vapoursynth_lazy" = no ; then
+ _vapoursynth_core=no
+fi
+check_trivial "VapourSynth core" $_vapoursynth_core VAPOURSYNTH_CORE
+
check_trivial "joystick" $_joystick JOYSTICK
check_statement_libs "lirc" $_lirc LIRC lirc/lirc_client.h "" -llirc_client
diff --git a/old-makefile b/old-makefile
index d0b7d678e6..519da884f1 100644
--- a/old-makefile
+++ b/old-makefile
@@ -105,7 +105,7 @@ SOURCES-$(LIBAVFILTER) += video/filter/vf_lavfi.c \
audio/filter/af_lavfi.c
SOURCES-$(LUA) += player/lua.c
-SOURCES-$(VAPOURSYNTH) += video/filter/vf_vapoursynth.c
+SOURCES-$(VAPOURSYNTH_CORE) += video/filter/vf_vapoursynth.c
SOURCES-$(DLOPEN) += video/filter/vf_dlopen.c
SOURCES = audio/audio.c \
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
diff --git a/wscript b/wscript
index da056d9b51..881333f675 100644
--- a/wscript
+++ b/wscript
@@ -244,6 +244,10 @@ iconv support use --disable-iconv.",
'deps_any': [ 'libquvi4', 'libquvi9' ],
'func': check_true
}, {
+ 'name' : '--lua',
+ 'desc' : 'Lua',
+ 'func': check_lua,
+ }, {
'name': '--libass',
'desc': 'SSA/ASS support',
'func': check_pkg_config('libass'),
@@ -319,9 +323,19 @@ If you really mean to compile without libass support use --disable-libass."
'desc': 'LCMS2 support',
'func': check_pkg_config('lcms2', '>= 2.6'),
}, {
+ 'name': 'vapoursynth-core',
+ 'desc': 'VapourSynth filter bridge (core)',
+ 'func': check_pkg_config('vapoursynth >= 23'),
+ }, {
'name': '--vapoursynth',
- 'desc': 'VapourSynth filter bridge',
- 'func': check_pkg_config('vapoursynth >= 23 vapoursynth-script >= 23'),
+ 'desc': 'VapourSynth filter bridge (Python)',
+ 'deps': ['vapoursynth-core'],
+ 'func': check_pkg_config('vapoursynth-script >= 23'),
+ }, {
+ 'name': '--vapoursynth-lazy',
+ 'desc': 'VapourSynth filter bridge (Lazy Lua)',
+ 'deps': ['vapoursynth-core', 'lua'],
+ 'func': check_true,
}
]
@@ -725,14 +739,6 @@ radio_and_tv_features = [
}
]
-scripting_features = [
- {
- 'name' : '--lua',
- 'desc' : 'Lua',
- 'func': check_lua,
- }
-]
-
standalone_features = [
{
'name': '--cplayer',
@@ -796,10 +802,9 @@ def options(opt):
opt.parse_features('video outputs', video_output_features)
opt.parse_features('hwaccels', hwaccel_features)
opt.parse_features('tv features', radio_and_tv_features)
- opt.parse_features('scripting', scripting_features)
opt.parse_features('standalone app', standalone_features)
- group = opt.get_option_group("scripting")
+ group = opt.get_option_group("optional feaures")
group.add_option('--lua',
type = 'string',
dest = 'LUA_VER',
@@ -856,7 +861,6 @@ def configure(ctx):
if ctx.options.LUA_VER:
ctx.options.enable_lua = True
- ctx.parse_dependencies(scripting_features)
ctx.parse_dependencies(standalone_features)
ctx.define('HAVE_SYS_SOUNDCARD_H',
diff --git a/wscript_build.py b/wscript_build.py
index 2f2b4afd02..f7df1c5aad 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -329,7 +329,7 @@ def build(ctx):
( "video/filter/vf_sub.c" ),
( "video/filter/vf_swapuv.c" ),
( "video/filter/vf_unsharp.c" ),
- ( "video/filter/vf_vapoursynth.c", "vapoursynth" ),
+ ( "video/filter/vf_vapoursynth.c", "vapoursynth-core" ),
( "video/filter/vf_vavpp.c", "vaapi-vpp"),
( "video/filter/vf_vdpaupp.c", "vdpau" ),
( "video/filter/vf_yadif.c" ),