Hi,

Following the discussion with Tanu, here's a second patch to announce
the correct latency in raop-sink.

This patch makes latency configurable in module-raop-sink, with a
default of 0 as it is right now.

It also makes module-raop-discover pass a latency= parameter to
module-raop-sink, and I made it so the latency is set depending on the
device model. Like USB VIDs/PIDs in the kernel, the best would be to
complete the list of device models/latencies when people try it on
other hardware.
I've set the latency to the correct value for my Pioneer N-30, and made
it so it returns 2s for Apple Airport Express. (I couldn't test Apple
hardware, though.)

Finally, I added a force_latency= parameter to module-raop-discover,
which, if set, is passed down to the module-raop-sinks it spawns when
it discovers AirPlay hardware, taking precedence over the
guess_latency_from_device() function.

Advice is welcome :)
-- 
Colin
From 84aff2424129a18b14d96b6fc4645428ea2739e0 Mon Sep 17 00:00:00 2001
From: Colin Leroy <[email protected]>
Date: Sat, 22 Jul 2017 14:50:40 +0200
Subject: [PATCH 1/2] RAOP: Announce real latency

Use predefined values depending on the server, and make it configurable.
AirPlay is supposed to have 2s of latency. With my hardware, this is
more 2.352 seconds after numerous tests.
---
 src/modules/raop/module-raop-discover.c | 42 +++++++++++++++++++++++++++++++++
 src/modules/raop/module-raop-sink.c     |  4 +++-
 src/modules/raop/raop-sink.c            | 11 +++++++++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
index 9c7ac3cd..1a47f727 100644
--- a/src/modules/raop/module-raop-discover.c
+++ b/src/modules/raop/module-raop-discover.c
@@ -49,6 +49,8 @@ PA_MODULE_AUTHOR("Colin Guthrie");
 PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of RAOP devices");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(true);
+PA_MODULE_USAGE(
+        "force_latency=<audio latency in usec - applies to all devices> ");
 
 #define SERVICE_TYPE_SINK "_raop._tcp"
 
@@ -61,9 +63,11 @@ struct userdata {
     AvahiServiceBrowser *sink_browser;
 
     pa_hashmap *tunnels;
+    uint32_t force_latency;
 };
 
 static const char* const valid_modargs[] = {
+    "force_latency",
     NULL
 };
 
@@ -127,6 +131,25 @@ static void tunnel_free(struct tunnel *t) {
     pa_xfree(t);
 }
 
+/* This functions returns RAOP audio latency as guessed by the
+ * device model header.
+ * Feel free to complete the possible values after testing with
+ * your hardware.
+ */
+static uint32_t guess_latency_from_device(const char *model) {
+    uint32_t default_latency = 2000000;
+
+    if (pa_streq(model, "AppleTV2,1")) {
+        default_latency = 2000000;
+    } else if (pa_streq(model, "PIONEER,1")) {
+        /* Pioneer N-30 */
+        default_latency = 2352000;
+    }
+
+    pa_log_debug("Default latency is %u for device model %s.", default_latency, model);
+    return default_latency;
+}
+
 static void resolver_cb(
         AvahiServiceResolver *r,
         AvahiIfIndex interface, AvahiProtocol protocol,
@@ -145,6 +168,7 @@ static void resolver_cb(
     char at[AVAHI_ADDRESS_STR_MAX];
     AvahiStringList *l;
     pa_module *m;
+    uint32_t latency = 0;
 
     pa_assert(u);
 
@@ -226,6 +250,9 @@ static void resolver_cb(
             /* Sample rate */
             pa_xfree(sr);
             sr = pa_xstrdup(value);
+        } else if (pa_streq(key, "am")) {
+            /* Device model */
+            latency = guess_latency_from_device(value);
         }
 
         avahi_free(key);
@@ -308,6 +335,16 @@ static void resolver_cb(
         pa_xfree(t);
     }
 
+    if (u->force_latency > 0) {
+        t = args;
+        args = pa_sprintf_malloc("%s latency=%u", args, u->force_latency);
+        pa_xfree(t);
+    } else if (latency > 0) {
+        t = args;
+        args = pa_sprintf_malloc("%s latency=%u", args, latency);
+        pa_xfree(t);
+    }
+
     pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
 
     if ((m = pa_module_load(u->core, "module-raop-sink", args))) {
@@ -426,6 +463,7 @@ int pa__init(pa_module *m) {
     struct userdata *u;
     pa_modargs *ma = NULL;
     int error;
+    uint32_t l;
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments.");
@@ -437,6 +475,10 @@ int pa__init(pa_module *m) {
     u->module = m;
     u->sink_browser = NULL;
 
+    if (pa_modargs_get_value_u32(ma, "force_latency", &l) == 0) {
+        u->force_latency = l;
+    }
+
     u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
 
     u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index 82fa48d9..bc1d0d67 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -46,7 +46,8 @@ PA_MODULE_USAGE(
         "rate=<sample rate> "
         "channels=<number of channels> "
         "username=<authentication user name, default: \"iTunes\"> "
-        "password=<authentication password>");
+        "password=<authentication password> "
+        "latency=<audio latency in usec>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -61,6 +62,7 @@ static const char* const valid_modargs[] = {
     "channels",
     "username",
     "password",
+    "latency",
     NULL
 };
 
diff --git a/src/modules/raop/raop-sink.c b/src/modules/raop/raop-sink.c
index e5d219e8..ae990d10 100644
--- a/src/modules/raop/raop-sink.c
+++ b/src/modules/raop/raop-sink.c
@@ -87,6 +87,8 @@ struct userdata {
     pa_usec_t start;
     pa_smoother *smoother;
     uint64_t write_count;
+
+    uint32_t audio_latency;
 };
 
 enum {
@@ -119,6 +121,9 @@ static int64_t sink_get_latency(const struct userdata *u) {
 
     latency = pa_bytes_to_usec(u->write_count, &u->sink->sample_spec) - (int64_t) now;
 
+    /* RAOP default latency */
+    latency += u->audio_latency;
+
     return latency;
 }
 
@@ -461,6 +466,7 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
     const char /* *username, */ *password;
     pa_sink_new_data data;
     const char *name = NULL;
+    uint32_t l;
 
     pa_assert(m);
     pa_assert(ma);
@@ -488,6 +494,11 @@ pa_sink* pa_raop_sink_new(pa_module *m, pa_modargs *ma, const char *driver) {
     u->rtpoll = pa_rtpoll_new();
     u->rtpoll_item = NULL;
 
+    if (pa_modargs_get_value_u32(ma, "latency", &l) == 0) {
+        u->audio_latency = l;
+    }
+
+
     if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
         pa_log("pa_thread_mq_init() failed.");
         goto fail;
-- 
2.11.0

Attachment: pgp2BwWQrCJ4D.pgp
Description: OpenPGP digital signature

_______________________________________________
pulseaudio-discuss mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss

Reply via email to