diff options
-rw-r--r-- | DOCS/man/libmpv.rst | 56 | ||||
-rw-r--r-- | player/scripting.c | 34 | ||||
-rw-r--r-- | wscript | 13 | ||||
-rw-r--r-- | wscript_build.py | 8 |
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 @@ -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', \ |