From b9ad90f68cbfd6a3ef3c40e786fec08db540cc49 Mon Sep 17 00:00:00 2001
From: Julian Bouzas <julian.bouzas@collabora.com>
Date: Wed, 25 Sep 2019 11:58:02 +0200
Subject: [PATCH] softdsp-endpoint: use the same number of channels in the
 converters as the sink/source node

---
 .../module-pipewire/audio-softdsp-endpoint.c  | 13 +++-
 .../audio-softdsp-endpoint/adapter.c          | 77 +++++++++++++++----
 .../audio-softdsp-endpoint/adapter.h          |  4 +
 .../audio-softdsp-endpoint/convert.c          | 32 +++++---
 .../audio-softdsp-endpoint/convert.h          |  6 +-
 5 files changed, 98 insertions(+), 34 deletions(-)

diff --git a/modules/module-pipewire/audio-softdsp-endpoint.c b/modules/module-pipewire/audio-softdsp-endpoint.c
index c7d2bde1..a35fea2a 100644
--- a/modules/module-pipewire/audio-softdsp-endpoint.c
+++ b/modules/module-pipewire/audio-softdsp-endpoint.c
@@ -162,6 +162,7 @@ on_audio_adapter_created(GObject *initable, GAsyncResult *res,
   enum pw_direction direction = wp_endpoint_get_direction(WP_ENDPOINT(self));
   g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self));
   g_autoptr (WpProperties) props = NULL;
+  const struct spa_audio_info_raw *format;
   g_autofree gchar *name = NULL;
   GVariantDict d;
   GVariantIter iter;
@@ -190,14 +191,20 @@ on_audio_adapter_created(GObject *initable, GAsyncResult *res,
   self->role = g_strdup (wp_properties_get (props, PW_KEY_MEDIA_ROLE));
 
   /* Just finish if no streams need to be created */
-  if (!self->streams)
-    return finish_endpoint_creation (self);
+  if (!self->streams) {
+    finish_endpoint_creation (self);
+    return;
+  }
+
+  /* Get the adapter format */
+  format = wp_audio_adapter_get_format (WP_AUDIO_ADAPTER (self->adapter));
+  g_return_if_fail (format);
 
   /* Create the audio converters */
   g_variant_iter_init (&iter, self->streams);
   for (i = 0; g_variant_iter_next (&iter, "&s", &stream); i++) {
     wp_audio_convert_new (WP_ENDPOINT(self), i, stream, direction,
-        self->proxy_node, on_audio_convert_created, self);
+        self->proxy_node, format, on_audio_convert_created, self);
 
     /* Register the stream */
     g_variant_dict_init (&d, NULL);
diff --git a/modules/module-pipewire/audio-softdsp-endpoint/adapter.c b/modules/module-pipewire/audio-softdsp-endpoint/adapter.c
index 84cfc512..abae8c3c 100644
--- a/modules/module-pipewire/audio-softdsp-endpoint/adapter.c
+++ b/modules/module-pipewire/audio-softdsp-endpoint/adapter.c
@@ -24,6 +24,9 @@ struct _WpAudioAdapter
 
   /* Props */
   gboolean convert;
+
+  /* THe raw format this adapter is configured */
+  struct spa_audio_info_raw format;
 };
 
 static GAsyncInitableIface *wp_audio_adapter_parent_interface = NULL;
@@ -35,46 +38,79 @@ G_DEFINE_TYPE_WITH_CODE (WpAudioAdapter, wp_audio_adapter, WP_TYPE_AUDIO_STREAM,
                            wp_audio_adapter_async_initable_init))
 
 static void
-wp_audio_adapter_init_async (GAsyncInitable *initable, int io_priority,
-    GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
+on_proxy_enum_format_done (WpProxyNode *proxy, GAsyncResult *res,
+    WpAudioAdapter *self)
 {
-  WpAudioAdapter *self = WP_AUDIO_ADAPTER(initable);
+  g_autoptr (GPtrArray) formats = NULL;
+  g_autoptr (GError) error = NULL;
   enum pw_direction direction =
       wp_audio_stream_get_direction (WP_AUDIO_STREAM (self));
   uint8_t buf[1024];
   struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
   struct spa_pod *param;
-  struct spa_audio_info_raw fmt_raw;
+  uint32_t media_type, media_subtype;
+
+  formats = wp_proxy_node_enum_params_collect_finish (proxy, res, &error);
+  if (error) {
+    g_message("WpAudioAdapter:%p enum format error: %s", self, error->message);
+    wp_audio_stream_init_task_finish (WP_AUDIO_STREAM (self),
+        g_steal_pointer (&error));
+    return;
+  }
 
-  /* Call the parent interface */
-  /* This will also augment the proxy and therefore bind it */
-  wp_audio_adapter_parent_interface->init_async (initable, io_priority,
-      cancellable, callback, data);
+  if (formats->len == 0 ||
+      !(param = g_ptr_array_index (formats, 0)) ||
+      spa_format_parse (param, &media_type, &media_subtype) < 0 ||
+      media_type != SPA_MEDIA_TYPE_audio ||
+      media_subtype != SPA_MEDIA_SUBTYPE_raw) {
+    g_message("WpAudioAdapter:%p node does not support audio/raw format", self);
+    wp_audio_stream_init_task_finish (WP_AUDIO_STREAM (self),
+        g_error_new (WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
+            "node does not support audio/raw format"));
+    return;
+  }
+
+  /* Parse the raw audio format */
+  spa_pod_fixate (param);
+  spa_format_audio_raw_parse (param, &self->format);
+
+  /* Keep the chanels but use the default format */
+  self->format.format = SPA_AUDIO_FORMAT_F32P;
+  self->format.rate = 48000;
 
-  /* Emit the ports */
   if (self->convert) {
     param = spa_pod_builder_add_object(&pod_builder,
         SPA_TYPE_OBJECT_ParamPortConfig,  SPA_PARAM_PortConfig,
         SPA_PARAM_PORT_CONFIG_direction,  SPA_POD_Id(direction),
         SPA_PARAM_PORT_CONFIG_mode,       SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_convert));
   } else {
-    /* Use the default format */
-    fmt_raw.format = SPA_AUDIO_FORMAT_F32P;
-    fmt_raw.flags = 1;
-    fmt_raw.rate = 48000;
-    fmt_raw.channels = 2;
-    fmt_raw.position[0] = SPA_AUDIO_CHANNEL_FL;
-    fmt_raw.position[1] = SPA_AUDIO_CHANNEL_FR;
-    param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &fmt_raw);
+    param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &self->format);
     param = spa_pod_builder_add_object(&pod_builder,
         SPA_TYPE_OBJECT_ParamPortConfig,  SPA_PARAM_PortConfig,
         SPA_PARAM_PORT_CONFIG_direction,  SPA_POD_Id(direction),
         SPA_PARAM_PORT_CONFIG_mode,       SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp),
         SPA_PARAM_PORT_CONFIG_format,     SPA_POD_Pod(param));
   }
+
   wp_audio_stream_set_port_config (WP_AUDIO_STREAM (self), param);
 }
 
+static void
+wp_audio_adapter_init_async (GAsyncInitable *initable, int io_priority,
+    GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data)
+{
+  WpAudioAdapter *self = WP_AUDIO_ADAPTER(initable);
+  WpProxyNode *proxy = wp_audio_stream_get_proxy_node (WP_AUDIO_STREAM (self));
+
+  /* Call the parent interface */
+  /* This will also augment the proxy and therefore bind it */
+  wp_audio_adapter_parent_interface->init_async (initable, io_priority,
+      cancellable, callback, data);
+
+  wp_proxy_node_enum_params_collect (proxy, SPA_PARAM_EnumFormat, NULL, NULL,
+      (GAsyncReadyCallback) on_proxy_enum_format_done, self);
+}
+
 static void
 wp_audio_adapter_async_initable_init (gpointer iface, gpointer iface_data)
 {
@@ -153,3 +189,10 @@ wp_audio_adapter_new (WpEndpoint *endpoint, guint stream_id,
       "convert", convert,
       NULL);
 }
+
+struct spa_audio_info_raw *
+wp_audio_adapter_get_format (WpAudioAdapter *self)
+{
+  g_return_val_if_fail (self, NULL);
+  return &self->format;
+}
diff --git a/modules/module-pipewire/audio-softdsp-endpoint/adapter.h b/modules/module-pipewire/audio-softdsp-endpoint/adapter.h
index 25012261..9dc145ea 100644
--- a/modules/module-pipewire/audio-softdsp-endpoint/adapter.h
+++ b/modules/module-pipewire/audio-softdsp-endpoint/adapter.h
@@ -16,6 +16,8 @@
 
 G_BEGIN_DECLS
 
+struct spa_audio_info_raw format;
+
 #define WP_TYPE_AUDIO_ADAPTER (wp_audio_adapter_get_type ())
 G_DECLARE_FINAL_TYPE (WpAudioAdapter, wp_audio_adapter, WP, AUDIO_ADAPTER,
     WpAudioStream)
@@ -24,6 +26,8 @@ void wp_audio_adapter_new (WpEndpoint *endpoint, guint stream_id,
     const char *stream_name, enum pw_direction direction, WpProxyNode *node,
     gboolean convert, GAsyncReadyCallback callback, gpointer user_data);
 
+struct spa_audio_info_raw *wp_audio_adapter_get_format (WpAudioAdapter *self);
+
 G_END_DECLS
 
 #endif
diff --git a/modules/module-pipewire/audio-softdsp-endpoint/convert.c b/modules/module-pipewire/audio-softdsp-endpoint/convert.c
index 5c41d235..afb7228c 100644
--- a/modules/module-pipewire/audio-softdsp-endpoint/convert.c
+++ b/modules/module-pipewire/audio-softdsp-endpoint/convert.c
@@ -17,6 +17,7 @@
 enum {
   PROP_0,
   PROP_TARGET,
+  PROP_FORMAT,
 };
 
 struct _WpAudioConvert
@@ -25,6 +26,7 @@ struct _WpAudioConvert
 
   /* Props */
   WpProxyNode *target;
+  struct spa_audio_info_raw format;
 
   /* Proxies */
   WpProxy *link_proxy;
@@ -109,7 +111,6 @@ on_audio_convert_proxy_done (WpProxy *proxy, GAsyncResult *res,
   g_autoptr (GError) error = NULL;
   enum pw_direction direction =
       wp_audio_stream_get_direction (WP_AUDIO_STREAM (self));
-  struct spa_audio_info_raw format;
   uint8_t buf[1024];
   struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
   struct spa_pod *param;
@@ -124,16 +125,8 @@ on_audio_convert_proxy_done (WpProxy *proxy, GAsyncResult *res,
 
   g_debug ("%s:%p setting format", G_OBJECT_TYPE_NAME (self), self);
 
-  /* Use the default format */
-  format.format = SPA_AUDIO_FORMAT_F32P;
-  format.flags = 1;
-  format.rate = 48000;
-  format.channels = 2;
-  format.position[0] = SPA_AUDIO_CHANNEL_FL;
-  format.position[1] = SPA_AUDIO_CHANNEL_FR;
-
   /* Emit the ports */
-  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format);
+  param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &self->format);
   param = spa_pod_builder_add_object(&pod_builder,
       SPA_TYPE_OBJECT_ParamPortConfig,  SPA_PARAM_PortConfig,
       SPA_PARAM_PORT_CONFIG_direction,  SPA_POD_Id(direction),
@@ -198,6 +191,14 @@ wp_audio_convert_set_property (GObject * object, guint property_id,
   case PROP_TARGET:
     self->target = g_value_dup_object (value);
     break;
+  case PROP_FORMAT: {
+    const struct spa_audio_info_raw *f = g_value_get_pointer (value);
+    if (f)
+      self->format = *f;
+    else
+      g_warning ("WpAudioConvert:%p Format needs to be valid", self);
+    break;
+  }
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     break;
@@ -214,6 +215,9 @@ wp_audio_convert_get_property (GObject * object, guint property_id,
   case PROP_TARGET:
     g_value_set_object (value, self->target);
     break;
+  case PROP_FORMAT:
+    g_value_set_pointer (value, &self->format);
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     break;
@@ -250,13 +254,16 @@ wp_audio_convert_class_init (WpAudioConvertClass * klass)
       g_param_spec_object ("target", "target", "The target device node",
           WP_TYPE_PROXY_NODE,
           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_FORMAT,
+      g_param_spec_pointer ("format", "format", "The accepted format",
+          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 }
 
 void
 wp_audio_convert_new (WpEndpoint *endpoint, guint stream_id,
     const char *stream_name, enum pw_direction direction,
-    WpProxyNode *target, GAsyncReadyCallback callback,
-    gpointer user_data)
+    WpProxyNode *target, const struct spa_audio_info_raw *format,
+    GAsyncReadyCallback callback, gpointer user_data)
 {
   g_async_initable_new_async (
       WP_TYPE_AUDIO_CONVERT, G_PRIORITY_DEFAULT, NULL, callback, user_data,
@@ -265,5 +272,6 @@ wp_audio_convert_new (WpEndpoint *endpoint, guint stream_id,
       "name", stream_name,
       "direction", direction,
       "target", target,
+      "format", format,
       NULL);
 }
diff --git a/modules/module-pipewire/audio-softdsp-endpoint/convert.h b/modules/module-pipewire/audio-softdsp-endpoint/convert.h
index 75737e42..84e8f404 100644
--- a/modules/module-pipewire/audio-softdsp-endpoint/convert.h
+++ b/modules/module-pipewire/audio-softdsp-endpoint/convert.h
@@ -16,14 +16,16 @@
 
 G_BEGIN_DECLS
 
+struct spa_audio_info_raw format;
+
 #define WP_TYPE_AUDIO_CONVERT (wp_audio_convert_get_type ())
 G_DECLARE_FINAL_TYPE (WpAudioConvert, wp_audio_convert, WP, AUDIO_CONVERT,
     WpAudioStream)
 
 void wp_audio_convert_new (WpEndpoint *endpoint, guint stream_id,
     const char *stream_name, enum pw_direction direction,
-    WpProxyNode *target, GAsyncReadyCallback callback,
-    gpointer user_data);
+    WpProxyNode *target, const struct spa_audio_info_raw *format,
+    GAsyncReadyCallback callback, gpointer user_data);
 
 G_END_DECLS
 
-- 
GitLab