From 23cc1edc283d051350732ed788733b4755550f02 Mon Sep 17 00:00:00 2001
From: Julian Bouzas <julian.bouzas@collabora.com>
Date: Wed, 28 Aug 2019 11:04:14 -0400
Subject: [PATCH] alsa-udev: cleaned module and set media class to
 Alsa/<direction>

---
 modules/module-pw-alsa-udev.c  | 155 +++++++++++++++++++++++----------
 modules/module-simple-policy.c |  10 +--
 2 files changed, 116 insertions(+), 49 deletions(-)

diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c
index 48cd8d25..6c499495 100644
--- a/modules/module-pw-alsa-udev.c
+++ b/modules/module-pw-alsa-udev.c
@@ -11,6 +11,7 @@
  * and automatically creates endpoints for all alsa device nodes that appear
  */
 
+#include <spa/utils/keys.h>
 #include <spa/utils/names.h>
 #include <spa/monitor/monitor.h>
 #include <pipewire/pipewire.h>
@@ -57,7 +58,6 @@ struct node {
   struct pw_properties *props;
 
   struct pw_proxy *proxy;
-  struct spa_node *node;
 };
 
 static void
@@ -89,6 +89,89 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
       endpoint);
 }
 
+static gboolean
+parse_alsa_properties (const struct spa_dict *props, const gchar **name,
+    const gchar **media_class, enum pw_direction *direction)
+{
+  const char *local_name = NULL;
+  const char *local_media_class = NULL;
+  enum pw_direction local_direction;
+
+  /* Get the name */
+  local_name = spa_dict_lookup (props, "node.name");
+  if (!local_name)
+    return FALSE;
+
+  /* Get the media class */
+  local_media_class = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS);
+  if (!local_media_class)
+    return FALSE;
+
+  /* Get the direction */
+  if (g_str_has_prefix (local_media_class, "Audio/Sink"))
+    local_direction = PW_DIRECTION_INPUT;
+  else if (g_str_has_prefix (local_media_class, "Audio/Source"))
+    local_direction = PW_DIRECTION_OUTPUT;
+  else
+    return FALSE;
+
+  /* Set the name */
+  if (name)
+    *name = local_name;
+
+  /* Set the media class */
+  if (media_class) {
+    switch (local_direction) {
+    case PW_DIRECTION_INPUT:
+      *media_class = "Alsa/Sink";
+      break;
+    case PW_DIRECTION_OUTPUT:
+      *media_class = "Alsa/Source";
+      break;
+    default:
+      break;
+    }
+  }
+
+  /* Set the direction */
+  if (direction)
+    *direction = local_direction;
+
+  return TRUE;
+}
+
+/* TODO: we need to find a better way to do this */
+static gboolean
+is_alsa_device (const struct spa_dict *props)
+{
+  const gchar *name = NULL;
+  const gchar *media_class = NULL;
+
+  /* Get the name */
+  name = spa_dict_lookup (props, "node.name");
+  if (!name)
+    return FALSE;
+
+  /* Get the media class */
+  media_class = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS);
+  if (!media_class)
+    return FALSE;
+
+  /* Check if it is an audio device */
+  if (!g_str_has_prefix (media_class, "Audio/"))
+    return FALSE;
+
+  /* Check it is not a convert */
+  if (g_str_has_prefix (media_class, "Audio/Convert"))
+    return FALSE;
+
+  /* Check if it is not a bluez device */
+  if (g_str_has_prefix (name, "api.bluez5"))
+    return FALSE;
+
+  return TRUE;
+}
+
 static void
 on_node_added(WpRemotePipewire *rp, guint id, gconstpointer p, gpointer d)
 {
@@ -100,41 +183,22 @@ on_node_added(WpRemotePipewire *rp, guint id, gconstpointer p, gpointer d)
   GVariantBuilder b;
   g_autoptr (GVariant) endpoint_props = NULL;
 
-  /* Make sure the node has properties */
+  /* Only handle alsa nodes */
   g_return_if_fail(props);
-
-  /* Get the media_class */
-  media_class = spa_dict_lookup(props, "media.class");
-
-  /* Make sure the media class is non-convert audio */
-  if (!g_str_has_prefix (media_class, "Audio/"))
-    return;
-  if (g_str_has_prefix (media_class, "Audio/Convert"))
-    return;
-
-  /* Get the name */
-  name = spa_dict_lookup (props, "media.name");
-  if (!name)
-    name = spa_dict_lookup (props, "node.name");
-
-  /* Don't handle bluetooth nodes */
-  if (g_str_has_prefix (name, "api.bluez5"))
+  if (!is_alsa_device (props))
     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");
+  /* Parse the alsa properties */
+  if (!parse_alsa_properties (props, &name, &media_class, &direction)) {
+    g_critical ("failed to parse alsa properties");
     return;
   }
 
   /* Set the properties */
   g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
   g_variant_builder_add (&b, "{sv}",
-      "name", g_variant_new_string (name));
+      "name", g_variant_new_take_string (
+          g_strdup_printf ("Alsa %u (%s)", id, name)));
   g_variant_builder_add (&b, "{sv}",
       "media-class", g_variant_new_string (media_class));
   g_variant_builder_add (&b, "{sv}",
@@ -172,36 +236,39 @@ create_node(struct impl *impl, struct device *dev, uint32_t id,
     const struct spa_device_object_info *info)
 {
   struct node *node;
-  const char *str;
+  const char *name;
+  struct pw_properties *props = NULL;
 
   /* Check if the type is a node */
   if (info->type != SPA_TYPE_INTERFACE_Node)
     return NULL;
 
+  /* Get the alsa name */
+  name = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK);
+  if (name == NULL)
+    name = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME);
+  if (name == NULL)
+    name = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS);
+  if (name == NULL)
+    name = "alsa-device";
+
+  /* Create the properties */
+  props = pw_properties_new_dict(&dev->props->dict);
+  pw_properties_update(props, info->props);
+  pw_properties_set(props, PW_KEY_NODE_NAME, name);
+  pw_properties_set(props, PW_KEY_FACTORY_NAME, info->factory_name);
+  pw_properties_set(props, "merger.monitor", "1");
+
   /* Create the node */
   node = g_slice_new0(struct node);
-
-  /* Set the node properties */
-  node->props = pw_properties_copy(dev->props);
-  pw_properties_update(node->props, info->props);
-  str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK);
-  if (str == NULL)
-    str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME);
-  if (str == NULL)
-    str = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS);
-  if (str == NULL)
-    str = "alsa-device";
-  pw_properties_set(node->props, PW_KEY_NODE_NAME, str);
-  pw_properties_set(node->props, "factory.name", info->factory_name);
-  pw_properties_set(node->props, "merger.monitor", "1");
-
-  /* Set the node info */
   node->impl = impl;
   node->device = dev;
   node->id = id;
+  node->props = props;
   node->proxy = wp_remote_pipewire_create_object(impl->remote_pipewire,
       "adapter", PW_TYPE_INTERFACE_Node, &node->props->dict);
   if (!node->proxy) {
+    pw_properties_free(props);
     g_slice_free (struct node, node);
     return NULL;
   }
diff --git a/modules/module-simple-policy.c b/modules/module-simple-policy.c
index a11ffca9..86f26e67 100644
--- a/modules/module-simple-policy.c
+++ b/modules/module-simple-policy.c
@@ -136,10 +136,10 @@ select_new_endpoint (WpSimplePolicy *self)
 
   if (!self->selected[DIRECTION_SINK]) {
     direction = DIRECTION_SINK;
-    media_class = "Audio/Sink";
+    media_class = "Alsa/Sink";
   } else if (!self->selected[DIRECTION_SOURCE]) {
     direction = DIRECTION_SOURCE;
-    media_class = "Audio/Source";
+    media_class = "Alsa/Source";
   } else
     return G_SOURCE_REMOVE;
 
@@ -172,8 +172,8 @@ simple_policy_endpoint_added (WpPolicy *policy, WpEndpoint *ep)
   guint32 control_id;
   gint direction;
 
-  /* we only care about audio device endpoints here */
-  if (!g_str_has_prefix (media_class, "Audio/"))
+  /* we only care about alsa device endpoints here */
+  if (!g_str_has_prefix (media_class, "Alsa/"))
     return;
 
   /* verify it has the "selected" control available */
@@ -282,7 +282,7 @@ handle_client (WpPolicy *policy, WpEndpoint *ep)
   g_variant_dict_init (&d, NULL);
   g_variant_dict_insert (&d, "action", "s", "link");
   g_variant_dict_insert (&d, "media.class", "s",
-      is_capture ? "Audio/Source" : "Audio/Sink");
+      is_capture ? "Alsa/Source" : "Alsa/Sink");
 
   g_object_get (ep, "role", &role, NULL);
   if (role)
-- 
GitLab