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