From 9a6e52072122e7643444432e51823250e5f91368 Mon Sep 17 00:00:00 2001 From: Julian Bouzas <julian.bouzas@collabora.com> Date: Mon, 26 Aug 2019 11:22:26 -0400 Subject: [PATCH] bluez: create bluetooth endpoints --- modules/module-pw-alsa-udev.c | 4 ++ modules/module-pw-bluez.c | 112 ++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c index af474eae..ff374007 100644 --- a/modules/module-pw-alsa-udev.c +++ b/modules/module-pw-alsa-udev.c @@ -116,6 +116,10 @@ on_node_added(WpRemotePipewire *rp, guint id, gconstpointer p, gpointer d) if (!name) name = spa_dict_lookup (props, "node.name"); + /* Don't handle bluetooth nodes */ + if (g_str_has_prefix (name, "api.bluez5")) + return; + /* Set the properties */ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", diff --git a/modules/module-pw-bluez.c b/modules/module-pw-bluez.c index 5bca9697..89a77ea9 100644 --- a/modules/module-pw-bluez.c +++ b/modules/module-pw-bluez.c @@ -25,6 +25,7 @@ struct monitor { struct impl { WpModule *module; WpRemotePipewire *remote_pipewire; + GHashTable *registered_endpoints; /* The bluez monitor */ struct monitor monitor; @@ -57,6 +58,107 @@ struct node { struct pw_proxy *proxy; }; +static void +on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d) +{ + struct impl *data = d; + WpEndpoint *endpoint = NULL; + guint global_id = 0; + GError *error = NULL; + + /* Get the endpoint */ + endpoint = wp_endpoint_new_finish(initable, res, NULL); + g_return_if_fail (endpoint); + + /* Check for error */ + if (error) { + g_clear_object (&endpoint); + g_warning ("Failed to create client endpoint: %s", error->message); + return; + } + + /* Get the endpoint global id */ + g_object_get (endpoint, "global-id", &global_id, NULL); + g_debug ("Created bluetooth endpoint for global id %d", global_id); + + /* Register the endpoint and add it to the table */ + wp_endpoint_register (endpoint); + g_hash_table_insert (data->registered_endpoints, GUINT_TO_POINTER(global_id), + endpoint); +} + +static void +on_node_added (WpRemotePipewire *rp, guint id, gconstpointer p, gpointer d) +{ + struct impl *data = d; + const struct spa_dict *props = p; + g_autoptr (WpCore) core = wp_module_get_core (data->module); + const gchar *name, *media_class; + enum pw_direction direction; + GVariantBuilder b; + g_autoptr (GVariant) endpoint_props = NULL; + + /* Make sure the node has properties */ + g_return_if_fail(props); + + /* Get the media_class */ + media_class = spa_dict_lookup(props, "media.class"); + + /* Get the name */ + name = spa_dict_lookup (props, "media.name"); + if (!name) + name = spa_dict_lookup (props, "node.name"); + + /* Only handle bluetooth nodes */ + if (!g_str_has_prefix (name, "api.bluez5")) + return; + + /* Get the direction */ + if (g_str_has_prefix (media_class, "Audio/Sink")) { + direction = PW_DIRECTION_INPUT; + } else if (g_str_has_prefix (media_class, "Audio/Source")) { + direction = PW_DIRECTION_OUTPUT; + } else { + g_critical ("failed to parse direction"); + return; + } + + /* Set the properties */ + g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&b, "{sv}", + "name", name ? + g_variant_new_take_string (g_strdup_printf ("Stream %u (%s)", id, name)) : + g_variant_new_take_string (g_strdup_printf ("Stream %u", id))); + g_variant_builder_add (&b, "{sv}", + "media-class", g_variant_new_string (media_class)); + g_variant_builder_add (&b, "{sv}", + "global-id", g_variant_new_uint32 (id)); + g_variant_builder_add (&b, "{sv}", + "direction", g_variant_new_uint32 (direction)); + endpoint_props = g_variant_builder_end (&b); + + /* Create the endpoint async */ + wp_factory_make (core, "pipewire-simple-endpoint", WP_TYPE_ENDPOINT, + endpoint_props, on_endpoint_created, data); +} + +static void +on_global_removed (WpRemotePipewire *rp, guint id, gpointer d) +{ + struct impl *data = d; + WpEndpoint *endpoint = NULL; + + /* Get the endpoint */ + endpoint = g_hash_table_lookup (data->registered_endpoints, + GUINT_TO_POINTER(id)); + if (!endpoint) + return; + + /* Unregister the endpoint and remove it from the table */ + wp_endpoint_unregister (endpoint); + g_hash_table_remove (data->registered_endpoints, GUINT_TO_POINTER(id)); +} + static struct node * create_node(struct impl *impl, struct device *dev, uint32_t id, const struct spa_device_object_info *info) @@ -343,6 +445,10 @@ module_destroy (gpointer data) impl->module = NULL; impl->remote_pipewire = NULL; + /* Destroy the registered endpoints table */ + g_hash_table_unref(impl->registered_endpoints); + impl->registered_endpoints = NULL; + /* Clean up */ g_slice_free (struct impl, impl); } @@ -365,6 +471,8 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) impl = g_slice_new0(struct impl); impl->module = module; impl->remote_pipewire = rp; + impl->registered_endpoints = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify)g_object_unref); /* Set destroy callback for impl */ wp_module_set_destroy_callback (module, module_destroy, impl); @@ -374,4 +482,8 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) /* Start the monitor when the connected callback is triggered */ g_signal_connect(rp, "state-changed::connected", (GCallback)start_monitor, impl); + + /* Register the global added/removed callbacks */ + g_signal_connect(rp, "global-added::node", (GCallback)on_node_added, impl); + g_signal_connect(rp, "global-removed", (GCallback)on_global_removed, impl); } -- GitLab