From 65b4562fa0e7b5115c55efa819c1034286575484 Mon Sep 17 00:00:00 2001 From: raghu447 Date: Wed, 29 Jul 2020 19:27:40 +0530 Subject: [PATCH] Implement PW_TYPE_INTERFACE_Metadata --- lib/wp/meson.build | 2 + lib/wp/metadata.c | 353 +++++++++++++++++++++++++++++ lib/wp/metadata.h | 45 ++++ lib/wp/wp.c | 1 + lib/wp/wp.h | 1 + modules/meson.build | 11 + modules/module-metadata-settings.c | 65 ++++++ src/config/wireplumber.conf | 3 + 8 files changed, 481 insertions(+) create mode 100644 lib/wp/metadata.c create mode 100644 lib/wp/metadata.h create mode 100644 modules/module-metadata-settings.c diff --git a/lib/wp/meson.build b/lib/wp/meson.build index 12973670..e7a4e56f 100644 --- a/lib/wp/meson.build +++ b/lib/wp/meson.build @@ -10,6 +10,7 @@ wp_lib_sources = files( 'error.c', 'iterator.c', 'link.c', + 'metadata.c', 'module.c', 'node.c', 'object-interest.c', @@ -43,6 +44,7 @@ wp_lib_headers = files( 'error.h', 'iterator.h', 'link.h', + 'metadata.h', 'module.h', 'node.h', 'object-interest.h', diff --git a/lib/wp/metadata.c b/lib/wp/metadata.c new file mode 100644 index 00000000..7077a64d --- /dev/null +++ b/lib/wp/metadata.c @@ -0,0 +1,353 @@ +/* WirePlumber + * + * Copyright © 2019-2020 Collabora Ltd. + * @author Raghavendra Rao + * + * SPDX-License-Identifier: MIT + */ + +/** + * SECTION: WpMetadata + * + * The #WpMetadata class allows accessing the properties and methods of + * Pipewire Jack metadata object (`struct pw_metadata`). + * + */ + +#define G_LOG_DOMAIN "wp-metadata" + +#include "metadata.h" +#include "spa-type.h" +#include "spa-pod.h" +#include "debug.h" +#include "private.h" +#include "error.h" +#include "wpenums.h" + +#include +#include +#include + +#include +#include +#include + +/* WpMetadata */ + +typedef struct _WpMetadataPrivate WpMetadataPrivate; +struct _WpMetadataPrivate +{ + struct pw_metadata *iface; + struct spa_hook listener; + + struct spa_hook_list hooks; + struct pw_properties *properties; + struct pw_array metadata; + struct pw_proxy *proxy; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (WpMetadata, wp_metadata, WP_TYPE_PROXY) + +static void +wp_metadata_init (WpMetadata * self) +{ +} + +static void +wp_metadata_finalize (GObject * object) +{ + G_OBJECT_CLASS (wp_metadata_parent_class)->finalize (object); +} + +static void +wp_metadata_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy) +{ + WpMetadata *self = WP_METADATA (proxy); + WpMetadataPrivate *priv = wp_metadata_get_instance_private (self); + priv->iface = (struct pw_metadata *) pw_proxy; +} + +static void +wp_metadata_class_init (WpMetadataClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + WpProxyClass *proxy_class = (WpProxyClass *) klass; + + object_class->finalize = wp_metadata_finalize; + + proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Metadata; + proxy_class->pw_iface_version = PW_VERSION_METADATA; + + proxy_class->pw_proxy_created = wp_metadata_pw_proxy_created; +} + +/* WpImplMetadata */ + +typedef struct _WpImplMetadata WpImplMetadata; +struct _WpImplMetadata +{ + WpMetadata parent; + + struct spa_interface iface; + struct spa_hook_list hooks; + gboolean subscribed; +}; + +G_DEFINE_TYPE (WpImplMetadata, wp_impl_metadata, WP_TYPE_METADATA) + +#define pw_metadata_emit(hooks,method,version,...) \ + spa_hook_list_call_simple(hooks, struct pw_metadata_events, \ + method, version, ##__VA_ARGS__) + +#define pw_metadata_emit_property(hooks,...) \ + pw_metadata_emit(hooks,property, 0, ##__VA_ARGS__) + +struct item { + uint32_t subject; + char *key; + char *type; + char *value; +}; + +static void +clear_item(struct item *item) +{ + free(item->key); + free(item->type); + free(item->value); + spa_zero(*item); +} + +static void +set_item(struct item *item, uint32_t subject, const char *key, + const char *type, const char *value) +{ + item->subject = subject; + item->key = strdup(key); + item->type = strdup(type); + item->value = strdup(value); +} + +static void +emit_properties(WpImplMetadata *self, + const struct spa_dict *dict) +{ + struct item *item; + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + pw_array_for_each(item, &priv->metadata) { + wp_info_object (self, "metadata : %d %s %s %s", + item->subject, item->key, item->type, item->value); + pw_metadata_emit_property(&priv->hooks, + item->subject, + item->key, + item->type, + item->value); + } +} + +static int +impl_add_listener(void *object, + struct spa_hook *listener, + const struct pw_metadata_events *events, + void *data) +{ + WpImplMetadata *self = WP_IMPL_METADATA (object); + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + struct spa_hook_list save; + + spa_hook_list_isolate (&priv->hooks, &save, listener, events, data); + + emit_properties(self, &priv->properties->dict); + + spa_hook_list_join (&priv->hooks, &save); + return 0; +} + +static struct item * +find_item(WpImplMetadata *self, uint32_t subject, const char *key) +{ + struct item *item; + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + pw_array_for_each(item, &priv->metadata) { + if (item->subject == subject && (key == NULL || + !strcmp(item->key, key))) { + return item; + } + } + return NULL; +} + +static int +clear_subjects(WpImplMetadata *self, uint32_t subject) +{ + struct item *item; + uint32_t removed = 0; + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + while (true) { + item = find_item(self, subject, NULL); + if (item == NULL) + break; + + wp_debug_object (self, "remove id:%d key:%s", subject, item->key); + + clear_item(item); + pw_array_remove(&priv->metadata, item); + removed++; + } + if (removed > 0) + pw_metadata_emit_property(&priv->hooks, subject, NULL, NULL, NULL); + return 0; +} + +static void +clear_items(WpImplMetadata *self) +{ + struct item *item; + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + pw_array_consume(item, &priv->metadata) + clear_subjects(self, item->subject); + pw_array_reset(&priv->metadata); +} + +static int +impl_set_property(void *object, uint32_t subject, const char *key, + const char *type, const char *value) +{ + WpImplMetadata *self = WP_IMPL_METADATA (object); + WpMetadataPrivate *priv; + struct item *item = NULL; + + g_return_val_if_fail (WP_IS_IMPL_METADATA (self), -1); + priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + if (key == NULL) + return clear_subjects(self, subject); + + item = find_item(self, subject, key); + if (item == NULL) { + if (value == NULL) + return 0; + item = pw_array_add(&priv->metadata, sizeof(*item)); + if (item == NULL) + return -errno; + } else { + clear_item(item); + } + + if (value != NULL) { + if (type == NULL) + type = "string"; + set_item(item, subject, key, type, value); + wp_debug_object (self, "%p: add id:%d key:%s type:%s value:%s", self, + subject, key, type, value); + } else { + type = NULL; + pw_array_remove(&priv->metadata, item); + wp_debug_object(self, "%p: remove id:%d key:%s", self, subject, key); + } + + pw_metadata_emit_property(&priv->hooks, subject, key, type, value); + + return 0; +} + +static int +impl_clear(void *object) +{ + WpImplMetadata *self = WP_IMPL_METADATA (object); + + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + clear_items(self); + pw_array_clear(&priv->metadata); + pw_properties_free(priv->properties); + return 0; +} + +static const struct pw_metadata_methods impl_metadata = { + PW_VERSION_METADATA_METHODS, + .add_listener = impl_add_listener, + .set_property = impl_set_property, + .clear = impl_clear, +}; + +static void +wp_impl_metadata_init (WpImplMetadata * self) +{ + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + self->iface = SPA_INTERFACE_INIT ( + PW_TYPE_INTERFACE_Metadata, + PW_VERSION_METADATA, + &impl_metadata, self); + spa_hook_list_init (&priv->hooks); + + priv->iface = (struct pw_metadata *) &self->iface; + + priv->properties = pw_properties_new(NULL, NULL); + pw_array_init(&priv->metadata, 4096); +} + +static void +wp_impl_metadata_finalize (GObject * object) +{ + G_OBJECT_CLASS (wp_impl_metadata_parent_class)->finalize (object); +} + +static void +wp_impl_metadata_augment (WpProxy * proxy, WpProxyFeatures features) +{ + WpImplMetadata *self = WP_IMPL_METADATA (proxy); + WpMetadataPrivate *priv = wp_metadata_get_instance_private (WP_METADATA (self)); + + /* PW_PROXY depends on BOUND */ + if (features & WP_PROXY_FEATURE_PW_PROXY) + features |= WP_PROXY_FEATURE_BOUND; + + if (features & WP_PROXY_FEATURE_BOUND) { + g_autoptr (WpCore) core = wp_proxy_get_core (proxy); + struct pw_core *pw_core = wp_core_get_pw_core (core); + + /* no pw_core -> we are not connected */ + if (!pw_core) { + wp_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_OPERATION_FAILED, + "The WirePlumber core is not connected; " + "object cannot be exported to PipeWire")); + wp_critical(G_LOG_DOMAIN "metadata : FAIL - Exiting %s",__FUNCTION__); + return; + } + + wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core, + PW_TYPE_INTERFACE_Metadata, + &priv->properties->dict, + priv->iface, 0)); + } +} + +static void +wp_impl_metadata_class_init (WpImplMetadataClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + WpProxyClass *proxy_class = (WpProxyClass *) klass; + + object_class->finalize = wp_impl_metadata_finalize; + + proxy_class->augment = wp_impl_metadata_augment; + proxy_class->enum_params = NULL; + proxy_class->subscribe_params = NULL; + proxy_class->pw_proxy_created = NULL; +} + +WpImplMetadata * +wp_impl_metadata_new (WpCore * core) +{ + g_return_val_if_fail (WP_IS_CORE (core), NULL); + + return g_object_new (WP_TYPE_IMPL_METADATA, + "core", core, + NULL); +} diff --git a/lib/wp/metadata.h b/lib/wp/metadata.h new file mode 100644 index 00000000..f91d34ec --- /dev/null +++ b/lib/wp/metadata.h @@ -0,0 +1,45 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author Raghavendra Rao + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_METADATA_H__ +#define __WIREPLUMBER_METADATA_H__ + +#include "proxy.h" + +G_BEGIN_DECLS + +#define WP_METADATA_FEATURES_STANDARD \ + (WP_PROXY_FEATURES_STANDARD) +/** + * WP_TYPE_METADATA: + * + * The #WpMetadata #GType + */ +#define WP_TYPE_METADATA (wp_metadata_get_type ()) +WP_API +G_DECLARE_DERIVABLE_TYPE (WpMetadata, wp_metadata, WP, METADATA, WpProxy) + +struct _WpMetadataClass +{ + WpProxyClass parent_class; +}; + + +/** + * WP_TYPE_IMPL_MEATADATA: + * + * The #WpImplMetadata #GType + */ +#define WP_TYPE_IMPL_METADATA (wp_impl_metadata_get_type ()) +WP_API +G_DECLARE_FINAL_TYPE (WpImplMetadata, wp_impl_metadata, WP, IMPL_METADATA, WpMetadata) +WP_API +WpImplMetadata * wp_impl_metadata_new (WpCore * core); +G_END_DECLS + +#endif diff --git a/lib/wp/wp.c b/lib/wp/wp.c index 48100d66..30a3d91f 100644 --- a/lib/wp/wp.c +++ b/lib/wp/wp.c @@ -80,6 +80,7 @@ wp_init (WpInitFlags flags) g_type_ensure (WP_TYPE_ENDPOINT_LINK); g_type_ensure (WP_TYPE_ENDPOINT_STREAM); g_type_ensure (WP_TYPE_LINK); + g_type_ensure (WP_TYPE_METADATA); g_type_ensure (WP_TYPE_NODE); g_type_ensure (WP_TYPE_PORT); g_type_ensure (WP_TYPE_SESSION); diff --git a/lib/wp/wp.h b/lib/wp/wp.h index d0277231..519f11b8 100644 --- a/lib/wp/wp.h +++ b/lib/wp/wp.h @@ -20,6 +20,7 @@ #include "error.h" #include "iterator.h" #include "link.h" +#include "metadata.h" #include "module.h" #include "node.h" #include "object-interest.h" diff --git a/modules/meson.build b/modules/meson.build index 0ca36893..80bcf92a 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -20,6 +20,17 @@ reserve_device_interface_src = gnome.gdbus_codegen('reserve-device-interface', namespace : 'Wp' ) +shared_library( + 'wireplumber-module-metadata-settings', + [ + 'module-metadata-settings.c', + ], + c_args : [common_c_args, '-DG_LOG_DOMAIN="m-metadata"'], + install : true, + install_dir : wireplumber_module_dir, + dependencies : [wp_dep, pipewire_dep], +) + shared_library( 'wireplumber-module-monitor', [ diff --git a/modules/module-metadata-settings.c b/modules/module-metadata-settings.c new file mode 100644 index 00000000..80f80db2 --- /dev/null +++ b/modules/module-metadata-settings.c @@ -0,0 +1,65 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author Raghavendra Rao + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +struct _WpMetadataSettings +{ + WpPlugin parent; + WpImplMetadata *metadata; +}; + +G_DECLARE_FINAL_TYPE (WpMetadataSettings, wp_metadata_settings, + WP, METADATA_SETTINGS, WpPlugin) +G_DEFINE_TYPE (WpMetadataSettings, wp_metadata_settings, WP_TYPE_PLUGIN) + +static void +wp_metadata_settings_init (WpMetadataSettings * self) +{ +} + +static void +wp_metadata_settings_activate (WpPlugin * plugin) +{ + WpMetadataSettings * self = WP_METADATA_SETTINGS (plugin); + g_autoptr (WpCore) core = wp_plugin_get_core (plugin); + + g_return_if_fail (core); + + self->metadata = wp_impl_metadata_new(core); + + wp_proxy_augment (WP_PROXY(self->metadata), + WP_METADATA_FEATURES_STANDARD, NULL, + NULL, self); +} + +static void +wp_metadata_settings_deactivate (WpPlugin * plugin) +{ + WpMetadataSettings * self = WP_METADATA_SETTINGS (plugin); + + g_clear_object (&self->metadata); +} + +static void +wp_metadata_settings_class_init (WpMetadataSettingsClass * klass) +{ + WpPluginClass *plugin_class = (WpPluginClass *) klass; + + plugin_class->activate = wp_metadata_settings_activate; + plugin_class->deactivate = wp_metadata_settings_deactivate; +} + +WP_PLUGIN_EXPORT void +wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) +{ + wp_plugin_register (g_object_new (wp_metadata_settings_get_type (), + "module", module, + NULL)); +} \ No newline at end of file diff --git a/src/config/wireplumber.conf b/src/config/wireplumber.conf index fc61dbad..b14d9e77 100644 --- a/src/config/wireplumber.conf +++ b/src/config/wireplumber.conf @@ -73,3 +73,6 @@ load-module C libwireplumber-module-config-endpoint # Implements linking clients to devices based on TOML configuration files load-module C libwireplumber-module-config-policy + +# Activates metadata module +load-module C libwireplumber-module-metadata-settings