From 91e26d6011bd194deffb5765f9b3306fb92738d9 Mon Sep 17 00:00:00 2001
Message-ID: <91e26d6011bd194deffb5765f9b3306fb92738d9.1737052666.git.sam@gentoo.org>
In-Reply-To: <1993383ddf67e296334c7916d6afc699ee6300c7.1737052666.git.sam@gentoo.org>
References: <1993383ddf67e296334c7916d6afc699ee6300c7.1737052666.git.sam@gentoo.org>
From: Wim Taymans <wtaymans@redhat.com>
Date: Tue, 3 Dec 2024 11:54:52 +0100
Subject: [PATCH 5/8] gst: add slave-method property

Set the slave-method to none by default to disable the resampler.

Fixes #4374
---
 src/gst/gstpipewiresink.c | 141 +++++++++++++++++++++++++++-----------
 src/gst/gstpipewiresink.h |  21 +++++-
 2 files changed, 120 insertions(+), 42 deletions(-)

diff --git a/src/gst/gstpipewiresink.c b/src/gst/gstpipewiresink.c
index 33f2322e9..bf1b427f0 100644
--- a/src/gst/gstpipewiresink.c
+++ b/src/gst/gstpipewiresink.c
@@ -37,6 +37,7 @@ GST_DEBUG_CATEGORY_STATIC (pipewire_sink_debug);
 #define GST_CAT_DEFAULT pipewire_sink_debug
 
 #define DEFAULT_PROP_MODE GST_PIPEWIRE_SINK_MODE_DEFAULT
+#define DEFAULT_PROP_SLAVE_METHOD GST_PIPEWIRE_SINK_SLAVE_METHOD_NONE
 
 #define MIN_BUFFERS     8u
 
@@ -49,7 +50,8 @@ enum
   PROP_CLIENT_PROPERTIES,
   PROP_STREAM_PROPERTIES,
   PROP_MODE,
-  PROP_FD
+  PROP_FD,
+  PROP_SLAVE_METHOD
 };
 
 GType
@@ -72,6 +74,26 @@ gst_pipewire_sink_mode_get_type (void)
   return (GType) mode_type;
 }
 
+GType
+gst_pipewire_sink_slave_method_get_type (void)
+{
+  static gsize method_type = 0;
+  static const GEnumValue method[] = {
+    {GST_PIPEWIRE_SINK_SLAVE_METHOD_NONE, "GST_PIPEWIRE_SINK_SLAVE_METHOD_NONE", "none"},
+    {GST_PIPEWIRE_SINK_SLAVE_METHOD_RESAMPLE, "GST_PIPEWIRE_SINK_SLAVE_METHOD_RESAMPLE", "resample"},
+    {0, NULL, NULL},
+  };
+
+  if (g_once_init_enter (&method_type)) {
+    GType tmp =
+        g_enum_register_static ("GstPipeWireSinkSlaveMethod", method);
+    g_once_init_leave (&method_type, tmp);
+  }
+
+  return (GType) method_type;
+}
+
+
 
 static GstStaticPadTemplate gst_pipewire_sink_template =
 GST_STATIC_PAD_TEMPLATE ("sink",
@@ -225,6 +247,17 @@ gst_pipewire_sink_class_init (GstPipeWireSinkClass * klass)
                                                       G_PARAM_READWRITE |
                                                       G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_SLAVE_METHOD,
+                                   g_param_spec_enum ("slave-method",
+                                                      "Slave Method",
+                                                      "Algorithm used to match the rate of the masterclock",
+                                                      GST_TYPE_PIPEWIRE_SINK_SLAVE_METHOD,
+                                                      DEFAULT_PROP_SLAVE_METHOD,
+                                                      G_PARAM_READWRITE |
+                                                      G_PARAM_STATIC_STRINGS));
+
+
   gstelement_class->provide_clock = gst_pipewire_sink_provide_clock;
   gstelement_class->change_state = gst_pipewire_sink_change_state;
 
@@ -408,6 +441,10 @@ gst_pipewire_sink_set_property (GObject * object, guint prop_id,
       pwsink->stream->fd = g_value_get_int (value);
       break;
 
+    case PROP_SLAVE_METHOD:
+      pwsink->slave_method = g_value_get_enum (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -449,12 +486,69 @@ gst_pipewire_sink_get_property (GObject * object, guint prop_id,
       g_value_set_int (value, pwsink->stream->fd);
       break;
 
+    case PROP_SLAVE_METHOD:
+      g_value_set_enum (value, pwsink->slave_method);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
+static void rate_match_resample(GstPipeWireSink *pwsink)
+{
+  GstPipeWireStream *stream = pwsink->stream;
+  double err, corr;
+  struct pw_time ts;
+  guint64 queued, now, elapsed, target;
+
+  if (!pwsink->rate_match)
+    return;
+
+  pw_stream_get_time_n(stream->pwstream, &ts, sizeof(ts));
+  now = pw_stream_get_nsec(stream->pwstream);
+  if (ts.now != 0)
+    elapsed = gst_util_uint64_scale_int (now - ts.now, ts.rate.denom, GST_SECOND * ts.rate.num);
+  else
+    elapsed = 0;
+
+  queued = ts.queued - ts.size;
+  target = elapsed;
+  err = ((gint64)queued - ((gint64)target));
+
+  corr = spa_dll_update(&stream->dll, SPA_CLAMPD(err, -128.0, 128.0));
+
+  stream->err_wdw = (double)ts.rate.denom/ts.size;
+
+  double avg = (stream->err_avg * stream->err_wdw + (err - stream->err_avg)) / (stream->err_wdw + 1.0);
+  stream->err_var = (stream->err_var * stream->err_wdw +
+                    (err - stream->err_avg) * (err - avg)) / (stream->err_wdw + 1.0);
+  stream->err_avg = avg;
+
+  if (stream->last_ts == 0 || stream->last_ts + SPA_NSEC_PER_SEC < now) {
+    double bw;
+
+    stream->last_ts = now;
+
+    if (stream->err_var == 0.0)
+      bw = 0.0;
+    else
+      bw = fabs(stream->err_avg) / sqrt(fabs(stream->err_var));
+
+    spa_dll_set_bw(&stream->dll, SPA_CLAMPD(bw, 0.001, SPA_DLL_BW_MAX), ts.size, ts.rate.denom);
+
+    GST_INFO_OBJECT (pwsink, "q:%"PRIi64"/%"PRIi64" e:%"PRIu64" err:%+03f corr:%f %f %f %f",
+                    ts.queued, ts.size, elapsed, err, corr,
+		    stream->err_avg, stream->err_var, stream->dll.bw);
+  }
+
+  if (pwsink->match) {
+    pwsink->match->rate = corr;
+    SPA_FLAG_UPDATE(pwsink->match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, true);
+  }
+}
+
 static void
 on_add_buffer (void *_data, struct pw_buffer *b)
 {
@@ -539,45 +633,12 @@ do_send_buffer (GstPipeWireSink *pwsink, GstBuffer *buffer)
     g_warning ("can't send buffer %s", spa_strerror(res));
   }
 
-  if (pwsink->rate_match) {
-    double err, corr;
-    struct pw_time ts;
-    guint64 queued, now, elapsed, target;
-
-    pw_stream_get_time_n(stream->pwstream, &ts, sizeof(ts));
-    now = pw_stream_get_nsec(stream->pwstream);
-    if (ts.now != 0)
-	    elapsed = gst_util_uint64_scale_int (now - ts.now, ts.rate.denom, GST_SECOND * ts.rate.num);
-    else
-	    elapsed = 0;
-
-    queued = ts.queued - ts.size;
-    target = 2 * elapsed;
-    err = ((gint64)queued - ((gint64)target));
-
-    corr = spa_dll_update(&stream->dll, SPA_CLAMPD(err, -128.0, 128.0));
-
-    stream->err_wdw = (double)ts.rate.denom/ts.size;
-
-    double avg = (stream->err_avg * stream->err_wdw + (err - stream->err_avg)) / (stream->err_wdw + 1.0);
-    stream->err_var = (stream->err_var * stream->err_wdw +
-                      (err - stream->err_avg) * (err - avg)) / (stream->err_wdw + 1.0);
-    stream->err_avg = avg;
-
-    if (stream->last_ts == 0 || stream->last_ts + SPA_NSEC_PER_SEC < now) {
-      stream->last_ts = now;
-      spa_dll_set_bw(&stream->dll, SPA_CLAMPD(fabs(stream->err_avg) / sqrt(fabs(stream->err_var)), 0.001, SPA_DLL_BW_MAX),
-                     ts.size, ts.rate.denom);
-    GST_INFO_OBJECT (pwsink, "queue buffer %p, pw_buffer %p q:%"PRIi64"/%"PRIi64" e:%"PRIu64
-		    " err:%+03f corr:%f %f %f %f",
-                    buffer, data->b, ts.queued, ts.size, elapsed, err, corr,
-		    stream->err_avg, stream->err_var, stream->dll.bw);
-    }
-
-    if (pwsink->match) {
-	pwsink->match->rate = corr;
-	SPA_FLAG_UPDATE(pwsink->match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, true);
-    }
+  switch (pwsink->slave_method) {
+    case GST_PIPEWIRE_SINK_SLAVE_METHOD_NONE:
+      break;
+    case GST_PIPEWIRE_SINK_SLAVE_METHOD_RESAMPLE:
+      rate_match_resample(pwsink);
+      break;
   }
 }
 
diff --git a/src/gst/gstpipewiresink.h b/src/gst/gstpipewiresink.h
index 33d7b5b4f..306297d0e 100644
--- a/src/gst/gstpipewiresink.h
+++ b/src/gst/gstpipewiresink.h
@@ -37,6 +37,22 @@ typedef enum
 
 #define GST_TYPE_PIPEWIRE_SINK_MODE (gst_pipewire_sink_mode_get_type ())
 
+
+/**
+ * GstPipeWireSinkSlaveMethod:
+ * @GST_PIPEWIRE_SINK_SLAVE_METHOD_NONE: no clock and timestamp slaving
+ * @GST_PIPEWIRE_SINK_SLAVE_METHOD_RESAMPLE: resample audio
+ *
+ * Different clock slaving methods
+ */
+typedef enum
+{
+  GST_PIPEWIRE_SINK_SLAVE_METHOD_NONE,
+  GST_PIPEWIRE_SINK_SLAVE_METHOD_RESAMPLE,
+} GstPipeWireSinkSlaveMethod;
+
+#define GST_TYPE_PIPEWIRE_SINK_SLAVE_METHOD (gst_pipewire_sink_slave_method_get_type ())
+
 /**
  * GstPipeWireSink:
  *
@@ -53,9 +69,10 @@ struct _GstPipeWireSink {
   gboolean rate_match;
   gint rate;
 
-  GstPipeWireSinkMode mode;
-
   struct spa_io_rate_match *match;
+
+  GstPipeWireSinkMode mode;
+  GstPipeWireSinkSlaveMethod slave_method;
 };
 
 GType gst_pipewire_sink_mode_get_type (void);
-- 
2.48.0