summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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', \