diff --git a/DOCS/man/libmpv.rst b/DOCS/man/libmpv.rst index 2b8020ae94..e00f8b9050 100644 --- a/DOCS/man/libmpv.rst +++ b/DOCS/man/libmpv.rst @@ -21,15 +21,15 @@ C PLUGINS You can write C plugins for mpv. These use the libmpv API, although they do not use the libmpv library itself. -They are available on Linux/BSD platforms only and enabled by default if the -compiler supports linking with the ``-rdynamic`` flag. +They are enabled by default if compiler supports linking with the ``-rdynamic`` +flag on Linux/BSD platforms. On Windows the are always enabled. 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. +(see the `FILES`_ section for details). They must have a ``.so`` or ``.dll`` +file extension. They can also be explicitly loaded with the ``--script`` option. API --- @@ -67,6 +67,10 @@ The current implementation requires that your plugins are **not** linked against libmpv. What your plugins use are not symbols from a libmpv binary, but symbols from the mpv host binary. +On Windows to make symbols from the host binary available, you have to define +MPV_CPLUGIN_DYNAMIC_SYM when compiling cplugin. This will load symbols +dynamically, before calling ``mpv_open_cplugin()``. + Examples -------- diff --git a/libmpv/client.h b/libmpv/client.h index f9c4420063..4199744ba7 100644 --- a/libmpv/client.h +++ b/libmpv/client.h @@ -28,10 +28,19 @@ #ifdef _WIN32 #define MPV_EXPORT __declspec(dllexport) +#define MPV_SELECTANY __declspec(selectany) #elif defined(__GNUC__) || defined(__clang__) #define MPV_EXPORT __attribute__((visibility("default"))) +#define MPV_SELECTANY #else #define MPV_EXPORT +#define MPV_SELECTANY +#endif + +#ifdef __cpp_decltype +#define MPV_DECLTYPE decltype +#else +#define MPV_DECLTYPE __typeof__ #endif #ifdef __cplusplus @@ -1888,6 +1897,127 @@ MPV_EXPORT int mpv_get_wakeup_pipe(mpv_handle *ctx); #endif +/** + * Defining MPV_CPLUGIN_DYNAMIC_SYM during plugin compilation will replace mpv_* + * functions with function pointers. Those pointer will be initialized when + * loading the plugin. + * + * It is recommended to use this symbol table when targeting Windows. The loader + * does not have notion of global symbols. Loading cplugin into mpv process will + * not allow this plugin to call any of the symbols that may be available in + * other modules. Instead cplugin has to link explicitly to specific PE binary, + * libmpv-2.dll/mpv.exe or any other binary that may have linked mpv statically. + * This limits portability of cplugin as it would need to be compiled separately + * for each of target PE binary that includes mpv's symbols. Which in practice + * is unrealistic, as we want one cplugin to be loaded without those restrictions. + * + * Instead of linking to any PE binary, we create function pointers for all mpv's + * exported symbols. For convenience names of entrypoints are redefined to those + * pointer, so no changes are required in cplugin source code, except of defining + * MPV_CPLUGIN_DYNAMIC_SYM. Those function pointer are exported to make them + * available for mpv to init with correct values during runtime, before calling + * `mpv_open_cplugin`. + * + * Note that those pointers are decorated with `selectany` attribute, so no need + * to worry about multiple definitions, linker will keep only single instance. + */ +#ifdef MPV_CPLUGIN_DYNAMIC_SYM + +#define MPV_DEFINE_SYM_PTR(name) \ + MPV_SELECTANY MPV_EXPORT \ + MPV_DECLTYPE(name) *pfn_##name; + +MPV_DEFINE_SYM_PTR(mpv_client_api_version) +#define mpv_client_api_version pfn_mpv_client_api_version +MPV_DEFINE_SYM_PTR(mpv_error_string) +#define mpv_error_string pfn_mpv_error_string +MPV_DEFINE_SYM_PTR(mpv_free) +#define mpv_free pfn_mpv_free +MPV_DEFINE_SYM_PTR(mpv_client_name) +#define mpv_client_name pfn_mpv_client_name +MPV_DEFINE_SYM_PTR(mpv_client_id) +#define mpv_client_id pfn_mpv_client_id +MPV_DEFINE_SYM_PTR(mpv_create) +#define mpv_create pfn_mpv_create +MPV_DEFINE_SYM_PTR(mpv_initialize) +#define mpv_initialize pfn_mpv_initialize +MPV_DEFINE_SYM_PTR(mpv_destroy) +#define mpv_destroy pfn_mpv_destroy +MPV_DEFINE_SYM_PTR(mpv_terminate_destroy) +#define mpv_terminate_destroy pfn_mpv_terminate_destroy +MPV_DEFINE_SYM_PTR(mpv_create_client) +#define mpv_create_client pfn_mpv_create_client +MPV_DEFINE_SYM_PTR(mpv_create_weak_client) +#define mpv_create_weak_client pfn_mpv_create_weak_client +MPV_DEFINE_SYM_PTR(mpv_load_config_file) +#define mpv_load_config_file pfn_mpv_load_config_file +MPV_DEFINE_SYM_PTR(mpv_get_time_us) +#define mpv_get_time_us pfn_mpv_get_time_us +MPV_DEFINE_SYM_PTR(mpv_free_node_contents) +#define mpv_free_node_contents pfn_mpv_free_node_contents +MPV_DEFINE_SYM_PTR(mpv_set_option) +#define mpv_set_option pfn_mpv_set_option +MPV_DEFINE_SYM_PTR(mpv_set_option_string) +#define mpv_set_option_string pfn_mpv_set_option_string +MPV_DEFINE_SYM_PTR(mpv_command) +#define mpv_command pfn_mpv_command +MPV_DEFINE_SYM_PTR(mpv_command_node) +#define mpv_command_node pfn_mpv_command_node +MPV_DEFINE_SYM_PTR(mpv_command_ret) +#define mpv_command_ret pfn_mpv_command_ret +MPV_DEFINE_SYM_PTR(mpv_command_string) +#define mpv_command_string pfn_mpv_command_string +MPV_DEFINE_SYM_PTR(mpv_command_async) +#define mpv_command_async pfn_mpv_command_async +MPV_DEFINE_SYM_PTR(mpv_command_node_async) +#define mpv_command_node_async pfn_mpv_command_node_async +MPV_DEFINE_SYM_PTR(mpv_abort_async_command) +#define mpv_abort_async_command pfn_mpv_abort_async_command +MPV_DEFINE_SYM_PTR(mpv_set_property) +#define mpv_set_property pfn_mpv_set_property +MPV_DEFINE_SYM_PTR(mpv_set_property_string) +#define mpv_set_property_string pfn_mpv_set_property_string +MPV_DEFINE_SYM_PTR(mpv_del_property) +#define mpv_del_property pfn_mpv_del_property +MPV_DEFINE_SYM_PTR(mpv_set_property_async) +#define mpv_set_property_async pfn_mpv_set_property_async +MPV_DEFINE_SYM_PTR(mpv_get_property) +#define mpv_get_property pfn_mpv_get_property +MPV_DEFINE_SYM_PTR(mpv_get_property_string) +#define mpv_get_property_string pfn_mpv_get_property_string +MPV_DEFINE_SYM_PTR(mpv_get_property_osd_string) +#define mpv_get_property_osd_string pfn_mpv_get_property_osd_string +MPV_DEFINE_SYM_PTR(mpv_get_property_async) +#define mpv_get_property_async pfn_mpv_get_property_async +MPV_DEFINE_SYM_PTR(mpv_observe_property) +#define mpv_observe_property pfn_mpv_observe_property +MPV_DEFINE_SYM_PTR(mpv_unobserve_property) +#define mpv_unobserve_property pfn_mpv_unobserve_property +MPV_DEFINE_SYM_PTR(mpv_event_name) +#define mpv_event_name pfn_mpv_event_name +MPV_DEFINE_SYM_PTR(mpv_event_to_node) +#define mpv_event_to_node pfn_mpv_event_to_node +MPV_DEFINE_SYM_PTR(mpv_request_event) +#define mpv_request_event pfn_mpv_request_event +MPV_DEFINE_SYM_PTR(mpv_request_log_messages) +#define mpv_request_log_messages pfn_mpv_request_log_messages +MPV_DEFINE_SYM_PTR(mpv_wait_event) +#define mpv_wait_event pfn_mpv_wait_event +MPV_DEFINE_SYM_PTR(mpv_wakeup) +#define mpv_wakeup pfn_mpv_wakeup +MPV_DEFINE_SYM_PTR(mpv_set_wakeup_callback) +#define mpv_set_wakeup_callback pfn_mpv_set_wakeup_callback +MPV_DEFINE_SYM_PTR(mpv_wait_async_requests) +#define mpv_wait_async_requests pfn_mpv_wait_async_requests +MPV_DEFINE_SYM_PTR(mpv_hook_add) +#define mpv_hook_add pfn_mpv_hook_add +MPV_DEFINE_SYM_PTR(mpv_hook_continue) +#define mpv_hook_continue pfn_mpv_hook_continue +MPV_DEFINE_SYM_PTR(mpv_get_wakeup_pipe) +#define mpv_get_wakeup_pipe pfn_mpv_get_wakeup_pipe + +#endif + #ifdef __cplusplus } #endif diff --git a/libmpv/render.h b/libmpv/render.h index 29f9b91e96..5c9e052bc8 100644 --- a/libmpv/render.h +++ b/libmpv/render.h @@ -731,6 +731,27 @@ MPV_EXPORT void mpv_render_context_report_swap(mpv_render_context *ctx); */ MPV_EXPORT void mpv_render_context_free(mpv_render_context *ctx); +#ifdef MPV_CPLUGIN_DYNAMIC_SYM + +MPV_DEFINE_SYM_PTR(mpv_render_context_create) +#define mpv_render_context_create pfn_mpv_render_context_create +MPV_DEFINE_SYM_PTR(mpv_render_context_set_parameter) +#define mpv_render_context_set_parameter pfn_mpv_render_context_set_parameter +MPV_DEFINE_SYM_PTR(mpv_render_context_get_info) +#define mpv_render_context_get_info pfn_mpv_render_context_get_info +MPV_DEFINE_SYM_PTR(mpv_render_context_set_update_callback) +#define mpv_render_context_set_update_callback pfn_mpv_render_context_set_update_callback +MPV_DEFINE_SYM_PTR(mpv_render_context_update) +#define mpv_render_context_update pfn_mpv_render_context_update +MPV_DEFINE_SYM_PTR(mpv_render_context_render) +#define mpv_render_context_render pfn_mpv_render_context_render +MPV_DEFINE_SYM_PTR(mpv_render_context_report_swap) +#define mpv_render_context_report_swap pfn_mpv_render_context_report_swap +MPV_DEFINE_SYM_PTR(mpv_render_context_free) +#define mpv_render_context_free pfn_mpv_render_context_free + +#endif + #ifdef __cplusplus } #endif diff --git a/libmpv/stream_cb.h b/libmpv/stream_cb.h index 9d1c2cc8d3..9ae6f31a16 100644 --- a/libmpv/stream_cb.h +++ b/libmpv/stream_cb.h @@ -233,6 +233,13 @@ typedef int (*mpv_stream_cb_open_ro_fn)(void *user_data, char *uri, MPV_EXPORT int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data, mpv_stream_cb_open_ro_fn open_fn); +#ifdef MPV_CPLUGIN_DYNAMIC_SYM + +MPV_DEFINE_SYM_PTR(mpv_stream_cb_add_ro) +#define mpv_stream_cb_add_ro pfn_mpv_stream_cb_add_ro + +#endif + #ifdef __cplusplus } #endif diff --git a/player/scripting.c b/player/scripting.c index 48153ef6df..9a23a0dae0 100644 --- a/player/scripting.c +++ b/player/scripting.c @@ -40,6 +40,8 @@ #include "core.h" #include "client.h" #include "libmpv/client.h" +#include "libmpv/render.h" +#include "libmpv/stream_cb.h" extern const struct mp_scripting mp_scripting_lua; extern const struct mp_scripting mp_scripting_cplugin; @@ -302,6 +304,76 @@ bool mp_load_scripts(struct MPContext *mpctx) #define MPV_DLOPEN_FN "mpv_open_cplugin" typedef int (*mpv_open_cplugin)(mpv_handle *handle); +static void init_sym_table(struct mp_script_args *args, void *lib) { +#define INIT_SYM(name) \ + { \ + void **sym = (void **)dlsym(lib, "pfn_" #name); \ + if (sym) { \ + if (*sym && *sym != &name) \ + MP_ERR(args, "Overriding already set function " #name "\n"); \ + *sym = &name; \ + } \ + } + + INIT_SYM(mpv_client_api_version); + INIT_SYM(mpv_error_string); + INIT_SYM(mpv_free); + INIT_SYM(mpv_client_name); + INIT_SYM(mpv_client_id); + INIT_SYM(mpv_create); + INIT_SYM(mpv_initialize); + INIT_SYM(mpv_destroy); + INIT_SYM(mpv_terminate_destroy); + INIT_SYM(mpv_create_client); + INIT_SYM(mpv_create_weak_client); + INIT_SYM(mpv_load_config_file); + INIT_SYM(mpv_get_time_us); + INIT_SYM(mpv_free_node_contents); + INIT_SYM(mpv_set_option); + INIT_SYM(mpv_set_option_string); + INIT_SYM(mpv_command); + INIT_SYM(mpv_command_node); + INIT_SYM(mpv_command_ret); + INIT_SYM(mpv_command_string); + INIT_SYM(mpv_command_async); + INIT_SYM(mpv_command_node_async); + INIT_SYM(mpv_abort_async_command); + INIT_SYM(mpv_set_property); + INIT_SYM(mpv_set_property_string); + INIT_SYM(mpv_del_property); + INIT_SYM(mpv_set_property_async); + INIT_SYM(mpv_get_property); + INIT_SYM(mpv_get_property_string); + INIT_SYM(mpv_get_property_osd_string); + INIT_SYM(mpv_get_property_async); + INIT_SYM(mpv_observe_property); + INIT_SYM(mpv_unobserve_property); + INIT_SYM(mpv_event_name); + INIT_SYM(mpv_event_to_node); + INIT_SYM(mpv_request_event); + INIT_SYM(mpv_request_log_messages); + INIT_SYM(mpv_wait_event); + INIT_SYM(mpv_wakeup); + INIT_SYM(mpv_set_wakeup_callback); + INIT_SYM(mpv_wait_async_requests); + INIT_SYM(mpv_hook_add); + INIT_SYM(mpv_hook_continue); + INIT_SYM(mpv_get_wakeup_pipe); + + INIT_SYM(mpv_render_context_create); + INIT_SYM(mpv_render_context_set_parameter); + INIT_SYM(mpv_render_context_get_info); + INIT_SYM(mpv_render_context_set_update_callback); + INIT_SYM(mpv_render_context_update); + INIT_SYM(mpv_render_context_render); + INIT_SYM(mpv_render_context_report_swap); + INIT_SYM(mpv_render_context_free); + + INIT_SYM(mpv_stream_cb_add_ro); + +#undef INIT_SYM +} + static int load_cplugin(struct mp_script_args *args) { void *lib = dlopen(args->filename, RTLD_NOW | RTLD_LOCAL); @@ -312,6 +384,9 @@ static int load_cplugin(struct mp_script_args *args) mpv_open_cplugin sym = (mpv_open_cplugin)dlsym(lib, MPV_DLOPEN_FN); if (!sym) goto error; + + init_sym_table(args, lib); + return sym(args->client) ? -1 : 0; error: ; char *err = dlerror();