diff --git a/src/modules/meson.build b/src/modules/meson.build index ccd08e7d5..6d816d4b8 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -48,6 +48,16 @@ pipewire_module_client_node = shared_library('pipewire-module-client-node', dependencies : [mathlib, dl_lib, pipewire_dep], ) +pipewire_module_link_factory = shared_library('pipewire-module-link-factory', + [ 'module-link-factory.c' ], + c_args : pipewire_module_c_args, + include_directories : [configinc, spa_inc], + link_with : spalib, + install : true, + install_dir : modules_install_dir, + dependencies : [mathlib, dl_lib, pipewire_dep], +) + #pipewire_module_protocol_dbus = shared_library('pipewire-module-protocol-dbus', [ 'module-protocol-dbus.c', gdbus_target ], # c_args : pipewire_module_c_args, # include_directories : [configinc, spa_inc], diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c index d395abd76..994b2c9f2 100644 --- a/src/modules/module-flatpak.c +++ b/src/modules/module-flatpak.c @@ -427,40 +427,9 @@ static void do_create_object(void *data, return; } -static void -do_create_link(void *data, - uint32_t output_node_id, - uint32_t output_port_id, - uint32_t input_node_id, - uint32_t input_port_id, - const struct spa_pod *filter, - const struct spa_dict *props, - uint32_t new_id) -{ - struct resource *resource = data; - struct client_info *cinfo = resource->cinfo; - - if (cinfo->is_sandboxed) { - pw_resource_error(resource->resource, -EPERM, "not allowed"); - return; - } - pw_resource_do_parent(resource->resource, - &resource->override, - struct pw_core_proxy_methods, - create_link, - output_node_id, - output_port_id, - input_node_id, - input_port_id, - filter, - props, - new_id); -} - static const struct pw_core_proxy_methods core_override = { PW_VERSION_CORE_PROXY_METHODS, .create_object = do_create_object, - .create_link = do_create_link, }; static void client_resource_impl(void *data, struct pw_resource *resource) diff --git a/src/modules/module-link-factory.c b/src/modules/module-link-factory.c new file mode 100644 index 000000000..136268dbe --- /dev/null +++ b/src/modules/module-link-factory.c @@ -0,0 +1,219 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "config.h" + +#include "pipewire/core.h" +#include "pipewire/interfaces.h" +#include "pipewire/log.h" +#include "pipewire/module.h" +#include "pipewire/link.h" + +struct factory_data { + struct pw_factory *this; + struct pw_properties *properties; + + struct spa_hook module_listener; +}; + +static void *create_object(void *_data, + struct pw_resource *resource, + uint32_t type, + uint32_t version, + struct pw_properties *properties, + uint32_t new_id) +{ + struct pw_client *client; + struct pw_node *output_node, *input_node; + struct pw_port *outport, *inport; + struct pw_core *core; + struct pw_type *t; + struct pw_global *global; + struct pw_link *link; + uint32_t output_node_id, input_node_id; + uint32_t output_port_id, input_port_id; + char *error; + const char *str; + int res; + + if (resource == NULL) + goto no_resource; + + if (properties == NULL) + goto no_properties; + + if ((str = pw_properties_get(properties, PW_LINK_OUTPUT_NODE_ID)) == NULL) + goto no_properties; + + output_node_id = pw_properties_parse_int(str); + + if ((str = pw_properties_get(properties, PW_LINK_INPUT_NODE_ID)) == NULL) + goto no_properties; + + input_node_id = pw_properties_parse_int(str); + + str = pw_properties_get(properties, PW_LINK_OUTPUT_PORT_ID); + output_port_id = str ? pw_properties_parse_int(str) : -1; + + str = pw_properties_get(properties, PW_LINK_INPUT_PORT_ID); + input_port_id = str ? pw_properties_parse_int(str) : -1; + + client = pw_resource_get_client(resource); + core = pw_client_get_core(client); + t = pw_core_get_type(core); + + global = pw_core_find_global(core, output_node_id); + if (global == NULL || pw_global_get_type(global) != t->node) + goto no_output; + + output_node = pw_global_get_object(global); + + global = pw_core_find_global(core, input_node_id); + if (global == NULL || pw_global_get_type(global) != t->node) + goto no_input; + + input_node = pw_global_get_object(global); + + if (output_port_id == -1) + outport = pw_node_get_free_port(output_node, SPA_DIRECTION_OUTPUT); + else + outport = pw_node_find_port(output_node, SPA_DIRECTION_OUTPUT, output_port_id); + if (outport == NULL) + goto no_output_port; + + if (input_port_id == -1) + inport = pw_node_get_free_port(input_node, SPA_DIRECTION_INPUT); + else + inport = pw_node_find_port(input_node, SPA_DIRECTION_INPUT, input_port_id); + if (inport == NULL) + goto no_input_port; + + link = pw_link_new(core, outport, inport, NULL, NULL, &error, 0); + if (link == NULL) + goto no_mem; + + pw_link_register(link, client, pw_client_get_global(client)); + + res = pw_global_bind(pw_link_get_global(link), client, PW_PERM_RWX, PW_VERSION_LINK, new_id); + if (res < 0) + goto no_bind; + + return link; + + no_resource: + pw_log_error("link factory needs a resource"); + pw_resource_error(resource, -EINVAL, "no resource"); + goto done; + no_properties: + pw_log_error("link-factory needs properties"); + pw_resource_error(resource, -EINVAL, "no properties"); + goto done; + no_output: + pw_log_error("link-factory unknown output node %d", output_node_id); + pw_resource_error(resource, -EINVAL, "unknown output node"); + goto done; + no_input: + pw_log_error("link-factory unknown input node %d", input_node_id); + pw_resource_error(resource, -EINVAL, "unknown input node"); + goto done; + no_output_port: + pw_log_error("link-factory unknown output port %d", output_port_id); + pw_resource_error(resource, -EINVAL, "unknown output port"); + goto done; + no_input_port: + pw_log_error("link-factory unknown input port %d", input_port_id); + pw_resource_error(resource, -EINVAL, "unknown input port"); + goto done; + no_mem: + pw_log_error("can't create link"); + pw_resource_error(resource, -ENOMEM, "no memory"); + goto done; + no_bind: + pw_resource_error(resource, res, "can't bind link"); + goto done; + done: + if (properties) + pw_properties_free(properties); + return NULL; +} + +static const struct pw_factory_implementation impl_factory = { + PW_VERSION_FACTORY_IMPLEMENTATION, + .create_object = create_object, +}; + +static void module_destroy(void *data) +{ + struct factory_data *d = data; + + spa_hook_remove(&d->module_listener); + + if (d->properties) + pw_properties_free(d->properties); + + pw_factory_destroy(d->this); +} + +static const struct pw_module_events module_events = { + PW_VERSION_MODULE_EVENTS, + .destroy = module_destroy, +}; + +static int module_init(struct pw_module *module, struct pw_properties *properties) +{ + struct pw_core *core = pw_module_get_core(module); + struct pw_type *t = pw_core_get_type(core); + struct pw_factory *factory; + struct factory_data *data; + + factory = pw_factory_new(core, + "link-factory", + t->link, + PW_VERSION_LINK, + NULL, + sizeof(*data)); + if (factory == NULL) + return -ENOMEM; + + data = pw_factory_get_user_data(factory); + data->this = factory; + data->properties = properties; + + pw_log_debug("module %p: new", module); + + pw_factory_set_implementation(factory, + &impl_factory, + data); + + pw_factory_register(factory, NULL, pw_module_get_global(module)); + + pw_module_add_listener(module, &data->module_listener, &module_events, data); + + return 0; +} + +int pipewire__module_init(struct pw_module *module, const char *args) +{ + return module_init(module, NULL); +} diff --git a/src/modules/module-protocol-native/protocol-native.c b/src/modules/module-protocol-native/protocol-native.c index 2978f6ea2..2342c8217 100644 --- a/src/modules/module-protocol-native/protocol-native.c +++ b/src/modules/module-protocol-native/protocol-native.c @@ -133,45 +133,6 @@ core_marshal_create_object(void *object, pw_protocol_native_end_proxy(proxy, b); } -static void -core_marshal_create_link(void *object, - uint32_t output_node_id, - uint32_t output_port_id, - uint32_t input_node_id, - uint32_t input_port_id, - const struct spa_pod *filter, - const struct spa_dict *props, - uint32_t new_id) -{ - struct pw_proxy *proxy = object; - struct spa_pod_builder *b; - uint32_t i, n_items; - - b = pw_protocol_native_begin_proxy(proxy, PW_CORE_PROXY_METHOD_CREATE_LINK); - - n_items = props ? props->n_items : 0; - - spa_pod_builder_add(b, - "[" - "i", output_node_id, - "i", output_port_id, - "i", input_node_id, - "i", input_port_id, - "P", filter, - "i", n_items, NULL); - - for (i = 0; i < n_items; i++) { - spa_pod_builder_add(b, - "s", props->items[i].key, - "s", props->items[i].value, NULL); - } - spa_pod_builder_add(b, - "i", new_id, - "]", NULL); - - pw_protocol_native_end_proxy(proxy, b); -} - static void core_marshal_update_types_client(void *object, uint32_t first_id, const char **types, uint32_t n_types) { @@ -502,45 +463,6 @@ static int core_demarshal_create_object(void *object, void *data, size_t size) return 0; } -static int core_demarshal_create_link(void *object, void *data, size_t size) -{ - struct pw_resource *resource = object; - struct spa_pod_parser prs; - uint32_t new_id, i; - uint32_t output_node_id, output_port_id, input_node_id, input_port_id; - struct spa_pod *filter = NULL; - struct spa_dict props; - - spa_pod_parser_init(&prs, data, size, 0); - if (spa_pod_parser_get(&prs, - "[" - "i", &output_node_id, - "i", &output_port_id, - "i", &input_node_id, - "i", &input_port_id, - "P", &filter, - "i", &props.n_items, NULL) < 0) - return -EINVAL; - - props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); - for (i = 0; i < props.n_items; i++) { - if (spa_pod_parser_get(&prs, "ss", - &props.items[i].key, &props.items[i].value, NULL) < 0) - return -EINVAL; - } - if (spa_pod_parser_get(&prs, "i", &new_id, NULL) < 0) - return -EINVAL; - - pw_resource_do(resource, struct pw_core_proxy_methods, create_link, output_node_id, - output_port_id, - input_node_id, - input_port_id, - filter, - &props, - new_id); - return 0; -} - static int core_demarshal_update_types_server(void *object, void *data, size_t size) { struct pw_resource *resource = object; @@ -1006,8 +928,7 @@ static const struct pw_core_proxy_methods pw_protocol_native_core_method_marshal &core_marshal_get_registry, &core_marshal_client_update, &core_marshal_permissions, - &core_marshal_create_object, - &core_marshal_create_link + &core_marshal_create_object }; static const struct pw_protocol_native_demarshal pw_protocol_native_core_method_demarshal[PW_CORE_PROXY_METHOD_NUM] = { @@ -1016,8 +937,7 @@ static const struct pw_protocol_native_demarshal pw_protocol_native_core_method_ { &core_demarshal_get_registry, 0, }, { &core_demarshal_client_update, 0, }, { &core_demarshal_permissions, 0, }, - { &core_demarshal_create_object, PW_PROTOCOL_NATIVE_REMAP, }, - { &core_demarshal_create_link, PW_PROTOCOL_NATIVE_REMAP, } + { &core_demarshal_create_object, PW_PROTOCOL_NATIVE_REMAP, } }; static const struct pw_core_proxy_events pw_protocol_native_core_event_marshal = { diff --git a/src/pipewire/core.c b/src/pipewire/core.c index c5ff9e27f..a53d13af6 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -230,93 +230,6 @@ core_create_object(void *object, goto done; } -static void -core_create_link(void *object, - uint32_t output_node_id, - uint32_t output_port_id, - uint32_t input_node_id, - uint32_t input_port_id, - const struct spa_pod *filter, - const struct spa_dict *props, - uint32_t new_id) -{ - struct pw_resource *resource = object; - struct pw_client *client = resource->client; - struct pw_node *output_node, *input_node; - struct pw_port *outport, *inport; - struct pw_core *core = client->core; - struct pw_global *global; - struct pw_link *link; - char *error; - int res; - - global = pw_core_find_global(core, output_node_id); - if (global == NULL || global->type != core->type.node) - goto no_output; - - output_node = global->object; - - global = pw_core_find_global(core, input_node_id); - if (global == NULL || global->type != core->type.node) - goto no_input; - - input_node = global->object; - - if (output_port_id == SPA_ID_INVALID) - outport = pw_node_get_free_port(output_node, SPA_DIRECTION_OUTPUT); - else - outport = pw_node_find_port(output_node, SPA_DIRECTION_OUTPUT, output_port_id); - if (outport == NULL) - goto no_output_port; - - if (input_port_id == SPA_ID_INVALID) - inport = pw_node_get_free_port(input_node, SPA_DIRECTION_INPUT); - else - inport = pw_node_find_port(input_node, SPA_DIRECTION_INPUT, input_port_id); - if (inport == NULL) - goto no_input_port; - - link = pw_link_new(core, outport, inport, NULL, NULL, &error, 0); - if (link == NULL) - goto no_link; - - pw_link_register(link, client, pw_client_get_global(client)); - - res = pw_global_bind(pw_link_get_global(link), client, PW_PERM_RWX, PW_VERSION_LINK, new_id); - if (res < 0) - goto no_bind; - - done: - return; - - no_output: - pw_core_resource_error(client->core_resource, - resource->id, -EINVAL, "unknown output node"); - goto done; - no_input: - pw_core_resource_error(client->core_resource, - resource->id, -EINVAL, "unknown input node"); - goto done; - no_output_port: - pw_core_resource_error(client->core_resource, - resource->id, -EINVAL, "unknown output port"); - goto done; - no_input_port: - pw_core_resource_error(client->core_resource, - resource->id, -EINVAL, "unknown input port"); - goto done; - no_link: - pw_core_resource_error(client->core_resource, - resource->id, -ENOMEM, "can't create link: %s, error"); - free(error); - goto done; - no_bind: - pw_core_resource_error(client->core_resource, - resource->id, res, "can't bind link: %d", res); - goto done; - -} - static void core_update_types(void *object, uint32_t first_id, const char **types, uint32_t n_types) { struct pw_resource *resource = object; @@ -339,7 +252,6 @@ static const struct pw_core_proxy_methods core_methods = { .client_update = core_client_update, .permissions = core_permissions, .create_object = core_create_object, - .create_link = core_create_link }; static void core_unbind_func(void *data) diff --git a/src/pipewire/interfaces.h b/src/pipewire/interfaces.h index 9816824bf..0b1179830 100644 --- a/src/pipewire/interfaces.h +++ b/src/pipewire/interfaces.h @@ -72,8 +72,7 @@ struct pw_link_proxy; #define PW_CORE_PROXY_METHOD_CLIENT_UPDATE 3 #define PW_CORE_PROXY_METHOD_PERMISSIONS 4 #define PW_CORE_PROXY_METHOD_CREATE_OBJECT 5 -#define PW_CORE_PROXY_METHOD_CREATE_LINK 6 -#define PW_CORE_PROXY_METHOD_NUM 7 +#define PW_CORE_PROXY_METHOD_NUM 6 /** * Key to update default permissions of globals without specific @@ -95,6 +94,11 @@ struct pw_link_proxy; * Value is "[r][w][x]" */ #define PW_CORE_PROXY_PERMISSIONS_EXISTING "permissions.existing" +#define PW_LINK_OUTPUT_NODE_ID "link.output_node.id" +#define PW_LINK_OUTPUT_PORT_ID "link.output_port.id" +#define PW_LINK_INPUT_NODE_ID "link.input_node.id" +#define PW_LINK_INPUT_PORT_ID "link.input_port.id" + /** * \struct pw_core_proxy_methods * \brief Core methods @@ -173,25 +177,6 @@ struct pw_core_proxy_methods { uint32_t version, const struct spa_dict *props, uint32_t new_id); - /** - * Create a new link between two node ports - * - * \param output_node_id the global id of the output node - * \param output_port_id the id of the output port - * \param input_node_id the global id of the input node - * \param input_port_id the id of the input port - * \param filter an optional format filter - * \param props optional properties - * \param new_id the client proxy id - */ - void (*create_link) (void *object, - uint32_t output_node_id, - uint32_t output_port_id, - uint32_t input_node_id, - uint32_t input_port_id, - const struct spa_pod *filter, - const struct spa_dict *props, - uint32_t new_id); }; static inline void @@ -240,24 +225,6 @@ pw_core_proxy_create_object(struct pw_core_proxy *core, return p; } -static inline struct pw_link_proxy * -pw_core_proxy_create_link(struct pw_core_proxy *core, - uint32_t type, - uint32_t output_node_id, - uint32_t output_port_id, - uint32_t input_node_id, - uint32_t input_port_id, - const struct spa_pod *filter, - const struct spa_dict *prop, - size_t user_data_size) -{ - struct pw_proxy *p = pw_proxy_new((struct pw_proxy*)core, type, user_data_size); - pw_proxy_do((struct pw_proxy*)core, struct pw_core_proxy_methods, create_link, output_node_id, output_port_id, - input_node_id, input_port_id, filter, prop, pw_proxy_get_id(p)); - return (struct pw_link_proxy*) p; -} - - #define PW_CORE_PROXY_EVENT_UPDATE_TYPES 0 #define PW_CORE_PROXY_EVENT_DONE 1 #define PW_CORE_PROXY_EVENT_ERROR 2 diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c index efe8b6477..fb88d2594 100644 --- a/src/tools/pipewire-cli.c +++ b/src/tools/pipewire-cli.c @@ -934,11 +934,18 @@ static bool do_create_link(struct data *data, const char *cmd, char *args, char } if (n == 5) props = parse_props(a[4]); + else + props = pw_properties_new(NULL, NULL); - proxy = (struct pw_proxy*)pw_core_proxy_create_link(rd->core_proxy, t->link, - atoi(a[0]), atoi(a[1]), - atoi(a[2]), atoi(a[3]), - NULL, + pw_properties_set(props, PW_LINK_OUTPUT_NODE_ID, a[0]); + pw_properties_set(props, PW_LINK_OUTPUT_PORT_ID, a[1]); + pw_properties_set(props, PW_LINK_INPUT_NODE_ID, a[2]); + pw_properties_set(props, PW_LINK_INPUT_PORT_ID, a[3]); + + proxy = (struct pw_proxy*)pw_core_proxy_create_object(rd->core_proxy, + "link-factory", + t->link, + PW_VERSION_LINK, props ? &props->dict : NULL, sizeof(struct proxy_data)); @@ -1099,6 +1106,8 @@ int main(int argc, char *argv[]) data.t = pw_core_get_type(data.core); info = pw_core_get_info(data.core); + pw_module_load(data.core, "libpipewire-module-link-factory", NULL); + pw_loop_add_io(l, STDIN_FILENO, SPA_IO_IN|SPA_IO_HUP, false, do_input, &data); fprintf(stdout, "Welcome to PipeWire \"%s\" version %s. Type 'help' for usage.\n", info->name, info->version);