diff --git a/pipewire-pulseaudio b/pipewire-pulseaudio index 772ec76cd..0c106e677 160000 --- a/pipewire-pulseaudio +++ b/pipewire-pulseaudio @@ -1 +1 @@ -Subproject commit 772ec76cd90b4ac089e6903d4a078088cd02e14d +Subproject commit 0c106e677752438fb1446397bac10e578c79b82c diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in index cc9bc0913..f3cb94342 100644 --- a/src/daemon/pipewire.conf.in +++ b/src/daemon/pipewire.conf.in @@ -16,6 +16,7 @@ add-spa-lib api.jack.* jack/libspa-jack #load-module libpipewire-module-spa-node api.alsa.seq.bridge node.name=MIDI-Bridge load-module libpipewire-module-rtkit load-module libpipewire-module-protocol-native +load-module libpipewire-module-metadata load-module libpipewire-module-spa-device-factory load-module libpipewire-module-spa-node-factory #load-module libpipewire-module-spa-node api.vulkan.compute.source node.name=my-compute-source diff --git a/src/examples/media-session/media-session.c b/src/examples/media-session/media-session.c index 88331dcac..6eb100b5c 100644 --- a/src/examples/media-session/media-session.c +++ b/src/examples/media-session/media-session.c @@ -87,6 +87,8 @@ struct impl { struct monitor alsa_monitor; struct monitor v4l2_monitor; + struct sm_metadata *metadata; + struct spa_dbus *dbus; struct spa_dbus_connection *dbus_connection; DBusConnection *conn; @@ -193,6 +195,7 @@ struct session { #include "alsa-monitor.c" #include "v4l2-monitor.c" #include "bluez-monitor.c" +#include "metadata.c" static void add_object(struct impl *impl, struct object *obj) { @@ -1244,6 +1247,12 @@ static void start_services(struct impl *impl) else pw_log_debug("got dbus connection %p", impl->conn); + pw_remote_export(impl->remote, + PW_TYPE_INTERFACE_Metadata, + NULL, + impl->metadata, + 0); + bluez5_start_monitor(impl, &impl->bluez5_monitor); alsa_start_monitor(impl, &impl->alsa_monitor); alsa_start_midi_bridge(impl); @@ -1316,6 +1325,7 @@ int main(int argc, char *argv[]) pw_module_load(impl.core, "libpipewire-module-client-device", NULL, NULL); pw_module_load(impl.core, "libpipewire-module-adapter", NULL, NULL); + pw_module_load(impl.core, "libpipewire-module-metadata", NULL, NULL); clock_gettime(CLOCK_MONOTONIC, &impl.now); @@ -1324,6 +1334,8 @@ int main(int argc, char *argv[]) if (pw_remote_connect(impl.remote) < 0) return -1; + impl.metadata = sm_metadata_new(NULL); + pw_main_loop_run(impl.loop); pw_core_destroy(impl.core); diff --git a/src/modules/meson.build b/src/modules/meson.build index 1466d45fe..1352b7e2e 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -81,6 +81,20 @@ pipewire_module_client_node = shared_library('pipewire-module-client-node', dependencies : [mathlib, dl_lib, pipewire_dep], ) +pipewire_module_metadata = shared_library('pipewire-module-metadata', + [ 'module-metadata.c', + 'module-metadata/resource-metadata.c', + 'module-metadata/proxy-metadata.c', + 'module-metadata/metadata.c', + 'module-metadata/protocol-native.c'], + c_args : pipewire_module_c_args, + include_directories : [configinc, spa_inc], + link_with : pipewire_module_protocol_native, + install : true, + install_dir : modules_install_dir, + dependencies : [mathlib, dl_lib, pipewire_dep], +) + test('pw-test-protocol-native', executable('pw-test-protocol-native', [ 'module-protocol-native/test-connection.c', diff --git a/src/modules/module-metadata.c b/src/modules/module-metadata.c new file mode 100644 index 000000000..d2b17fd5f --- /dev/null +++ b/src/modules/module-metadata.c @@ -0,0 +1,185 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "config.h" + +#include + +#include +#include + +#define NAME "metadata" + +static const struct spa_dict_item module_props[] = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans " }, + { PW_KEY_MODULE_DESCRIPTION, "Allow clients to create metadata store" }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + + +void * pw_metadata_new(struct pw_core *core, struct pw_resource *resource, + struct pw_properties *properties); + +struct pw_proxy *pw_remote_metadata_export(struct pw_remote *remote, + uint32_t type, struct pw_properties *props, void *object, size_t user_data_size); + +int pw_protocol_native_ext_metadata_init(struct pw_core *core); + +struct factory_data { + struct pw_factory *this; + + struct pw_module *module; + struct spa_hook module_listener; + + struct pw_export_type export_metadata; +}; + +static void *create_object(void *_data, + struct pw_resource *resource, + uint32_t type, + uint32_t version, + struct pw_properties *properties, + uint32_t new_id) +{ + void *result; + struct pw_resource *metadata_resource; + struct pw_client *client = pw_resource_get_client(resource); + int res; + + metadata_resource = pw_resource_new(client, new_id, PW_PERM_RWX, type, version, 0); + if (metadata_resource == NULL) { + res = -errno; + goto error_resource; + } + + pw_log_debug("."); + result = pw_metadata_new(pw_client_get_core(client), metadata_resource, properties); + if (result == NULL) { + res = -errno; + goto error_node; + } + pw_log_debug("."); + return result; + +error_resource: + pw_log_error("can't create resource: %s", spa_strerror(res)); + pw_resource_error(resource, res, "can't create resource: %s", spa_strerror(res)); + goto error_exit; +error_node: + pw_log_error("can't create metadata: %s", spa_strerror(res)); + pw_resource_error(resource, res, "can't create metadata: %s", spa_strerror(res)); + goto error_exit_free; + +error_exit_free: + pw_resource_destroy(metadata_resource); +error_exit: + errno = -res; + 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); + + spa_list_remove(&d->export_metadata.link); + + pw_factory_destroy(d->this); +} + +static void module_registered(void *data) +{ + struct factory_data *d = data; + struct pw_module *module = d->module; + struct pw_factory *factory = d->this; + struct spa_dict_item items[1]; + char id[16]; + int res; + + snprintf(id, sizeof(id), "%d", pw_global_get_id(pw_module_get_global(module))); + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); + pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); + + if ((res = pw_factory_register(factory, NULL)) < 0) { + pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); + } +} + +static const struct pw_module_events module_events = { + PW_VERSION_MODULE_EVENTS, + .destroy = module_destroy, + .registered = module_registered, +}; + +SPA_EXPORT +int pipewire__module_init(struct pw_module *module, const char *args) +{ + struct pw_core *core = pw_module_get_core(module); + struct pw_factory *factory; + struct factory_data *data; + int res; + + if ((res = pw_protocol_native_ext_metadata_init(core)) < 0) + return res; + + factory = pw_factory_new(core, + "metadata", + PW_TYPE_INTERFACE_Metadata, + PW_VERSION_METADATA, + NULL, + sizeof(*data)); + if (factory == NULL) + return -errno; + + data = pw_factory_get_user_data(factory); + data->this = factory; + data->module = module; + + pw_log_debug("module %p: new", module); + + pw_factory_set_implementation(factory, + &impl_factory, + data); + + data->export_metadata.type = PW_TYPE_INTERFACE_Metadata; + data->export_metadata.func = pw_remote_metadata_export; + pw_core_register_export_type(core, &data->export_metadata); + + pw_module_add_listener(module, &data->module_listener, &module_events, data); + + pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); + + return 0; +} diff --git a/src/modules/module-metadata/metadata.c b/src/modules/module-metadata/metadata.c new file mode 100644 index 000000000..e5e0bc00e --- /dev/null +++ b/src/modules/module-metadata/metadata.c @@ -0,0 +1,186 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include "pipewire/private.h" + +#include + +struct impl { + struct pw_global *global; + + struct pw_metadata *metadata; + struct pw_resource *resource; + struct spa_hook resource_listener; +}; + +struct resource_data { + struct impl *impl; + + struct pw_resource *resource; + struct spa_hook resource_listener; + struct spa_hook object_listener; + struct spa_hook metadata_listener; +}; + +static int metadata_set_property(void *object, + uint32_t subject, + const char *key, + const char *type, + const char *value) +{ + struct resource_data *d = object; + struct impl *impl = d->impl; + pw_log_debug("%p", impl->metadata); + pw_metadata_set_property(impl->metadata, subject, key, type, value); + return 0; +} + + +static int metadata_clear(void *object) +{ + struct resource_data *d = object; + struct impl *impl = d->impl; + pw_log_debug("%p", impl->metadata); + pw_metadata_clear(impl->metadata); + return 0; +} + +static const struct pw_metadata_methods metadata_methods = { + PW_VERSION_METADATA_METHODS, + .set_property = metadata_set_property, + .clear = metadata_clear, +}; + +#define pw_metadata_resource(r,m,v,...) \ + pw_resource_call_res(r,struct pw_metadata_events,m,v,__VA_ARGS__) + +#define pw_metadata_resource_property(r,...) \ + pw_metadata_resource(r,property,0,__VA_ARGS__) + +static int metadata_property(void *object, + uint32_t subject, + const char *key, + const char *type, + const char *value) +{ + struct resource_data *d = object; + pw_log_debug("%p", d->resource); + pw_metadata_resource_property(d->resource, subject, key, type, value); + return 0; +} + +static const struct pw_metadata_events metadata_events = { + PW_VERSION_METADATA_EVENTS, + .property = metadata_property, +}; + +static void global_unbind(void *data) +{ + struct resource_data *d = data; + if (d->resource) + spa_hook_remove(&d->metadata_listener); +} + +static const struct pw_resource_events resource_events = { + PW_VERSION_RESOURCE_EVENTS, + .destroy = global_unbind, +}; + +static int +global_bind(void *_data, struct pw_client *client, uint32_t permissions, + uint32_t version, uint32_t id) +{ + struct impl *impl = _data; + struct pw_resource *resource; + struct resource_data *data; + + resource = pw_resource_new(client, id, permissions, PW_TYPE_INTERFACE_Metadata, version, sizeof(*data)); + if (resource == NULL) + return -errno; + + data = pw_resource_get_user_data(resource); + data->impl = impl; + data->resource = resource; + + pw_log_debug("."); +// pw_resource_install_marshal(resource, true); + + /* listen for when the resource goes away */ + pw_resource_add_listener(resource, + &data->resource_listener, + &resource_events, data); + + /* resource methods -> implemention */ + pw_log_debug("."); + pw_resource_add_object_listener(resource, + &data->object_listener, + &metadata_methods, data); + /* implementation events -> resource */ + pw_log_debug(". %p", impl->metadata); + pw_metadata_add_listener(impl->metadata, + &data->metadata_listener, + &metadata_events, data); + pw_log_debug("."); + + return 0; +} + +void * +pw_metadata_new(struct pw_core *core, struct pw_resource *resource, + struct pw_properties *properties) +{ + struct impl *impl; + + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) + return NULL; + + impl = calloc(1, sizeof(*impl)); + if (impl == NULL) { + pw_properties_free(properties); + return NULL; + } + + pw_properties_set(properties, PW_KEY_METADATA_NAME, "default"); + + pw_resource_install_marshal(resource, true); + + impl->global = pw_global_new(core, + PW_TYPE_INTERFACE_Metadata, + PW_VERSION_METADATA, + properties, + global_bind, impl); + if (impl->global == NULL) { + free(impl); + return NULL; + } + impl->resource = resource; + impl->metadata = (struct pw_metadata*)resource; + + pw_global_register(impl->global); + + return impl; +} diff --git a/src/modules/module-metadata/protocol-native.c b/src/modules/module-metadata/protocol-native.c new file mode 100644 index 000000000..e02caa28c --- /dev/null +++ b/src/modules/module-metadata/protocol-native.c @@ -0,0 +1,335 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include + +#include + +#include +#include + +static int metadata_resource_marshal_add_listener(void *object, + struct spa_hook *listener, + const struct pw_metadata_events *events, + void *data) +{ + struct pw_resource *resource = object; + pw_resource_add_object_listener(resource, listener, events, data); + return 0; +} + +static int metadata_proxy_marshal_add_listener(void *object, + struct spa_hook *listener, + const struct pw_metadata_events *events, + void *data) +{ + struct pw_proxy *proxy = object; + pw_proxy_add_object_listener(proxy, listener, events, data); + return 0; +} + +static int metadata_demarshal_add_listener(void *object, + const struct pw_protocol_native_message *msg) +{ + return -ENOTSUP; +} + +static void metadata_marshal_set_property(struct spa_pod_builder *b, uint32_t subject, + const char *key, const char *type, const char *value) +{ + spa_pod_builder_add_struct(b, + SPA_POD_Int(subject), + SPA_POD_String(key), + SPA_POD_String(type), + SPA_POD_String(value)); +} + +static int metadata_proxy_marshal_set_property(void *object, uint32_t subject, + const char *key, const char *type, const char *value) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + b = pw_protocol_native_begin_proxy(proxy, PW_METADATA_METHOD_SET_PROPERTY, NULL); + metadata_marshal_set_property(b, subject, key, type, value); + return pw_protocol_native_end_proxy(proxy, b); +} +static int metadata_resource_marshal_set_property(void *object, uint32_t subject, + const char *key, const char *type, const char *value) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + b = pw_protocol_native_begin_resource(resource, PW_METADATA_METHOD_SET_PROPERTY, NULL); + metadata_marshal_set_property(b, subject, key, type, value); + return pw_protocol_native_end_resource(resource, b); +} + +static int metadata_demarshal_set_property(struct spa_pod_parser *prs, uint32_t *subject, + char **key, char **type, char **value) +{ + return spa_pod_parser_get_struct(prs, + SPA_POD_Int(subject), + SPA_POD_String(key), + SPA_POD_String(type), + SPA_POD_String(value)); +} + +static int metadata_proxy_demarshal_set_property(void *object, + const struct pw_protocol_native_message *msg) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + uint32_t subject; + char *key, *type, *value; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (metadata_demarshal_set_property(&prs, &subject, &key, &type, &value) < 0) + return -EINVAL; + return pw_proxy_notify(proxy, struct pw_metadata_methods, set_property, 0, subject, key, type, value); +} + +static int metadata_resource_demarshal_set_property(void *object, + const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t subject; + char *key, *type, *value; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (metadata_demarshal_set_property(&prs, &subject, &key, &type, &value) < 0) + return -EINVAL; + return pw_resource_notify(resource, struct pw_metadata_methods, set_property, 0, subject, key, type, value); +} + +static void metadata_marshal_clear(struct spa_pod_builder *b) +{ + spa_pod_builder_add_struct(b, SPA_POD_None()); +} + +static int metadata_proxy_marshal_clear(void *object) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + b = pw_protocol_native_begin_proxy(proxy, PW_METADATA_METHOD_CLEAR, NULL); + metadata_marshal_clear(b); + return pw_protocol_native_end_proxy(proxy, b); +} +static int metadata_resource_marshal_clear(void *object) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + b = pw_protocol_native_begin_resource(resource, PW_METADATA_METHOD_CLEAR, NULL); + metadata_marshal_clear(b); + return pw_protocol_native_end_resource(resource, b); +} + +static int metadata_proxy_demarshal_clear(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, SPA_POD_None()) < 0) + return -EINVAL; + pw_proxy_notify(proxy, struct pw_metadata_methods, clear, 0); + return 0; +} + +static int metadata_resource_demarshal_clear(void *object, const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (spa_pod_parser_get_struct(&prs, SPA_POD_None()) < 0) + return -EINVAL; + pw_resource_notify(resource, struct pw_metadata_methods, clear, 0); + return 0; +} + +static void metadata_marshal_property(struct spa_pod_builder *b, uint32_t subject, + const char *key, const char *type, const char *value) +{ + spa_pod_builder_add_struct(b, + SPA_POD_Int(subject), + SPA_POD_String(key), + SPA_POD_String(type), + SPA_POD_String(value)); +} + +static int metadata_proxy_marshal_property(void *object, uint32_t subject, + const char *key, const char *type, const char *value) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + pw_log_debug("."); + b = pw_protocol_native_begin_proxy(proxy, PW_METADATA_EVENT_PROPERTY, NULL); + metadata_marshal_property(b, subject, key, type, value); + return pw_protocol_native_end_proxy(proxy, b); +} + +static int metadata_resource_marshal_property(void *object, uint32_t subject, + const char *key, const char *type, const char *value) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + pw_log_debug("."); + b = pw_protocol_native_begin_resource(resource, PW_METADATA_EVENT_PROPERTY, NULL); + metadata_marshal_property(b, subject, key, type, value); + return pw_protocol_native_end_resource(resource, b); +} + +static int metadata_demarshal_property(struct spa_pod_parser *prs, + uint32_t *subject, char **key, char **type, char **value) +{ + return spa_pod_parser_get_struct(prs, + SPA_POD_Int(subject), + SPA_POD_String(key), + SPA_POD_String(type), + SPA_POD_String(value)); +} + +static int metadata_proxy_demarshal_property(void *object, + const struct pw_protocol_native_message *msg) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + uint32_t subject; + char *key, *type, *value; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (metadata_demarshal_property(&prs, + &subject, &key, &type, &value) < 0) + return -EINVAL; + pw_proxy_notify(proxy, struct pw_metadata_events, property, 0, subject, key, type, value); + return 0; +} + +static int metadata_resource_demarshal_property(void *object, + const struct pw_protocol_native_message *msg) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t subject; + char *key, *type, *value; + + spa_pod_parser_init(&prs, msg->data, msg->size); + if (metadata_demarshal_property(&prs, + &subject, &key, &type, &value) < 0) + return -EINVAL; + pw_resource_notify(resource, struct pw_metadata_events, property, 0, subject, key, type, value); + return 0; +} + +static const struct pw_metadata_methods pw_protocol_native_metadata_client_method_marshal = { + PW_VERSION_METADATA_METHODS, + .add_listener = &metadata_proxy_marshal_add_listener, + .set_property = &metadata_proxy_marshal_set_property, + .clear = &metadata_proxy_marshal_clear, +}; +static const struct pw_metadata_methods pw_protocol_native_metadata_server_method_marshal = { + PW_VERSION_METADATA_METHODS, + .add_listener = &metadata_resource_marshal_add_listener, + .set_property = &metadata_resource_marshal_set_property, + .clear = &metadata_resource_marshal_clear, +}; + +static const struct pw_protocol_native_demarshal +pw_protocol_native_metadata_client_method_demarshal[PW_METADATA_METHOD_NUM] = +{ + [PW_METADATA_METHOD_ADD_LISTENER] = { &metadata_demarshal_add_listener, 0 }, + [PW_METADATA_METHOD_SET_PROPERTY] = { &metadata_proxy_demarshal_set_property, 0 }, + [PW_METADATA_METHOD_CLEAR] = { &metadata_proxy_demarshal_clear, 0 }, +}; + +static const struct pw_protocol_native_demarshal +pw_protocol_native_metadata_server_method_demarshal[PW_METADATA_METHOD_NUM] = +{ + [PW_METADATA_METHOD_ADD_LISTENER] = { &metadata_demarshal_add_listener, 0 }, + [PW_METADATA_METHOD_SET_PROPERTY] = { &metadata_resource_demarshal_set_property, 0 }, + [PW_METADATA_METHOD_CLEAR] = { &metadata_resource_demarshal_clear, 0 }, +}; + +static const struct pw_metadata_events pw_protocol_native_metadata_client_event_marshal = { + PW_VERSION_METADATA_EVENTS, + .property = &metadata_proxy_marshal_property, +}; + +static const struct pw_metadata_events pw_protocol_native_metadata_server_event_marshal = { + PW_VERSION_METADATA_EVENTS, + .property = &metadata_resource_marshal_property, +}; + +static const struct pw_protocol_native_demarshal +pw_protocol_native_metadata_client_event_demarshal[PW_METADATA_EVENT_NUM] = +{ + [PW_METADATA_EVENT_PROPERTY] = { &metadata_proxy_demarshal_property, 0 }, +}; + +static const struct pw_protocol_native_demarshal +pw_protocol_native_metadata_server_event_demarshal[PW_METADATA_EVENT_NUM] = +{ + [PW_METADATA_EVENT_PROPERTY] = { &metadata_resource_demarshal_property, 0 }, +}; + +static const struct pw_protocol_marshal pw_protocol_native_metadata_marshal = { + PW_TYPE_INTERFACE_Metadata, + PW_VERSION_METADATA, + 0, + PW_METADATA_METHOD_NUM, + PW_METADATA_EVENT_NUM, + .client_marshal = &pw_protocol_native_metadata_client_method_marshal, + .server_demarshal = pw_protocol_native_metadata_server_method_demarshal, + .server_marshal = &pw_protocol_native_metadata_server_event_marshal, + .client_demarshal = pw_protocol_native_metadata_client_event_demarshal, +}; + +static const struct pw_protocol_marshal pw_protocol_native_metadata_impl_marshal = { + PW_TYPE_INTERFACE_Metadata, + PW_VERSION_METADATA, + PW_PROTOCOL_MARSHAL_FLAG_IMPL, + PW_METADATA_EVENT_NUM, + PW_METADATA_METHOD_NUM, + .client_marshal = &pw_protocol_native_metadata_client_event_marshal, + .server_demarshal = pw_protocol_native_metadata_server_event_demarshal, + .server_marshal = &pw_protocol_native_metadata_server_method_marshal, + .client_demarshal = pw_protocol_native_metadata_client_method_demarshal, +}; + +int pw_protocol_native_ext_metadata_init(struct pw_core *core) +{ + struct pw_protocol *protocol; + + protocol = pw_core_find_protocol(core, PW_TYPE_INFO_PROTOCOL_Native); + if (protocol == NULL) + return -EPROTO; + + pw_protocol_add_marshal(protocol, &pw_protocol_native_metadata_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_metadata_impl_marshal); + return 0; +} diff --git a/src/modules/module-metadata/proxy-metadata.c b/src/modules/module-metadata/proxy-metadata.c new file mode 100644 index 000000000..ce2247966 --- /dev/null +++ b/src/modules/module-metadata/proxy-metadata.c @@ -0,0 +1,97 @@ +/* PipeWire + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "pipewire/pipewire.h" +#include "pipewire/private.h" +#include "extensions/metadata.h" + +struct object_data { + struct pw_remote *remote; + struct pw_core *core; + + struct pw_metadata *object; + struct spa_hook object_listener; + struct spa_hook object_methods; + + struct pw_proxy *proxy; + struct spa_hook proxy_listener; +}; + +static void object_proxy_destroy(void *_data) +{ + struct object_data *data = _data; + spa_hook_remove(&data->object_listener); +} + +static const struct pw_proxy_events proxy_events = { + PW_VERSION_PROXY_EVENTS, + .destroy = object_proxy_destroy, +}; + +struct pw_proxy *pw_remote_metadata_export(struct pw_remote *remote, + uint32_t type, struct pw_properties *props, void *object, + size_t user_data_size) +{ + struct pw_metadata *meta = object; + struct spa_interface *iface; + struct pw_proxy *proxy; + struct object_data *data; + + proxy = pw_core_proxy_create_object(remote->core_proxy, + "metadata", + PW_TYPE_INTERFACE_Metadata, + PW_VERSION_METADATA, + props ? &props->dict : NULL, + user_data_size + sizeof(struct object_data)); + if (props) + pw_properties_free(props); + if (proxy == NULL) + return NULL; + + data = pw_proxy_get_user_data(proxy); + data = SPA_MEMBER(data, user_data_size, struct object_data); + data->remote = remote; + data->object = object; + data->core = pw_remote_get_core(remote); + data->proxy = proxy; + + iface = (struct spa_interface*)proxy; + + pw_proxy_install_marshal(proxy, true); + + pw_proxy_add_listener(proxy, &data->proxy_listener, &proxy_events, data); + + pw_proxy_add_object_listener(proxy, &data->object_methods, + meta->iface.cb.funcs, meta->iface.cb.data); + pw_metadata_add_listener(meta, &data->object_listener, + iface->cb.funcs, iface->cb.data); + + return proxy; +} diff --git a/src/modules/module-metadata/resource-metadata.c b/src/modules/module-metadata/resource-metadata.c new file mode 100644 index 000000000..9dcab1378 --- /dev/null +++ b/src/modules/module-metadata/resource-metadata.c @@ -0,0 +1,146 @@ +/* PipeWire + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "pipewire/private.h" + +struct impl { + struct pw_core *core; + struct pw_device *device; + struct spa_hook device_listener; + + struct pw_resource *resource; + struct spa_hook resource_listener; + struct spa_hook object_listener; + + unsigned int registered:1; +}; + +static void device_info(void *data, const struct spa_device_info *info) +{ + struct impl *impl = data; + if (!impl->registered) { + pw_device_register(impl->device, NULL); + pw_device_set_implementation(impl->device, + (struct spa_device*)impl->resource); + impl->registered = true; + } +} + +static const struct spa_device_events object_events = { + SPA_VERSION_DEVICE_EVENTS, + .info = device_info, +}; + +static void device_resource_destroy(void *data) +{ + struct impl *impl = data; + + pw_log_debug("client-device %p: destroy", impl); + + impl->resource = NULL; + spa_hook_remove(&impl->device_listener); + spa_hook_remove(&impl->resource_listener); + spa_hook_remove(&impl->object_listener); + pw_device_destroy(impl->device); +} + +static const struct pw_resource_events resource_events = { + PW_VERSION_RESOURCE_EVENTS, + .destroy = device_resource_destroy, +}; + +static void device_destroy(void *data) +{ + struct impl *impl = data; + + pw_log_debug("client-device %p: destroy", impl); + + impl->device = NULL; + spa_hook_remove(&impl->device_listener); + spa_hook_remove(&impl->resource_listener); + spa_hook_remove(&impl->object_listener); + pw_resource_destroy(impl->resource); +} + +static const struct pw_device_events device_events = { + PW_VERSION_DEVICE_EVENTS, + .destroy = device_destroy, +}; + +struct pw_device *pw_client_device_new(struct pw_resource *resource, + struct pw_properties *properties) +{ + struct impl *impl; + struct pw_device *device; + struct pw_client *client = pw_resource_get_client(resource); + struct pw_core *core = pw_client_get_core(client); + + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) + return NULL; + + pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", client->global->id); + + device = pw_device_new(core, properties, sizeof(struct impl)); + if (device == NULL) + return NULL; + + impl = pw_device_get_user_data(device); + impl->device = device; + impl->core = core; + impl->resource = resource; + + pw_resource_install_marshal(resource, true); + + pw_device_add_listener(impl->device, + &impl->device_listener, + &device_events, impl); + + pw_resource_add_listener(impl->resource, + &impl->resource_listener, + &resource_events, + impl); + pw_resource_add_object_listener(impl->resource, + &impl->object_listener, + &object_events, + impl); + + return device; +} diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c index 2fc8c3a83..b319a280e 100644 --- a/src/pipewire/pipewire.c +++ b/src/pipewire/pipewire.c @@ -575,8 +575,9 @@ static const struct spa_type_info type_info[] = { { PW_TYPE_INTERFACE_Link, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Link", NULL }, { PW_TYPE_INTERFACE_Client, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Client", NULL }, { PW_TYPE_INTERFACE_Module, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Module", NULL }, - { PW_TYPE_INTERFACE_ClientNode, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientNode", NULL }, { PW_TYPE_INTERFACE_Device, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Device", NULL }, + { PW_TYPE_INTERFACE_ClientNode, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientNode", NULL }, + { PW_TYPE_INTERFACE_Metadata, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Metadata", NULL }, { SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types }, { 0, 0, NULL, NULL }, }; diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c index e75f80053..40c74c24e 100644 --- a/src/pipewire/remote.c +++ b/src/pipewire/remote.c @@ -242,6 +242,7 @@ struct pw_remote *pw_remote_new(struct pw_core *core, pw_module_load(core, "libpipewire-module-rtkit", NULL, NULL); pw_module_load(core, "libpipewire-module-client-node", NULL, NULL); pw_module_load(core, "libpipewire-module-adapter", NULL, NULL); + pw_module_load(core, "libpipewire-module-metadata", NULL, NULL); spa_list_append(&core->remote_list, &this->link); diff --git a/src/pipewire/type.h b/src/pipewire/type.h index a1b205f75..9fd024dba 100644 --- a/src/pipewire/type.h +++ b/src/pipewire/type.h @@ -48,6 +48,7 @@ enum { /* extensions */ PW_TYPE_INTERFACE_EXTENSIONS = PW_TYPE_INTERFACE_START + 0x1000, PW_TYPE_INTERFACE_ClientNode, + PW_TYPE_INTERFACE_Metadata, };