From 2c76103c30ba2794f2596cb6f7c87a755a6e34b6 Mon Sep 17 00:00:00 2001
From: Julian Bouzas <julian.bouzas@collabora.com>
Date: Mon, 17 Jun 2019 10:42:59 -0400
Subject: [PATCH] modules: use the new proxy API

---
 modules/module-pipewire/port.h             |  30 --
 modules/module-pipewire/simple-endpoint.c  |   2 -
 modules/module-pw-alsa-udev.c              | 329 +++++++++-----------
 modules/module-pw-audio-softdsp-endpoint.c | 335 +++++++++++----------
 4 files changed, 317 insertions(+), 379 deletions(-)
 delete mode 100644 modules/module-pipewire/port.h

diff --git a/modules/module-pipewire/port.h b/modules/module-pipewire/port.h
deleted file mode 100644
index 5fcf98bf..00000000
--- a/modules/module-pipewire/port.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* WirePlumber
- *
- * Copyright © 2019 Collabora Ltd.
- *    @author Julian Bouzas <julian.bouzas@collabora.com>
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <wp/wp.h>
-#include <pipewire/pipewire.h>
-#include <spa/param/audio/format-utils.h>
-
-struct _WpPort
-{
-  struct spa_list l;
-
-  /* Port proxy and listener */
-  struct pw_proxy *proxy;
-  struct spa_hook listener;
-
-  /* Port info */
-  uint32_t id;
-  uint32_t parent_id;
-  enum pw_direction direction;
-  uint32_t media_type;
-  uint32_t media_subtype;
-  struct pw_port_info *info;
-  struct spa_audio_info_raw format;
-};
-typedef struct _WpPort WpPort;
\ No newline at end of file
diff --git a/modules/module-pipewire/simple-endpoint.c b/modules/module-pipewire/simple-endpoint.c
index e971e896..fb256815 100644
--- a/modules/module-pipewire/simple-endpoint.c
+++ b/modules/module-pipewire/simple-endpoint.c
@@ -18,8 +18,6 @@
 #include <spa/pod/parser.h>
 #include <spa/param/props.h>
 
-#include "port.h"
-
 struct _WpPipewireSimpleEndpoint
 {
   WpEndpoint parent;
diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c
index 023ee084..df0ce669 100644
--- a/modules/module-pw-alsa-udev.c
+++ b/modules/module-pw-alsa-udev.c
@@ -15,153 +15,155 @@
 #include <pipewire/pipewire.h>
 #include <spa/param/audio/format-utils.h>
 
-#include "module-pipewire/port.h"
-
-typedef void (*WpDoneCallback)(gpointer);
-
-struct done_data {
-  WpDoneCallback callback;
-  gpointer data;
-  GDestroyNotify data_destroy;
-};
-
-struct impl {
-  WpCore *wp_core;
+struct impl
+{
+  WpCore *core;
 
   /* Remote */
   struct pw_remote *remote;
   struct spa_hook remote_listener;
 
-  /* Core */
-  struct pw_core_proxy *core_proxy;
-  struct spa_hook core_listener;
-  int core_seq;
-  GQueue *done_queue;
-
   /* Registry */
   struct pw_registry_proxy *registry_proxy;
   struct spa_hook registry_listener;
 
-  /* Ports */
-  struct spa_list port_list;
+  /* The alsa node proxies */
+  GHashTable *alsa_nodes_info;
 };
 
-struct endpoint_info {
-  struct impl *impl;
-  uint32_t id;
-  uint32_t parent_id;
+struct endpoint_info
+{
   gchar *name;
   gchar *media_class;
 };
 
-static void endpoint_info_destroy(gpointer p) {
+struct proxy_info
+{
+  const struct impl *impl;
+  uint32_t node_id;
+  WpProxyPort *proxy_port;
+};
+
+static void
+endpoint_info_destroy(gpointer p)
+{
   struct endpoint_info *ei = p;
+
+  /* Free the name */
   if (ei->name) {
     g_free (ei->name);
     ei->name = NULL;
   }
+
+  /* Free the media class */
   if (ei->media_class) {
     g_free (ei->media_class);
     ei->media_class = NULL;
   }
-  g_slice_free (struct endpoint_info, p);
-}
 
-static void done_data_destroy(gpointer p) {
-  struct done_data *dd = p;
-  if (dd->data_destroy) {
-    dd->data_destroy(dd->data);
-    dd->data = NULL;
-  }
-  g_slice_free (struct done_data, dd);
+  /* Clean up */
+  g_slice_free (struct endpoint_info, p);
 }
 
-static void sync_core_with_callabck(struct impl* impl,
-    WpDoneCallback callback, gpointer data, GDestroyNotify data_destroy) {
-  struct done_data *dd = g_new0(struct done_data, 1);
-
-  /* Set the data */
-  dd->callback = callback;
-  dd->data = data;
-  dd->data_destroy = data_destroy;
+static void
+proxy_info_destroy(gpointer p)
+{
+  struct proxy_info *pi = p;
 
-  /* Add the data to the queue */
-  g_queue_push_tail (impl->done_queue, dd);
+  /* Unref the proxy port */
+  if (pi->proxy_port) {
+    g_object_unref (pi->proxy_port);
+    pi->proxy_port = NULL;
+  }
 
-  /* Sync the core */
-  impl->core_seq = pw_core_proxy_sync(impl->core_proxy, 0, impl->core_seq);
+  /* Clean up */
+  g_slice_free (struct proxy_info, p);
 }
 
-static void create_endpoint(gpointer p) {
-  struct endpoint_info *ei = p;
-  struct spa_proxy *proxy = NULL;
+static void
+proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  struct proxy_info *pi = data;
+  const struct impl *impl = pi->impl;
+  WpProxyNode *proxy_node = NULL;
+  struct endpoint_info *ei = NULL;
   GVariantBuilder b;
   g_autoptr(GVariant) endpoint_props = NULL;
   WpEndpoint *endpoint = NULL;
 
-  /* Make sure the endpoint info is valid */
-  if (!ei)
+  /* Get the proxy */
+  proxy_node = wp_proxy_node_new_finish(initable, res, NULL);
+  if (!proxy_node)
     return;
 
-  /* Register the proxy */
-  proxy = pw_registry_proxy_bind (ei->impl->registry_proxy,
-      ei->id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0);
+  /* Register the proxy node */
+  wp_proxy_register(WP_PROXY(proxy_node));
+  
+  /* Get the alsa node info */
+  ei = g_hash_table_lookup(impl->alsa_nodes_info, GINT_TO_POINTER(pi->node_id));
+  if (!data)
+    return;
 
-  /* Build the GVariant properties for the endpoint */
+  /* Build the properties for the endpoint */
   g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
   g_variant_builder_add (&b, "{sv}", "name",
-      g_variant_new_take_string (g_strdup_printf ("Endpoint %u: %s", ei->id,
-      ei->name)));
+      g_variant_new_take_string (g_strdup_printf ("Endpoint %u: %s",
+          pi->node_id, ei->name)));
   g_variant_builder_add (&b, "{sv}",
       "media-class", g_variant_new_string (ei->media_class));
   g_variant_builder_add (&b, "{sv}",
-      "node-proxy", g_variant_new_uint64 ((guint64) proxy));
+      "proxy-node", g_variant_new_uint64 ((guint64) proxy_node));
   g_variant_builder_add (&b, "{sv}",
-      "port-list", g_variant_new_uint64 ((guint64) &ei->impl->port_list));
+      "proxy-port", g_variant_new_uint64 ((guint64)
+          g_object_ref(pi->proxy_port)));
   endpoint_props = g_variant_builder_end (&b);
 
   /* Create and register the endpoint */
-  endpoint = wp_factory_make (ei->impl->wp_core, "pw-audio-softdsp-endpoint",
+  endpoint = wp_factory_make (impl->core, "pw-audio-softdsp-endpoint",
       WP_TYPE_ENDPOINT, endpoint_props);
-  wp_endpoint_register (endpoint, ei->impl->wp_core);
-}
 
-static void enum_format_and_create_endpoint(gpointer p) {
-  struct endpoint_info *ei = p, *ei_copy = NULL;
-  WpPort *port = NULL;
+  /* Register the endpoint */
+  wp_endpoint_register (endpoint, impl->core);
 
-  /* Make sure the endpoint info is valid */
-  if (!ei)
-    return;
+  /* Clean up */
+  proxy_info_destroy (pi);
+}
 
-  /* Find the unique alsa port */
-  spa_list_for_each(port, &ei->impl->port_list, l) {
-    if (port->parent_id == ei->id)
-      break;
-  }
+static void
+proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data)
+{
+  struct proxy_info *pi = data;
+  const struct impl *impl = pi->impl;
+  WpProxyPort *proxy_port = NULL;
+  struct pw_proxy *proxy = NULL;
+
+  /* Get the proxy port */
+  proxy_port = wp_proxy_port_new_finish(initable, res, NULL);
+  if (!proxy_port)
+    return;
 
-  /* Emit the port EnumFormat */
-  pw_port_proxy_enum_params((struct pw_port_proxy*)port->proxy, 0,
-          SPA_PARAM_EnumFormat, 0, -1, NULL);
+  /* Register the proxy port */
+  wp_proxy_register(WP_PROXY(proxy_port));
 
-  /* Copy endpoint info */
-  ei_copy = g_new0(struct endpoint_info, 1);
-  ei_copy->impl = ei->impl;
-  ei_copy->id = ei->id;
-  ei_copy->name = g_strdup(ei->name);
-  ei_copy->media_class = g_strdup(ei->media_class);
+  /* Forward the proxy port */
+  pi->proxy_port = proxy_port;
+  
+  /* Get the node proxy */
+  proxy = pw_registry_proxy_bind (impl->registry_proxy, pi->node_id,
+      PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0);
+  if (!proxy)
+    return;
 
-  /* Forward the endpoint creation until the port EnumFormat is emitted */
-  sync_core_with_callabck(ei->impl, create_endpoint, ei_copy,
-      endpoint_info_destroy);
+  /* Create the proxy node asynchronically */
+  wp_proxy_node_new(impl->core, proxy, proxy_node_created, pi);
 }
 
 static void
 handle_node(struct impl *impl, uint32_t id, uint32_t parent_id,
             const struct spa_dict *props)
 {
-  struct endpoint_info *ei = NULL;
   const gchar *media_class = NULL, *name = NULL;
+  struct endpoint_info *ei = NULL;
 
   /* Make sure the node has properties */
   if (!props) {
@@ -181,93 +183,46 @@ handle_node(struct impl *impl, uint32_t id, uint32_t parent_id,
 
   /* Create the endpoint info */
   ei = g_new0(struct endpoint_info, 1);
-  ei->impl = impl;
-  ei->id = id;
   ei->name = g_strdup(name);
   ei->media_class = g_strdup(media_class);
 
-  /* Delay the creation of the endpoint until all ports have been created */
-  sync_core_with_callabck(impl, enum_format_and_create_endpoint, ei,
-      endpoint_info_destroy);
-}
-
-static void port_event_info(void *data, const struct pw_port_info *info)
-{
-  WpPort *port = data;
-  port->info  = pw_port_info_update(port->info, info);
-}
-
-static void port_event_param(void *data, int seq, uint32_t id, uint32_t index,
-  uint32_t next, const struct spa_pod *param)
-{
-  WpPort *port = data;
-
-  /* Only handle EnumFormat */
-  if (id != SPA_PARAM_EnumFormat)
-    return;
-
-  /* Parse the format */
-  if (spa_format_parse(param, &port->media_type, &port->media_subtype) < 0)
-    return;
-
-  /* Only handle RAW audio types */
-  if (port->media_type != SPA_MEDIA_TYPE_audio ||
-      port->media_subtype != SPA_MEDIA_SUBTYPE_raw)
-    return;
-
-  /* Parse the raw audio format */
-  spa_pod_fixate((struct spa_pod*)param);
-  spa_format_audio_raw_parse(param, &port->format);
+  /* Insert the alsa node info in the hash table */
+  g_hash_table_insert(impl->alsa_nodes_info, GINT_TO_POINTER (id), ei);
 }
 
-static const struct pw_port_proxy_events port_events = {
-  PW_VERSION_PORT_PROXY_EVENTS,
-  .info = port_event_info,
-  .param = port_event_param,
-};
-
 static void
 handle_port(struct impl *impl, uint32_t id, uint32_t parent_id,
             const struct spa_dict *props)
 {
-  struct pw_proxy *proxy;
-  WpPort *port;
-  const char *direction_prop;
+  struct proxy_info *pi = NULL;
+  struct pw_proxy *proxy = NULL;
 
+  /* Only handle ports whose parent is an alsa node */
+  if (!g_hash_table_contains(impl->alsa_nodes_info, GINT_TO_POINTER (parent_id)))
+    return;
+  
   /* Make sure the port has porperties */
   if (!props)
     return;
 
-  /* Get the direction property */
-  direction_prop = spa_dict_lookup(props, "port.direction");
-  if (!direction_prop)
-    return;
-
-  /* Get the proxy */
+  /* Get the port proxy */
   proxy = pw_registry_proxy_bind (impl->registry_proxy, id,
-      PW_TYPE_INTERFACE_Port, PW_VERSION_NODE, sizeof(WpPort));
+      PW_TYPE_INTERFACE_Port, PW_VERSION_PORT, 0);
   if (!proxy)
     return;
 
-  /* Get the port */
-  port = pw_proxy_get_user_data(proxy);
-
-  /* Set the info */
-  port->id = id;
-  port->parent_id = parent_id;
-  port->direction =
-      !strcmp(direction_prop, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
-
-  /* Set the proxy and listener */
-  port->proxy = proxy;
-  pw_proxy_add_proxy_listener(proxy, &port->listener, &port_events, port);
+  /* Create the port info */
+  pi = g_new0(struct proxy_info, 1);
+  pi->impl = impl;
+  pi->node_id = parent_id;
+  pi->proxy_port = NULL;
 
-  /* Add the port to the list */
-  spa_list_append(&impl->port_list, &port->l);
+  /* Create the proxy port asynchronically */
+  wp_proxy_port_new(impl->core, proxy, proxy_port_created, pi);
 }
 
 static void
-registry_global(void *data,uint32_t id, uint32_t parent_id,
+registry_global(void *data, uint32_t id, uint32_t parent_id,
 		uint32_t permissions, uint32_t type, uint32_t version,
 		const struct spa_dict *props)
 {
@@ -292,35 +247,16 @@ static const struct pw_registry_proxy_events registry_events = {
   .global = registry_global,
 };
 
-static void core_done(void *d, uint32_t id, int seq)
-{
-  struct impl * impl = d;
-  struct done_data * dd = NULL;
-
-  /* Process all the done_data queue */
-  while ((dd = g_queue_pop_head(impl->done_queue))) {
-    if (dd->callback)
-      dd->callback(dd->data);
-    done_data_destroy(dd);
-  }
-}
-
-static const struct pw_core_proxy_events core_events = {
-  PW_VERSION_CORE_EVENTS,
-  .done = core_done
-};
-
 static void on_state_changed(void *_data, enum pw_remote_state old,
     enum pw_remote_state state, const char *error)
 {
   struct impl *impl = _data;
+  struct pw_core_proxy *core_proxy = NULL;
 
   switch (state) {
   case PW_REMOTE_STATE_CONNECTED:
-    impl->core_proxy = pw_remote_get_core_proxy (impl->remote);
-    pw_core_proxy_add_listener(impl->core_proxy, &impl->core_listener,
-        &core_events, impl);
-    impl->registry_proxy = pw_core_proxy_get_registry (impl->core_proxy,
+    core_proxy = pw_remote_get_core_proxy (impl->remote);
+    impl->registry_proxy = pw_core_proxy_get_registry (core_proxy,
         PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
     pw_registry_proxy_add_listener(impl->registry_proxy,
         &impl->registry_listener, &registry_events, impl);
@@ -340,29 +276,46 @@ static void
 module_destroy (gpointer data)
 {
   struct impl *impl = data;
-  g_queue_free_full(impl->done_queue, done_data_destroy);
+
+  /* Destroy the hash table */
+  if (impl->alsa_nodes_info) {
+    g_hash_table_destroy(impl->alsa_nodes_info);
+    impl->alsa_nodes_info = NULL;
+  }
+
+  /* Clean up */
   g_slice_free (struct impl, impl);
 }
 
+struct impl *
+module_create (WpCore * core) {
+  /* Allocate impl */
+  struct impl *impl = g_new0(struct impl, 1);
+
+  /* Set core */
+  impl->core = core;
+
+  /* Set remote */
+  impl->remote = wp_core_get_global(core, WP_GLOBAL_PW_REMOTE);
+
+  /* Create the hash table */
+  impl->alsa_nodes_info = g_hash_table_new_full (g_direct_hash,
+      g_direct_equal, NULL, endpoint_info_destroy);
+
+  /* Add the remote listener */
+  pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events,
+      impl);
+
+  /* Return the module */
+  return impl;
+}
+
 void
 wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
 {
-  /* This needs to create the alsa sink and alsa source nodes, but since
-   * this is already implemented in the alsa-module of pipewire, for now
-   * we just listen for the alsa nodes created by pipewire. We eventually
-   * need to move all the node creation logic here */
-
   /* Create the impl */
-  struct impl *impl = g_new0(struct impl, 1);
-  impl->wp_core = core;
-  impl->remote = wp_core_get_global(core, WP_GLOBAL_PW_REMOTE);
-  impl->done_queue = g_queue_new();
-  spa_list_init(&impl->port_list);
+  struct impl *impl = module_create (core);
 
   /* Set destroy callback for impl */
   wp_module_set_destroy_callback (module, module_destroy, impl);
-
-  /* Add a state changed listener */
-  pw_remote_add_listener(impl->remote, &impl->remote_listener, &remote_events,
-      impl);
 }
diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c
index 4d2f0be2..77d8265e 100644
--- a/modules/module-pw-audio-softdsp-endpoint.c
+++ b/modules/module-pw-audio-softdsp-endpoint.c
@@ -19,53 +19,49 @@
 #include <spa/pod/builder.h>
 #include <spa/param/props.h>
 
-#include "module-pipewire/port.h"
-
 #define MIN_QUANTUM_SIZE  64
 #define MAX_QUANTUM_SIZE  1024
 
-struct _WpPwAudioSoftdspEndpoint {
+struct _WpPwAudioSoftdspEndpoint
+{
   WpEndpoint parent;
 
   /* temporary method to select which endpoint
    * is going to be the default input/output */
   gboolean selected;
-
-  /* The core proxy */
+  
+  /* Core */
   struct pw_core_proxy *core_proxy;
 
-  /* Node proxy and listener */
-  struct pw_node_proxy *node_proxy;
-  struct spa_hook listener;
-  struct spa_hook proxy_listener;
+  /* Registry */
+  struct pw_registry_proxy *registry_proxy;
+  struct spa_hook registry_listener;
 
-  /* Node info */
-  struct pw_node_info *node_info;
-  uint32_t media_type;
-  uint32_t media_subtype;
-  struct spa_audio_info_raw format;
+  /* Direction */
   enum pw_direction direction;
 
-  /* DSP proxy and listener */
-  struct pw_node_proxy *dsp_proxy;
-  struct spa_hook dsp_listener;
+  /* Proxy */
+  WpProxyNode *proxy_node;
+  WpProxyPort *proxy_port;
 
-  /* DSP info */
-  struct pw_node_info *dsp_info;
+  /* DSP port id */
+  uint32_t dsp_port_id;
+
+  /* Volume */
   gfloat master_volume;
   gboolean master_mute;
 
-  /* Link proxy and listener */
+  /* TODO: This needs to use the new proxy API */
+  struct pw_node_proxy *dsp_proxy;
+  struct spa_hook dsp_listener;
+  struct pw_node_info *dsp_info;
   struct pw_proxy *link_proxy;
-
-  /* The all port list reference */
-  /* TODO: make it thread safe */
-  struct spa_list *port_list;
 };
 
 enum {
   PROP_0,
   PROP_NODE_PROXY,
+  PROP_PORT_PROXY,
 };
 
 enum {
@@ -79,15 +75,43 @@ G_DECLARE_FINAL_TYPE (WpPwAudioSoftdspEndpoint, endpoint,
 
 G_DEFINE_TYPE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT)
 
+static gboolean
+endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id,
+    WpEndpointLink * link, GVariant ** properties, GError ** error)
+{
+  WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep);
+  GVariantBuilder b;
+  
+  /* Make sure dsp info is valid */
+  if (!self->dsp_info)
+    return FALSE;
+  
+  /* Set the properties */
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}", "node-id",
+      g_variant_new_uint32 (self->dsp_info->id));
+  g_variant_builder_add (&b, "{sv}", "node-port-id",
+      g_variant_new_uint32 (self->dsp_port_id));
+  *properties = g_variant_builder_end (&b);
+
+  return TRUE;
+}
+
 static void
-on_dsp_running (WpPwAudioSoftdspEndpoint *self)
+on_dsp_running(WpPwAudioSoftdspEndpoint *self)
 {
   struct pw_properties *props;
+  const struct pw_node_info *node_info = NULL;
 
   /* Return if the node has already been linked */
   if (self->link_proxy)
     return;
 
+  /* Get the node info */
+  node_info = wp_proxy_node_get_info(self->proxy_node);
+  if (!node_info)
+    return;
+
   /* Create new properties */
   props = pw_properties_new(NULL, NULL);
 
@@ -96,10 +120,10 @@ on_dsp_running (WpPwAudioSoftdspEndpoint *self)
   if (self->direction == PW_DIRECTION_OUTPUT) {
     pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", self->dsp_info->id);
     pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", -1);
-    pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", self->node_info->id);
+    pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", node_info->id);
     pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", -1);
   } else {
-    pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", self->node_info->id);
+    pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", node_info->id);
     pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", -1);
     pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", self->dsp_info->id);
     pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", -1);
@@ -209,20 +233,30 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self)
   struct spa_pod_builder pod_builder = { 0, };
   struct spa_pod *param;
   uint32_t ids[1] = { SPA_PARAM_Props };
+  const struct pw_node_info *node_info;
+  const struct spa_audio_info_raw *port_format;
+  struct spa_audio_info_raw format;
+  
+  /* Get the node info */
+  node_info = wp_proxy_node_get_info(self->proxy_node);
+  if (!node_info)
+    return;
 
-  /* Return if the node has been already emitted */
-  if (self->dsp_proxy)
+  /* Get the port format */
+  port_format = wp_proxy_port_get_format(self->proxy_port);
+  if (!port_format)
     return;
+  format = *port_format;
 
   /* Get the properties */
-  props = pw_properties_new_dict(self->node_info->props);
+  props = pw_properties_new_dict(node_info->props);
   if (!props)
     return;
 
   /* Get the DSP name */
   dsp_name = pw_properties_get(props, "device.nick");
   if (!dsp_name)
-    dsp_name = self->node_info->name;
+    dsp_name = node_info->name;
 
   /* Set the properties */
   pw_properties_set(props, "audio-dsp.name", dsp_name);
@@ -239,7 +273,7 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self)
 
   /* Set DSP proxy params */
   spa_pod_builder_init(&pod_builder, buf, sizeof(buf));
-  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &self->format);
+  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
   param = spa_pod_builder_add_object(&pod_builder,
       SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile,
       SPA_PARAM_PROFILE_direction,  SPA_POD_Id(pw_direction_reverse(self->direction)),
@@ -251,64 +285,6 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self)
   pw_properties_free(props);
 }
 
-static void
-node_event_info (void *data, const struct pw_node_info *info)
-{
-  WpPwAudioSoftdspEndpoint *self = data;
-  WpPort *port = NULL;
-
-  /* Set the node info */
-  self->node_info = pw_node_info_update(self->node_info, info);
-
-  /* Find the node port */
-  spa_list_for_each(port, self->port_list, l) {
-    if (port->parent_id == self->node_info->id)
-      break;
-  }
-
-  /* Set the format using the port format */
-  self->format = port->format;
-
-  /* Emit the audio DSP node */
-  emit_audio_dsp_node(self);
-
-  /* TODO: Handle the different states */
-  switch (info->state) {
-  case PW_NODE_STATE_IDLE:
-    break;
-  case PW_NODE_STATE_RUNNING:
-    break;
-  case PW_NODE_STATE_SUSPENDED:
-    break;
-  default:
-    break;
-  }
-}
-
-static const struct pw_node_proxy_events node_events = {
-  PW_VERSION_NODE_PROXY_EVENTS,
-  .info = node_event_info,
-};
-
-static void
-node_proxy_destroy(void *data)
-{
-  WpPwAudioSoftdspEndpoint *self = data;
-
-  self->node_proxy = NULL;
-  wp_endpoint_unregister (WP_ENDPOINT (self));
-}
-
-static const struct pw_proxy_events node_proxy_events = {
-  PW_VERSION_PROXY_EVENTS,
-  .destroy = node_proxy_destroy,
-};
-
-static void
-endpoint_init (WpPwAudioSoftdspEndpoint * self)
-{
-}
-
 static void
 endpoint_constructed (GObject * object)
 {
@@ -325,12 +301,6 @@ endpoint_constructed (GObject * object)
     g_critical ("failed to parse direction");
   }
 
-  /* Set the node and proxy listeners */
-  pw_proxy_add_listener ((struct pw_proxy *) self->node_proxy, &self->listener,
-      &node_proxy_events, self);
-  pw_node_proxy_add_listener(self->node_proxy, &self->proxy_listener,
-      &node_events, self);
-
   g_variant_dict_init (&d, NULL);
   g_variant_dict_insert (&d, "id", "u", 0);
   g_variant_dict_insert (&d, "name", "s", "default");
@@ -369,23 +339,30 @@ endpoint_finalize (GObject * object)
 {
   WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object);
 
-  /* Clear node_info */
-  if (self->node_info)
-    pw_node_info_free(self->node_info);
+  /* Unref the proxy node */
+  if (self->proxy_node) {
+    g_object_unref(self->proxy_node);
+    self->proxy_node = NULL;
+  }
 
-  /* Clear dsp_info */
-  if (self->dsp_info)
-    pw_node_info_free(self->dsp_info);
+  /* Unref the proxy port */
+  if (self->proxy_port) {
+    g_object_unref(self->proxy_port);
+    self->proxy_port = NULL;
+  }
 
-  /* Remove and destroy the node_proxy */
-  if (self->node_proxy) {
-    spa_hook_remove (&self->listener);
-    pw_proxy_destroy ((struct pw_proxy *) self->node_proxy);
+  /* Clear the dsp info */
+  if (self->dsp_info) {
+    pw_node_info_free(self->dsp_info);
+    self->dsp_info = NULL;
   }
 
-  /* Remove and destroy the dsp_proxy */
-  if (self->dsp_proxy)
+  /* Destroy the dsp_proxy */
+  if (self->dsp_proxy) {
+    spa_hook_remove (&self->dsp_listener);
     pw_proxy_destroy ((struct pw_proxy *) self->dsp_proxy);
+    self->dsp_proxy = NULL;
+  }
 
   G_OBJECT_CLASS (endpoint_parent_class)->finalize (object);
 }
@@ -398,7 +375,12 @@ endpoint_set_property (GObject * object, guint property_id,
 
   switch (property_id) {
   case PROP_NODE_PROXY:
-    self->node_proxy = g_value_get_pointer (value);
+    g_clear_object(&self->proxy_node);
+    self->proxy_node = g_value_get_object(value);
+    break;
+  case PROP_PORT_PROXY:
+    g_clear_object(&self->proxy_port);
+    self->proxy_port = g_value_get_object(value);
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -414,7 +396,10 @@ endpoint_get_property (GObject * object, guint property_id,
 
   switch (property_id) {
   case PROP_NODE_PROXY:
-    g_value_set_pointer (value, self->node_proxy);
+    g_value_set_object (value, self->proxy_node);
+    break;
+  case PROP_PORT_PROXY:
+    g_value_set_object (value, self->proxy_port);
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -422,46 +407,6 @@ endpoint_get_property (GObject * object, guint property_id,
   }
 }
 
-static gboolean
-endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id,
-    WpEndpointLink * link, GVariant ** properties, GError ** error)
-{
-  WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep);
-  WpPort *port = NULL, *node_port = NULL, *dsp_port = NULL;
-  GVariantBuilder b;
-
-  /* Find the node port */
-  spa_list_for_each(port, self->port_list, l) {
-    if (self->node_info->id == port->parent_id) {
-      node_port = port;
-      break;
-    }
-  }
-  if (!node_port)
-    return FALSE;
-
-  /* Find the first dsp port with the same direction as the node port */
-  spa_list_for_each(port, self->port_list, l) {
-    if (self->dsp_info->id == port->parent_id
-        && port->direction == node_port->direction) {
-      dsp_port = port;
-      break;
-    }
-  }
-  if (!dsp_port)
-    return FALSE;
-
-  /* Set the properties */
-  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
-  g_variant_builder_add (&b, "{sv}", "node-id",
-      g_variant_new_uint32 (self->dsp_info->id));
-  g_variant_builder_add (&b, "{sv}", "node-port-id",
-      g_variant_new_uint32 (dsp_port->id));
-  *properties = g_variant_builder_end (&b);
-
-  return TRUE;
-}
-
 static GVariant *
 endpoint_get_control_value (WpEndpoint * ep, guint32 control_id)
 {
@@ -538,6 +483,11 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id,
   return TRUE;
 }
 
+static void
+endpoint_init (WpPwAudioSoftdspEndpoint * self)
+{
+}
+
 static void
 endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass)
 {
@@ -554,11 +504,71 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass)
   endpoint_class->set_control_value = endpoint_set_control_value;
 
   g_object_class_install_property (object_class, PROP_NODE_PROXY,
-      g_param_spec_pointer ("node-proxy", "node-proxy",
-          "Pointer to the source/sink pw_node_proxy* of the device",
+      g_param_spec_object ("node-proxy", "node-proxy",
+          "Pointer to the node proxy of the device", WP_TYPE_PROXY_NODE,
           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_PORT_PROXY,
+      g_param_spec_object ("port-proxy", "port-proxy",
+          "Pointer to the port ptoxy of the device", WP_TYPE_PROXY_PORT,
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+handle_port(WpPwAudioSoftdspEndpoint *self, uint32_t id, uint32_t parent_id,
+    const struct spa_dict *props)
+{
+  const char *direction_prop = NULL;
+  enum pw_direction direction;
+
+  /* Make sure the dsp port is not already set*/
+  if (self->dsp_port_id != 0)
+    return;
+
+  /* Make sure the port has porperties */
+  if (!props)
+    return;
+
+  /* Only handle ports owned by this endpoint */
+  if (!self->dsp_info || self->dsp_info->id != parent_id)
+    return;
+
+  /* Get the direction property */
+  direction_prop = spa_dict_lookup(props, "port.direction");
+  if (!direction_prop)
+    return;
+  direction =
+      !strcmp(direction_prop, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT;
+
+  /* Only handle ports with the oposit direction of the endpoint */
+  if (self->direction == direction)
+    return;
+  
+  /* Set the dsp port id */
+  self->dsp_port_id = id;
+}
+
+static void
+registry_global(void *data, uint32_t id, uint32_t parent_id,
+		uint32_t permissions, uint32_t type, uint32_t version,
+		const struct spa_dict *props)
+{
+  WpPwAudioSoftdspEndpoint *self = data;
+
+  switch (type) {
+  case PW_TYPE_INTERFACE_Port:
+    handle_port(self, id, parent_id, props);
+    break;
+
+  default:
+    break;
+  }
 }
 
+static const struct pw_registry_proxy_events registry_events = {
+  PW_VERSION_REGISTRY_PROXY_EVENTS,
+  .global = registry_global,
+};
+
 static gpointer
 endpoint_factory (WpFactory * factory, GType type, GVariant * properties)
 {
@@ -566,7 +576,7 @@ endpoint_factory (WpFactory * factory, GType type, GVariant * properties)
   struct pw_remote *remote;
   const gchar *name = NULL;
   const gchar *media_class = NULL;
-  guint64 proxy, port_list;
+  guint64 proxy_node, proxy_port;
 
   /* Make sure the type is not the base class type */
   if (type != WP_TYPE_ENDPOINT)
@@ -591,29 +601,36 @@ endpoint_factory (WpFactory * factory, GType type, GVariant * properties)
       return NULL;
   if (!g_variant_lookup (properties, "media-class", "&s", &media_class))
       return NULL;
-  if (!g_variant_lookup (properties, "node-proxy", "t", &proxy))
+  if (!g_variant_lookup (properties, "proxy-node", "t", &proxy_node))
       return NULL;
-  if (!g_variant_lookup (properties, "port-list", "t", &port_list))
+  if (!g_variant_lookup (properties, "proxy-port", "t", &proxy_port))
       return NULL;
 
   /* Create the softdsp endpoint object */
   WpPwAudioSoftdspEndpoint *ep = g_object_new (endpoint_get_type (),
       "name", name,
       "media-class", media_class,
-      "node-proxy", (gpointer) proxy,
+      "node-proxy", (gpointer) proxy_node,
+      "port-proxy", (gpointer) proxy_port,
       NULL);
   if (!ep)
     return NULL;
 
-  /* Set the port list reference */
-  ep->port_list = (gpointer) port_list;
-
   /* Set the core proxy */
   ep->core_proxy = pw_remote_get_core_proxy(remote);
   if (!ep->core_proxy) {
     g_warning("failed to get core proxy. Skipping...");
     return NULL;
   }
+  
+  /* Add registry listener */
+  ep->registry_proxy = pw_core_proxy_get_registry (ep->core_proxy,
+      PW_TYPE_INTERFACE_Registry, PW_VERSION_REGISTRY, 0);
+  pw_registry_proxy_add_listener(ep->registry_proxy, &ep->registry_listener,
+      &registry_events, ep);
+
+  /* Emit the audio DSP node */
+  emit_audio_dsp_node(ep);
 
   /* Return the object */
   return ep;
-- 
GitLab