summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-01-12 17:37:11 +0100
committerwm4 <wm4@nowhere>2017-01-12 17:45:11 +0100
commit44e06b70d504d5a9073990851a353820370f3667 (patch)
treeffda223303ae592718ae8c2daadfc40b77f37808
parentac98ff71ddf3da7d864f149fc290fa07600b311a (diff)
downloadmpv-44e06b70d504d5a9073990851a353820370f3667.tar.bz2
mpv-44e06b70d504d5a9073990851a353820370f3667.tar.xz
player: add experimental C plugin interface
This basically reuses the scripting infrastructure. Note that this needs to be explicitly enabled at compilation. For one, enabling export for certain symbols from an executable seems to be quite toolchain-specific. It might not work outside of Linux and cause random problems within Linux. If C plugins actually become commonly used and this approach is starting to turn out as a problem, we can build mpv CLI as a wrapper for libmpv, which would remove the requirement that plugins pick up host symbols. I'm being lazy, so implementation/documentation are parked in existing files, even if that stuff doesn't necessarily belong there. Sue me, or better send patches.
-rw-r--r--DOCS/man/libmpv.rst56
-rw-r--r--player/scripting.c34
-rw-r--r--wscript13
-rw-r--r--wscript_build.py8
4 files changed, 110 insertions, 1 deletions
diff --git a/DOCS/man/libmpv.rst b/DOCS/man/libmpv.rst
index d3e78aa5bc..a85b2ed569 100644
--- a/DOCS/man/libmpv.rst
+++ b/DOCS/man/libmpv.rst
@@ -14,3 +14,59 @@ mpv, further documentation is spread over a few places:
- http://mpv.io/manual/master/#list-of-input-commands
- http://mpv.io/manual/master/#properties
- https://github.com/mpv-player/mpv-examples/tree/master/libmpv
+
+C PLUGINS
+=========
+
+You can write C plugins for mpv. These use the libmpv API, although they do not
+use the libmpv library itself.
+
+Currently, they must be explicitlx enabled at build time with
+``--enable-cplugins``. They are available on Linux/BSD platforms only.
+
+C plugins location
+------------------
+
+C plugins are put into the mpv scripts directory in its config directory
+(see the `FILES`_ section for details). They must have a ``.so`` file extension.
+They can also be explicitly loaded with the ``--script`` option.
+
+API
+---
+
+A C plugin must export the following function::
+
+ int mpv_open_cplugin(mpv_handle *handle)
+
+The plugin function will be called on loading time. This function does not
+return as long as your plugin is loaded (it runs in its own thread). The
+``handle`` will be deallocated as soon as the plugin function returns.
+
+The return value is interpreted as error status. A value of ``0`` is
+interpreted as success, while any other value signals an error. In the latter
+case, the player prints an uninformative error message that loading failed.
+
+Within the plugin function, you can call libmpv API functions. The ``handle``
+is created by ``mpv_create_client()`` (or actually an internal equivalent),
+and belongs to you. You can call ``mpv_wait_event()`` to wait for things
+happening, and so on.
+
+Note that the player might block until your plugin calls ``mpv_wait_event()``
+for the first time. This gives you a chance to install initial hooks etc.
+before playback begins.
+
+The details are quite similar to Lua scripts.
+
+Linkage to libmpv
+-----------------
+
+The current implementation requires that your plugins are **not** linked against
+libmpv. What your plugins uses are not symbols from a libmpv binary, but
+symbols from the mpv host binary.
+
+Examples
+--------
+
+See:
+
+- https://github.com/mpv-player/mpv-examples/tree/master/cplugins
diff --git a/player/scripting.c b/player/scripting.c
index 4b92f7bf1b..2469b67678 100644
--- a/player/scripting.c
+++ b/player/scripting.c
@@ -37,11 +37,15 @@
#include "libmpv/client.h"
extern const struct mp_scripting mp_scripting_lua;
+extern const struct mp_scripting mp_scripting_cplugin;
static const struct mp_scripting *const scripting_backends[] = {
#if HAVE_LUA
&mp_scripting_lua,
#endif
+#if HAVE_CPLUGINS
+ &mp_scripting_cplugin,
+#endif
NULL
};
@@ -228,3 +232,33 @@ void mp_load_scripts(struct MPContext *mpctx)
}
talloc_free(tmp);
}
+
+#if HAVE_CPLUGINS
+
+#include <dlfcn.h>
+
+#define MPV_DLOPEN_FN "mpv_open_cplugin"
+typedef int (*mpv_open_cplugin)(mpv_handle *handle);
+
+static int load_cplugin(struct mpv_handle *client, const char *fname)
+{
+ int r = -1;
+ void *lib = dlopen(fname, RTLD_NOW | RTLD_LOCAL);
+ if (!lib)
+ goto error;
+ mpv_open_cplugin sym = (mpv_open_cplugin)dlsym(lib, MPV_DLOPEN_FN);
+ if (!sym)
+ goto error;
+ r = sym(client) ? -1 : 0;
+error:
+ if (lib)
+ dlclose(lib);
+ return r;
+}
+
+const struct mp_scripting mp_scripting_cplugin = {
+ .file_ext = "so",
+ .load = load_cplugin,
+};
+
+#endif
diff --git a/wscript b/wscript
index 193cf70bc4..2872e2500a 100644
--- a/wscript
+++ b/wscript
@@ -68,6 +68,12 @@ build_options = [
'desc': 'dynamic loader',
'func': check_libs(['dl'], check_statement('dlfcn.h', 'dlopen("", 0)'))
}, {
+ 'name': '--cplugins',
+ 'desc': 'C plugins',
+ 'deps': [ 'libdl' ],
+ 'default': 'disable',
+ 'func': check_cc(linkflags=['-Wl,-export-dynamic']),
+ }, {
'name': 'dlopen',
'desc': 'dlopen',
'deps_any': [ 'libdl', 'os-win32', 'os-cygwin' ],
@@ -1059,6 +1065,13 @@ def configure(ctx):
if ctx.dependency_satisfied('clang-database'):
ctx.load('clang_compilation_database')
+ if ctx.dependency_satisfied('cplugins'):
+ # We need to export the libmpv symbols, since the mpv binary itself is
+ # not linked against libmpv. The C plugin needs to be able to pick
+ # up the libmpv symbols from the binary. We still restrict the set
+ # of exported symbols via mpv.def.
+ ctx.env.LINKFLAGS += ['-Wl,-export-dynamic']
+
ctx.store_dependencies_lists()
def __write_version__(ctx):
diff --git a/wscript_build.py b/wscript_build.py
index 8e3cb6aa8e..9e213e824e 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -470,13 +470,19 @@ def build(ctx):
features = "c",
)
+ syms = False
+ if ctx.dependency_satisfied('cplugins'):
+ syms = True
+ ctx.load("syms")
+
if ctx.dependency_satisfied('cplayer'):
ctx(
target = "mpv",
source = main_fn_c,
use = ctx.dependencies_use() + ['objects'],
includes = _all_includes(ctx),
- features = "c cprogram",
+ features = "c cprogram" + (" syms" if syms else ""),
+ export_symbols_def = "libmpv/mpv.def", # for syms=True
install_path = ctx.env.BINDIR
)
for f in ['mpv.conf', 'input.conf', 'mplayer-input.conf', \