]> code.delx.au - pulseaudio/blobdiff - src/modules/module-tunnel.c
tunnel: Fix inverted if condition
[pulseaudio] / src / modules / module-tunnel.c
index a46d6e599f22b5f014eff278a4b73c8b658a1c8b..6b3512e26413a64113900cf22b7c829e242091fe 100644 (file)
@@ -6,7 +6,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef HAVE_X11
+#include <xcb/xcb.h>
+#endif
+
+#include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/version.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/version.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
 #include <pulsecore/pdispatch.h>
 #include <pulsecore/pstream.h>
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/socket-client.h>
 #include <pulsecore/pdispatch.h>
 #include <pulsecore/pstream.h>
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/socket-client.h>
-#include <pulsecore/socket-util.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/thread-mq.h>
-#include <pulsecore/rtclock.h>
+#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/proplist-util.h>
 #include <pulsecore/auth-cookie.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/proplist-util.h>
 #include <pulsecore/auth-cookie.h>
+#include <pulsecore/mcalign.h>
+#include <pulsecore/strlist.h>
+
+#ifdef HAVE_X11
+#include <pulsecore/x11prop.h>
+#endif
 
 #ifdef TUNNEL_SINK
 #include "module-tunnel-sink-symdef.h"
 
 #ifdef TUNNEL_SINK
 #include "module-tunnel-sink-symdef.h"
 #include "module-tunnel-source-symdef.h"
 #endif
 
 #include "module-tunnel-source-symdef.h"
 #endif
 
+#define ENV_DEFAULT_SINK "PULSE_SINK"
+#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
+#define ENV_DEFAULT_SERVER "PULSE_SERVER"
+#define ENV_COOKIE_FILE "PULSE_COOKIE"
+
 #ifdef TUNNEL_SINK
 PA_MODULE_DESCRIPTION("Tunnel module for sinks");
 PA_MODULE_USAGE(
 #ifdef TUNNEL_SINK
 PA_MODULE_DESCRIPTION("Tunnel module for sinks");
 PA_MODULE_USAGE(
+        "sink_name=<name for the local sink> "
+        "sink_properties=<properties for the local sink> "
+        "auto=<determine server/sink/cookie automatically> "
         "server=<address> "
         "sink=<remote sink name> "
         "cookie=<filename> "
         "format=<sample format> "
         "channels=<number of channels> "
         "rate=<sample rate> "
         "server=<address> "
         "sink=<remote sink name> "
         "cookie=<filename> "
         "format=<sample format> "
         "channels=<number of channels> "
         "rate=<sample rate> "
-        "sink_name=<name for the local sink> "
         "channel_map=<channel map>");
 #else
 PA_MODULE_DESCRIPTION("Tunnel module for sources");
 PA_MODULE_USAGE(
         "channel_map=<channel map>");
 #else
 PA_MODULE_DESCRIPTION("Tunnel module for sources");
 PA_MODULE_USAGE(
+        "source_name=<name for the local source> "
+        "source_properties=<properties for the local source> "
+        "auto=<determine server/source/cookie automatically> "
         "server=<address> "
         "source=<remote source name> "
         "cookie=<filename> "
         "format=<sample format> "
         "channels=<number of channels> "
         "rate=<sample rate> "
         "server=<address> "
         "source=<remote source name> "
         "cookie=<filename> "
         "format=<sample format> "
         "channels=<number of channels> "
         "rate=<sample rate> "
-        "source_name=<name for the local source> "
         "channel_map=<channel map>");
 #endif
 
 PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_VERSION(PACKAGE_VERSION);
         "channel_map=<channel map>");
 #endif
 
 PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_LOAD_ONCE(false);
 
 static const char* const valid_modargs[] = {
 
 static const char* const valid_modargs[] = {
+    "auto",
     "server",
     "cookie",
     "format",
     "server",
     "cookie",
     "format",
@@ -97,9 +116,11 @@ static const char* const valid_modargs[] = {
     "rate",
 #ifdef TUNNEL_SINK
     "sink_name",
     "rate",
 #ifdef TUNNEL_SINK
     "sink_name",
+    "sink_properties",
     "sink",
 #else
     "source_name",
     "sink",
 #else
     "source_name",
+    "source_properties",
     "source",
 #endif
     "channel_map",
     "source",
 #endif
     "channel_map",
@@ -108,7 +129,7 @@ static const char* const valid_modargs[] = {
 
 #define DEFAULT_TIMEOUT 5
 
 
 #define DEFAULT_TIMEOUT 5
 
-#define LATENCY_INTERVAL 10
+#define LATENCY_INTERVAL (10*PA_USEC_PER_SEC)
 
 #define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
 
 
 #define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
 
@@ -145,6 +166,8 @@ static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t t
 static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 #ifdef TUNNEL_SINK
 
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 #ifdef TUNNEL_SINK
@@ -159,7 +182,12 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
     [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
     [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
     [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
     [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
     [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
-    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved
+    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
+    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_RECORD_STREAM_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_CLIENT_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed,
+    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed
 };
 
 struct userdata {
 };
 
 struct userdata {
@@ -182,6 +210,7 @@ struct userdata {
 #else
     char *source_name;
     pa_source *source;
 #else
     char *source_name;
     pa_source *source;
+    pa_mcalign *mcalign;
 #endif
 
     pa_auth_cookie *auth_cookie;
 #endif
 
     pa_auth_cookie *auth_cookie;
@@ -193,11 +222,11 @@ struct userdata {
 
     int64_t counter, counter_delta;
 
 
     int64_t counter, counter_delta;
 
-    pa_bool_t remote_corked:1;
-    pa_bool_t remote_suspended:1;
+    bool remote_corked:1;
+    bool remote_suspended:1;
 
 
-    pa_usec_t transport_usec;
-    pa_bool_t transport_usec_valid;
+    pa_usec_t transport_usec; /* maintained in the main thread */
+    pa_usec_t thread_transport_usec; /* maintained in the IO thread */
 
     uint32_t ignore_latency_before;
 
 
     uint32_t ignore_latency_before;
 
@@ -221,6 +250,11 @@ struct userdata {
 
 static void request_latency(struct userdata *u);
 
 
 static void request_latency(struct userdata *u);
 
+/* Called from main context */
+static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_log_debug("Got stream or client event.");
+}
+
 /* Called from main context */
 static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
 /* Called from main context */
 static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
@@ -231,7 +265,7 @@ static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t
     pa_assert(u->pdispatch == pd);
 
     pa_log_warn("Stream killed");
     pa_assert(u->pdispatch == pd);
 
     pa_log_warn("Stream killed");
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -251,7 +285,7 @@ static void command_overflow_or_underflow(pa_pdispatch *pd,  uint32_t command,
 static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
     uint32_t channel;
 static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
     uint32_t channel;
-    pa_bool_t suspended;
+    bool suspended;
 
     pa_assert(pd);
     pa_assert(t);
 
     pa_assert(pd);
     pa_assert(t);
@@ -261,11 +295,14 @@ static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
         pa_tagstruct_get_boolean(t, &suspended) < 0 ||
         !pa_tagstruct_eof(t)) {
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
         pa_tagstruct_get_boolean(t, &suspended) < 0 ||
         !pa_tagstruct_eof(t)) {
-        pa_log("Invalid packet");
-        pa_module_unload_request(u->module, TRUE);
+
+        pa_log("Invalid packet.");
+        pa_module_unload_request(u->module, true);
         return;
     }
 
         return;
     }
 
+    pa_log_debug("Server reports device suspend.");
+
 #ifdef TUNNEL_SINK
     pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
 #else
 #ifdef TUNNEL_SINK
     pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
 #else
@@ -278,13 +315,78 @@ static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag
 /* Called from main context */
 static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
 /* Called from main context */
 static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
+    uint32_t channel, di;
+    const char *dn;
+    bool suspended;
 
     pa_assert(pd);
     pa_assert(t);
     pa_assert(u);
     pa_assert(u->pdispatch == pd);
 
 
     pa_assert(pd);
     pa_assert(t);
     pa_assert(u);
     pa_assert(u->pdispatch == pd);
 
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &di) < 0 ||
+        pa_tagstruct_gets(t, &dn) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0) {
+
+        pa_log_error("Invalid packet.");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
     pa_log_debug("Server reports a stream move.");
     pa_log_debug("Server reports a stream move.");
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
+#endif
+
+    request_latency(u);
+}
+
+static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t channel, maxlength, tlength = 0, fragsize, prebuf, minreq;
+    pa_usec_t usec;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &maxlength) < 0) {
+
+        pa_log_error("Invalid packet.");
+        pa_module_unload_request(u->module, true);
+        return;
+    }
+
+    if (command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED) {
+        if (pa_tagstruct_getu32(t, &fragsize) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+
+            pa_log_error("Invalid packet.");
+            pa_module_unload_request(u->module, true);
+            return;
+        }
+    } else {
+        if (pa_tagstruct_getu32(t, &tlength) < 0 ||
+            pa_tagstruct_getu32(t, &prebuf) < 0 ||
+            pa_tagstruct_getu32(t, &minreq) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+
+            pa_log_error("Invalid packet.");
+            pa_module_unload_request(u->module, true);
+            return;
+        }
+    }
+
+#ifdef TUNNEL_SINK
+    pa_log_debug("Server reports buffer attrs changed. tlength now at %lu, before %lu.", (unsigned long) tlength, (unsigned long) u->tlength);
+#endif
+
     request_latency(u);
 }
 
     request_latency(u);
 }
 
@@ -292,7 +394,7 @@ static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa
 
 /* Called from main context */
 static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
 
 /* Called from main context */
 static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-   struct userdata *u = userdata;
+    struct userdata *u = userdata;
 
     pa_assert(pd);
     pa_assert(t);
 
     pa_assert(pd);
     pa_assert(t);
@@ -306,30 +408,41 @@ static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 #endif
 
 /* Called from IO thread context */
 #endif
 
 /* Called from IO thread context */
-static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
+static void check_smoother_status(struct userdata *u, bool past) {
     pa_usec_t x;
     pa_usec_t x;
+
     pa_assert(u);
 
     pa_assert(u);
 
-    if (u->remote_corked == cork)
-        return;
+    x = pa_rtclock_now();
 
 
-    u->remote_corked = cork;
-    x = pa_rtclock_usec();
+    /* Correct by the time the requested issued needs to travel to the
+     * other side.  This is a valid thread-safe access, because the
+     * main thread is waiting for us */
 
 
-    /* Correct by the time this needs to travel to the other side.
-     * This is a valid thread-safe access, because the main thread is
-     * waiting for us */
-    if (u->transport_usec_valid)
-        x += u->transport_usec;
+    if (past)
+        x -= u->thread_transport_usec;
+    else
+        x += u->thread_transport_usec;
 
     if (u->remote_suspended || u->remote_corked)
         pa_smoother_pause(u->smoother, x);
     else
 
     if (u->remote_suspended || u->remote_corked)
         pa_smoother_pause(u->smoother, x);
     else
-        pa_smoother_resume(u->smoother, x);
+        pa_smoother_resume(u->smoother, x, true);
+}
+
+/* Called from IO thread context */
+static void stream_cork_within_thread(struct userdata *u, bool cork) {
+    pa_assert(u);
+
+    if (u->remote_corked == cork)
+        return;
+
+    u->remote_corked = cork;
+    check_smoother_status(u, false);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
-static void stream_cork(struct userdata *u, pa_bool_t cork) {
+static void stream_cork(struct userdata *u, bool cork) {
     pa_tagstruct *t;
     pa_assert(u);
 
     pa_tagstruct *t;
     pa_assert(u);
 
@@ -351,27 +464,14 @@ static void stream_cork(struct userdata *u, pa_bool_t cork) {
 }
 
 /* Called from IO thread context */
 }
 
 /* Called from IO thread context */
-static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) {
-    pa_usec_t x;
+static void stream_suspend_within_thread(struct userdata *u, bool suspend) {
     pa_assert(u);
 
     if (u->remote_suspended == suspend)
         return;
 
     u->remote_suspended = suspend;
     pa_assert(u);
 
     if (u->remote_suspended == suspend)
         return;
 
     u->remote_suspended = suspend;
-
-    x = pa_rtclock_usec();
-
-    /* Correct by the time this needed to travel from the other side.
-     * This is a valid thread-safe access, because the main thread is
-     * waiting for us */
-    if (u->transport_usec_valid)
-        x -= u->transport_usec;
-
-    if (u->remote_suspended || u->remote_corked)
-        pa_smoother_pause(u->smoother, x);
-    else
-        pa_smoother_resume(u->smoother, x);
+    check_smoother_status(u, true);
 }
 
 #ifdef TUNNEL_SINK
 }
 
 #ifdef TUNNEL_SINK
@@ -402,12 +502,12 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
         case PA_SINK_MESSAGE_SET_STATE: {
             int r;
 
         case PA_SINK_MESSAGE_SET_STATE: {
             int r;
 
-            /* First, change the state, because otherwide pa_sink_render() would fail */
+            /* First, change the state, because otherwise pa_sink_render() would fail */
             if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
 
             if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
 
-                stream_cork_within_thread(u, u->sink->state == PA_SINK_SUSPENDED);
+                stream_cork_within_thread(u, u->sink->thread_info.state == PA_SINK_SUSPENDED);
 
 
-                if (PA_SINK_IS_OPENED(u->sink->state))
+                if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
                     send_data(u);
             }
 
                     send_data(u);
             }
 
@@ -418,7 +518,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             pa_usec_t yl, yr, *usec = data;
 
             yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
             pa_usec_t yl, yr, *usec = data;
 
             yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
-            yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
+            yr = pa_smoother_get(u->smoother, pa_rtclock_now());
 
             *usec = yl > yr ? yl - yr : 0;
             return 0;
 
             *usec = yl > yr ? yl - yr : 0;
             return 0;
@@ -434,24 +534,25 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
             return 0;
 
 
             return 0;
 
-
         case SINK_MESSAGE_REMOTE_SUSPEND:
 
             stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
             return 0;
 
         case SINK_MESSAGE_REMOTE_SUSPEND:
 
             stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
             return 0;
 
-
         case SINK_MESSAGE_UPDATE_LATENCY: {
             pa_usec_t y;
 
             y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
 
         case SINK_MESSAGE_UPDATE_LATENCY: {
             pa_usec_t y;
 
             y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
 
-            if (y > (pa_usec_t) offset || offset < 0)
+            if (y > (pa_usec_t) offset)
                 y -= (pa_usec_t) offset;
             else
                 y = 0;
 
                 y -= (pa_usec_t) offset;
             else
                 y = 0;
 
-            pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+            pa_smoother_put(u->smoother, pa_rtclock_now(), y);
+
+            /* We can access this freely here, since the main thread is waiting for us */
+            u->thread_transport_usec = u->transport_usec;
 
             return 0;
         }
 
             return 0;
         }
@@ -483,17 +584,18 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
 
         case PA_SINK_SUSPENDED:
             pa_assert(PA_SINK_IS_OPENED(s->state));
 
         case PA_SINK_SUSPENDED:
             pa_assert(PA_SINK_IS_OPENED(s->state));
-            stream_cork(u, TRUE);
+            stream_cork(u, true);
             break;
 
         case PA_SINK_IDLE:
         case PA_SINK_RUNNING:
             if (s->state == PA_SINK_SUSPENDED)
             break;
 
         case PA_SINK_IDLE:
         case PA_SINK_RUNNING:
             if (s->state == PA_SINK_SUSPENDED)
-                stream_cork(u, FALSE);
+                stream_cork(u, false);
             break;
 
         case PA_SINK_UNLINKED:
         case PA_SINK_INIT:
             break;
 
         case PA_SINK_UNLINKED:
         case PA_SINK_INIT:
+        case PA_SINK_INVALID_STATE:
             ;
     }
 
             ;
     }
 
@@ -512,7 +614,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
             int r;
 
             if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0)
             int r;
 
             if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0)
-                stream_cork_within_thread(u, u->source->state == PA_SOURCE_SUSPENDED);
+                stream_cork_within_thread(u, u->source->thread_info.state == PA_SOURCE_SUSPENDED);
 
             return r;
         }
 
             return r;
         }
@@ -521,20 +623,29 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
             pa_usec_t yr, yl, *usec = data;
 
             yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
             pa_usec_t yr, yl, *usec = data;
 
             yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
-            yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
+            yr = pa_smoother_get(u->smoother, pa_rtclock_now());
 
             *usec = yr > yl ? yr - yl : 0;
             return 0;
         }
 
 
             *usec = yr > yl ? yr - yl : 0;
             return 0;
         }
 
-        case SOURCE_MESSAGE_POST:
+        case SOURCE_MESSAGE_POST: {
+            pa_memchunk c;
+
+            pa_mcalign_push(u->mcalign, chunk);
 
 
-            if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
-                pa_source_post(u->source, chunk);
+            while (pa_mcalign_pop(u->mcalign, &c) >= 0) {
 
 
-            u->counter += (int64_t) chunk->length;
+                if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
+                    pa_source_post(u->source, &c);
+
+                pa_memblock_unref(c.memblock);
+
+                u->counter += (int64_t) c.length;
+            }
 
             return 0;
 
             return 0;
+        }
 
         case SOURCE_MESSAGE_REMOTE_SUSPEND:
 
 
         case SOURCE_MESSAGE_REMOTE_SUSPEND:
 
@@ -545,13 +656,12 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
             pa_usec_t y;
 
             y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
             pa_usec_t y;
 
             y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
+            y += (pa_usec_t) offset;
 
 
-            if (offset >= 0 || y > (pa_usec_t) -offset)
-                y += (pa_usec_t) offset;
-            else
-                y = 0;
+            pa_smoother_put(u->smoother, pa_rtclock_now(), y);
 
 
-            pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
+            /* We can access this freely here, since the main thread is waiting for us */
+            u->thread_transport_usec = u->transport_usec;
 
             return 0;
         }
 
             return 0;
         }
@@ -570,17 +680,18 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
 
         case PA_SOURCE_SUSPENDED:
             pa_assert(PA_SOURCE_IS_OPENED(s->state));
 
         case PA_SOURCE_SUSPENDED:
             pa_assert(PA_SOURCE_IS_OPENED(s->state));
-            stream_cork(u, TRUE);
+            stream_cork(u, true);
             break;
 
         case PA_SOURCE_IDLE:
         case PA_SOURCE_RUNNING:
             if (s->state == PA_SOURCE_SUSPENDED)
             break;
 
         case PA_SOURCE_IDLE:
         case PA_SOURCE_RUNNING:
             if (s->state == PA_SOURCE_SUSPENDED)
-                stream_cork(u, FALSE);
+                stream_cork(u, false);
             break;
 
         case PA_SOURCE_UNLINKED:
         case PA_SOURCE_INIT:
             break;
 
         case PA_SOURCE_UNLINKED:
         case PA_SOURCE_INIT:
+        case PA_SINK_INVALID_STATE:
             ;
     }
 
             ;
     }
 
@@ -597,18 +708,16 @@ static void thread_func(void *userdata) {
     pa_log_debug("Thread starting up");
 
     pa_thread_mq_install(&u->thread_mq);
     pa_log_debug("Thread starting up");
 
     pa_thread_mq_install(&u->thread_mq);
-    pa_rtpoll_install(u->rtpoll);
 
     for (;;) {
         int ret;
 
 #ifdef TUNNEL_SINK
 
     for (;;) {
         int ret;
 
 #ifdef TUNNEL_SINK
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
-            if (u->sink->thread_info.rewind_requested)
-                pa_sink_process_rewind(u->sink, 0);
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
 #endif
 
 #endif
 
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0)
             goto fail;
 
         if (ret == 0)
             goto fail;
 
         if (ret == 0)
@@ -644,7 +753,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command,  uint32_t tag, p
     }
 
     if (channel != u->channel) {
     }
 
     if (channel != u->channel) {
-        pa_log("Recieved data for invalid channel");
+        pa_log("Received data for invalid channel");
         goto fail;
     }
 
         goto fail;
     }
 
@@ -652,7 +761,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command,  uint32_t tag, p
     return;
 
 fail:
     return;
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
 }
 
 #endif
 }
 
 #endif
@@ -660,8 +769,8 @@ fail:
 /* Called from main context */
 static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
 /* Called from main context */
 static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
-    pa_usec_t sink_usec, source_usec, transport_usec;
-    pa_bool_t playing;
+    pa_usec_t sink_usec, source_usec;
+    bool playing;
     int64_t write_index, read_index;
     struct timeval local, remote, now;
     pa_sample_spec *ss;
     int64_t write_index, read_index;
     struct timeval local, remote, now;
     pa_sample_spec *ss;
@@ -707,7 +816,6 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
     }
 
     if (tag < u->ignore_latency_before) {
     }
 
     if (tag < u->ignore_latency_before) {
-        request_latency(u);
         return;
     }
 
         return;
     }
 
@@ -723,7 +831,6 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
 #endif
     } else
         u->transport_usec = pa_timeval_diff(&now, &local)/2;
 #endif
     } else
         u->transport_usec = pa_timeval_diff(&now, &local)/2;
-    u->transport_usec_valid = TRUE;
 
     /* First, take the device's delay */
 #ifdef TUNNEL_SINK
 
     /* First, take the device's delay */
 #ifdef TUNNEL_SINK
@@ -743,9 +850,9 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
     /* Our measurements are already out of date, hence correct by the     *
      * transport latency */
 #ifdef TUNNEL_SINK
     /* Our measurements are already out of date, hence correct by the     *
      * transport latency */
 #ifdef TUNNEL_SINK
-    delay -= (int64_t) transport_usec;
+    delay -= (int64_t) u->transport_usec;
 #else
 #else
-    delay += (int64_t) transport_usec;
+    delay += (int64_t) u->transport_usec;
 #endif
 
     /* Now correct by what we have have read/written since we requested the update */
 #endif
 
     /* Now correct by what we have have read/written since we requested the update */
@@ -765,7 +872,7 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
 
 fail:
 
 
 fail:
 
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -784,8 +891,7 @@ static void request_latency(struct userdata *u) {
     pa_tagstruct_putu32(t, tag = u->ctag++);
     pa_tagstruct_putu32(t, u->channel);
 
     pa_tagstruct_putu32(t, tag = u->ctag++);
     pa_tagstruct_putu32(t, u->channel);
 
-    pa_gettimeofday(&now);
-    pa_tagstruct_put_timeval(t, &now);
+    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
 
     pa_pstream_send_tagstruct(u->pstream, t);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
 
     pa_pstream_send_tagstruct(u->pstream, t);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
@@ -795,9 +901,8 @@ static void request_latency(struct userdata *u) {
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e,  const struct timeval *tv, void *userdata) {
+static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
     struct userdata *u = userdata;
     struct userdata *u = userdata;
-    struct timeval ntv;
 
     pa_assert(m);
     pa_assert(e);
 
     pa_assert(m);
     pa_assert(e);
@@ -805,9 +910,7 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e,  const struct
 
     request_latency(u);
 
 
     request_latency(u);
 
-    pa_gettimeofday(&ntv);
-    ntv.tv_sec += LATENCY_INTERVAL;
-    m->time_restart(e, &ntv);
+    pa_core_rttime_restart(u->core, e, pa_rtclock_now() + LATENCY_INTERVAL);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -859,6 +962,7 @@ static void update_description(struct userdata *u) {
 static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
     pa_sample_spec ss;
 static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
     pa_sample_spec ss;
+    pa_channel_map cm;
     const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
     uint32_t cookie;
 
     const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
     uint32_t cookie;
 
@@ -880,7 +984,8 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_gets(t, &default_sink_name) < 0 ||
         pa_tagstruct_gets(t, &default_source_name) < 0 ||
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_gets(t, &default_sink_name) < 0 ||
         pa_tagstruct_gets(t, &default_source_name) < 0 ||
-        pa_tagstruct_getu32(t, &cookie) < 0) {
+        pa_tagstruct_getu32(t, &cookie) < 0 ||
+        (u->version >= 15 && pa_tagstruct_get_channel_map(t, &cm) < 0)) {
 
         pa_log("Parse failure");
         goto fail;
 
         pa_log("Parse failure");
         goto fail;
@@ -902,7 +1007,62 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
     return;
 
 fail:
     return;
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
+}
+
+static int read_ports(struct userdata *u, pa_tagstruct *t) {
+    if (u->version >= 16) {
+        uint32_t n_ports;
+        const char *s;
+
+        if (pa_tagstruct_getu32(t, &n_ports)) {
+            pa_log("Parse failure");
+            return -PA_ERR_PROTOCOL;
+        }
+
+        for (uint32_t j = 0; j < n_ports; j++) {
+            uint32_t priority;
+
+            if (pa_tagstruct_gets(t, &s) < 0 || /* name */
+                pa_tagstruct_gets(t, &s) < 0 || /* description */
+                pa_tagstruct_getu32(t, &priority) < 0) {
+
+                pa_log("Parse failure");
+                return -PA_ERR_PROTOCOL;
+            }
+            if (u->version >= 24 && pa_tagstruct_getu32(t, &priority) < 0) { /* available */
+                pa_log("Parse failure");
+                return -PA_ERR_PROTOCOL;
+            }
+        }
+
+        if (pa_tagstruct_gets(t, &s) < 0) { /* active port */
+            pa_log("Parse failure");
+            return -PA_ERR_PROTOCOL;
+        }
+    }
+    return 0;
+}
+
+static int read_formats(struct userdata *u, pa_tagstruct *t) {
+    uint8_t n_formats;
+    pa_format_info *format;
+
+    if (pa_tagstruct_getu8(t, &n_formats) < 0) { /* no. of formats */
+        pa_log("Parse failure");
+        return -PA_ERR_PROTOCOL;
+    }
+
+    for (uint8_t j = 0; j < n_formats; j++) {
+        format = pa_format_info_new();
+        if (pa_tagstruct_get_format_info(t, format)) { /* format info */
+            pa_format_info_free(format);
+            pa_log("Parse failure");
+            return -PA_ERR_PROTOCOL;
+        }
+        pa_format_info_free(format);
+    }
+    return 0;
 }
 
 #ifdef TUNNEL_SINK
 }
 
 #ifdef TUNNEL_SINK
@@ -915,15 +1075,12 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t
     pa_sample_spec ss;
     pa_channel_map cm;
     pa_cvolume volume;
     pa_sample_spec ss;
     pa_channel_map cm;
     pa_cvolume volume;
-    pa_bool_t mute;
+    bool mute;
     pa_usec_t latency;
     pa_usec_t latency;
-    pa_proplist *pl;
 
     pa_assert(pd);
     pa_assert(u);
 
 
     pa_assert(pd);
     pa_assert(u);
 
-    pl = pa_proplist_new();
-
     if (command != PA_COMMAND_REPLY) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
     if (command != PA_COMMAND_REPLY) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
@@ -953,7 +1110,7 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t
     if (u->version >= 13) {
         pa_usec_t configured_latency;
 
     if (u->version >= 13) {
         pa_usec_t configured_latency;
 
-        if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+        if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
             pa_tagstruct_get_usec(t, &configured_latency) < 0) {
 
             pa_log("Parse failure");
             pa_tagstruct_get_usec(t, &configured_latency) < 0) {
 
             pa_log("Parse failure");
@@ -961,14 +1118,32 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t
         }
     }
 
         }
     }
 
+    if (u->version >= 15) {
+        pa_volume_t base_volume;
+        uint32_t state, n_volume_steps, card;
+
+        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
+            pa_tagstruct_getu32(t, &state) < 0 ||
+            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
+            pa_tagstruct_getu32(t, &card) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (read_ports(u, t) < 0)
+        goto fail;
+
+    if (u->version >= 21 && read_formats(u, t) < 0)
+        goto fail;
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
     }
 
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
     }
 
-    pa_proplist_free(pl);
-
-    if (!u->sink_name || strcmp(name, u->sink_name))
+    if (!u->sink_name || !pa_streq(name, u->sink_name))
         return;
 
     pa_xfree(u->device_description);
         return;
 
     pa_xfree(u->device_description);
@@ -979,8 +1154,7 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t
     return;
 
 fail:
     return;
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
-    pa_proplist_free(pl);
+    pa_module_unload_request(u->module, true);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -989,17 +1163,15 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag
     uint32_t idx, owner_module, client, sink;
     pa_usec_t buffer_usec, sink_usec;
     const char *name, *driver, *resample_method;
     uint32_t idx, owner_module, client, sink;
     pa_usec_t buffer_usec, sink_usec;
     const char *name, *driver, *resample_method;
-    pa_bool_t mute;
+    bool mute = false;
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
     pa_cvolume volume;
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
     pa_cvolume volume;
-    pa_proplist *pl;
+    bool b;
 
     pa_assert(pd);
     pa_assert(u);
 
 
     pa_assert(pd);
     pa_assert(u);
 
-    pl = pa_proplist_new();
-
     if (command != PA_COMMAND_REPLY) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
     if (command != PA_COMMAND_REPLY) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
@@ -1034,40 +1206,64 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag
     }
 
     if (u->version >= 13) {
     }
 
     if (u->version >= 13) {
-        if (pa_tagstruct_get_proplist(t, pl) < 0) {
+        if (pa_tagstruct_get_proplist(t, NULL) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 19) {
+        if (pa_tagstruct_get_boolean(t, &b) < 0) {
 
             pa_log("Parse failure");
             goto fail;
         }
     }
 
 
             pa_log("Parse failure");
             goto fail;
         }
     }
 
+    if (u->version >= 20) {
+        if (pa_tagstruct_get_boolean(t, &b) < 0 ||
+            pa_tagstruct_get_boolean(t, &b) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 21) {
+        pa_format_info *format = pa_format_info_new();
+
+        if (pa_tagstruct_get_format_info(t, format) < 0) {
+            pa_format_info_free(format);
+            pa_log("Parse failure");
+            goto fail;
+        }
+        pa_format_info_free(format);
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
     }
 
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
     }
 
-    pa_proplist_free(pl);
-
     if (idx != u->device_index)
         return;
 
     pa_assert(u->sink);
 
     if ((u->version < 11 || !!mute == !!u->sink->muted) &&
     if (idx != u->device_index)
         return;
 
     pa_assert(u->sink);
 
     if ((u->version < 11 || !!mute == !!u->sink->muted) &&
-        pa_cvolume_equal(&volume, &u->sink->volume))
+        pa_cvolume_equal(&volume, &u->sink->real_volume))
         return;
 
         return;
 
-    memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+    pa_sink_volume_changed(u->sink, &volume);
 
     if (u->version >= 11)
 
     if (u->version >= 11)
-        u->sink->muted = !!mute;
+        pa_sink_mute_changed(u->sink, mute);
 
 
-    pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
     return;
 
 fail:
     return;
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
-    pa_proplist_free(pl);
+    pa_module_unload_request(u->module, true);
 }
 
 #else
 }
 
 #else
@@ -1080,15 +1276,12 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
     pa_sample_spec ss;
     pa_channel_map cm;
     pa_cvolume volume;
     pa_sample_spec ss;
     pa_channel_map cm;
     pa_cvolume volume;
-    pa_bool_t mute;
+    bool mute;
     pa_usec_t latency, configured_latency;
     pa_usec_t latency, configured_latency;
-    pa_proplist *pl;
 
     pa_assert(pd);
     pa_assert(u);
 
 
     pa_assert(pd);
     pa_assert(u);
 
-    pl = pa_proplist_new();
-
     if (command != PA_COMMAND_REPLY) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
     if (command != PA_COMMAND_REPLY) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
@@ -1116,7 +1309,7 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
     }
 
     if (u->version >= 13) {
     }
 
     if (u->version >= 13) {
-        if (pa_tagstruct_get_proplist(t, pl) < 0 ||
+        if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
             pa_tagstruct_get_usec(t, &configured_latency) < 0) {
 
             pa_log("Parse failure");
             pa_tagstruct_get_usec(t, &configured_latency) < 0) {
 
             pa_log("Parse failure");
@@ -1124,14 +1317,32 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
         }
     }
 
         }
     }
 
+    if (u->version >= 15) {
+        pa_volume_t base_volume;
+        uint32_t state, n_volume_steps, card;
+
+        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
+            pa_tagstruct_getu32(t, &state) < 0 ||
+            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
+            pa_tagstruct_getu32(t, &card) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (read_ports(u, t) < 0)
+        goto fail;
+
+    if (u->version >= 22 && read_formats(u, t) < 0)
+        goto fail;
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
     }
 
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
     }
 
-    pa_proplist_free(pl);
-
-    if (!u->source_name || strcmp(name, u->source_name))
+    if (!u->source_name || !pa_streq(name, u->source_name))
         return;
 
     pa_xfree(u->device_description);
         return;
 
     pa_xfree(u->device_description);
@@ -1142,8 +1353,7 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
     return;
 
 fail:
     return;
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
-    pa_proplist_free(pl);
+    pa_module_unload_request(u->module, true);
 }
 
 #endif
 }
 
 #endif
@@ -1204,7 +1414,7 @@ static void command_subscribe_event(pa_pdispatch *pd,  uint32_t command,  uint32
     if (pa_tagstruct_getu32(t, &e) < 0 ||
         pa_tagstruct_getu32(t, &idx) < 0) {
         pa_log("Invalid protocol reply");
     if (pa_tagstruct_getu32(t, &e) < 0 ||
         pa_tagstruct_getu32(t, &idx) < 0) {
         pa_log("Invalid protocol reply");
-        pa_module_unload_request(u->module, TRUE);
+        pa_module_unload_request(u->module, true);
         return;
     }
 
         return;
     }
 
@@ -1224,12 +1434,11 @@ static void command_subscribe_event(pa_pdispatch *pd,  uint32_t command,  uint32
 /* Called from main context */
 static void start_subscribe(struct userdata *u) {
     pa_tagstruct *t;
 /* Called from main context */
 static void start_subscribe(struct userdata *u) {
     pa_tagstruct *t;
-    uint32_t tag;
     pa_assert(u);
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
     pa_assert(u);
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
-    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->ctag++);
     pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
 #ifdef TUNNEL_SINK
                         PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
     pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
 #ifdef TUNNEL_SINK
                         PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
@@ -1244,7 +1453,6 @@ static void start_subscribe(struct userdata *u) {
 /* Called from main context */
 static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
 /* Called from main context */
 static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
-    struct timeval ntv;
 #ifdef TUNNEL_SINK
     uint32_t bytes;
 #endif
 #ifdef TUNNEL_SINK
     uint32_t bytes;
 #endif
@@ -1288,7 +1496,7 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t
         pa_channel_map cm;
         uint32_t device_index;
         const char *dn;
         pa_channel_map cm;
         uint32_t device_index;
         const char *dn;
-        pa_bool_t suspended;
+        bool suspended;
 
         if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
             pa_tagstruct_get_channel_map(t, &cm) < 0 ||
 
         if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
             pa_tagstruct_get_channel_map(t, &cm) < 0 ||
@@ -1312,11 +1520,22 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t
         if (pa_tagstruct_get_usec(t, &usec) < 0)
             goto parse_error;
 
         if (pa_tagstruct_get_usec(t, &usec) < 0)
             goto parse_error;
 
-#ifdef TUNNEL_SINK
-        pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0);
-#else
-        pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0);
-#endif
+/* #ifdef TUNNEL_SINK */
+/*         pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); */
+/* #else */
+/*         pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); */
+/* #endif */
+    }
+
+    if (u->version >= 21) {
+        pa_format_info *format = pa_format_info_new();
+
+        if (pa_tagstruct_get_format_info(t, format) < 0) {
+            pa_format_info_free(format);
+            goto parse_error;
+        }
+
+        pa_format_info_free(format);
     }
 
     if (!pa_tagstruct_eof(t))
     }
 
     if (!pa_tagstruct_eof(t))
@@ -1326,9 +1545,7 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t
     request_info(u);
 
     pa_assert(!u->time_event);
     request_info(u);
 
     pa_assert(!u->time_event);
-    pa_gettimeofday(&ntv);
-    ntv.tv_sec += LATENCY_INTERVAL;
-    u->time_event = u->core->mainloop->time_new(u->core->mainloop, &ntv, timeout_callback, u);
+    u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + LATENCY_INTERVAL, timeout_callback, u);
 
     request_latency(u);
 
 
     request_latency(u);
 
@@ -1344,7 +1561,7 @@ parse_error:
     pa_log("Invalid reply. (Create stream)");
 
 fail:
     pa_log("Invalid reply. (Create stream)");
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
 
 }
 
 
 }
 
@@ -1353,9 +1570,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     struct userdata *u = userdata;
     pa_tagstruct *reply;
     char name[256], un[128], hn[128];
     struct userdata *u = userdata;
     pa_tagstruct *reply;
     char name[256], un[128], hn[128];
-#ifdef TUNNEL_SINK
     pa_cvolume volume;
     pa_cvolume volume;
-#endif
 
     pa_assert(pd);
     pa_assert(u);
 
     pa_assert(pd);
     pa_assert(u);
@@ -1389,11 +1604,17 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
 
 #ifdef TUNNEL_SINK
     pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
 
 #ifdef TUNNEL_SINK
+    pa_proplist_setf(u->sink->proplist, "tunnel.remote_version", "%u", u->version);
+    pa_sink_update_proplist(u->sink, 0, NULL);
+
     pa_snprintf(name, sizeof(name), "%s for %s@%s",
                 u->sink_name,
                 pa_get_user_name(un, sizeof(un)),
                 pa_get_host_name(hn, sizeof(hn)));
 #else
     pa_snprintf(name, sizeof(name), "%s for %s@%s",
                 u->sink_name,
                 pa_get_user_name(un, sizeof(un)),
                 pa_get_host_name(hn, sizeof(hn)));
 #else
+    pa_proplist_setf(u->source->proplist, "tunnel.remote_version", "%u", u->version);
+    pa_source_update_proplist(u->source, 0, NULL);
+
     pa_snprintf(name, sizeof(name), "%s for %s@%s",
                 u->source_name,
                 pa_get_user_name(un, sizeof(un)),
     pa_snprintf(name, sizeof(name), "%s for %s@%s",
                 u->source_name,
                 pa_get_user_name(un, sizeof(un)),
@@ -1402,14 +1623,14 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
 
     reply = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
 
     reply = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
-    pa_tagstruct_putu32(reply, tag = u->ctag++);
+    pa_tagstruct_putu32(reply, u->ctag++);
 
     if (u->version >= 13) {
         pa_proplist *pl;
         pl = pa_proplist_new();
 
     if (u->version >= 13) {
         pa_proplist *pl;
         pl = pa_proplist_new();
-        pa_init_proplist(pl);
         pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
         pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
         pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
         pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+        pa_init_proplist(pl);
         pa_tagstruct_put_proplist(reply, pl);
         pa_proplist_free(pl);
     } else
         pa_tagstruct_put_proplist(reply, pl);
         pa_proplist_free(pl);
     } else
@@ -1468,20 +1689,20 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
 #endif
 
     if (u->version >= 12) {
 #endif
 
     if (u->version >= 12) {
-        pa_tagstruct_put_boolean(reply, FALSE); /* no_remap */
-        pa_tagstruct_put_boolean(reply, FALSE); /* no_remix */
-        pa_tagstruct_put_boolean(reply, FALSE); /* fix_format */
-        pa_tagstruct_put_boolean(reply, FALSE); /* fix_rate */
-        pa_tagstruct_put_boolean(reply, FALSE); /* fix_channels */
-        pa_tagstruct_put_boolean(reply, TRUE); /* no_move */
-        pa_tagstruct_put_boolean(reply, FALSE); /* variable_rate */
+        pa_tagstruct_put_boolean(reply, false); /* no_remap */
+        pa_tagstruct_put_boolean(reply, false); /* no_remix */
+        pa_tagstruct_put_boolean(reply, false); /* fix_format */
+        pa_tagstruct_put_boolean(reply, false); /* fix_rate */
+        pa_tagstruct_put_boolean(reply, false); /* fix_channels */
+        pa_tagstruct_put_boolean(reply, true); /* no_move */
+        pa_tagstruct_put_boolean(reply, false); /* variable_rate */
     }
 
     if (u->version >= 13) {
         pa_proplist *pl;
 
     }
 
     if (u->version >= 13) {
         pa_proplist *pl;
 
-        pa_tagstruct_put_boolean(reply, FALSE); /* start muted/peak detect*/
-        pa_tagstruct_put_boolean(reply, TRUE); /* adjust_latency */
+        pa_tagstruct_put_boolean(reply, false); /* start muted/peak detect*/
+        pa_tagstruct_put_boolean(reply, true); /* adjust_latency */
 
         pl = pa_proplist_new();
         pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
 
         pl = pa_proplist_new();
         pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
@@ -1496,11 +1717,46 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
 
     if (u->version >= 14) {
 #ifdef TUNNEL_SINK
 
     if (u->version >= 14) {
 #ifdef TUNNEL_SINK
-        pa_tagstruct_put_boolean(reply, FALSE); /* volume_set */
+        pa_tagstruct_put_boolean(reply, false); /* volume_set */
 #endif
 #endif
-        pa_tagstruct_put_boolean(reply, TRUE); /* early rquests */
+        pa_tagstruct_put_boolean(reply, true); /* early rquests */
     }
 
     }
 
+    if (u->version >= 15) {
+#ifdef TUNNEL_SINK
+        pa_tagstruct_put_boolean(reply, false); /* muted_set */
+#endif
+        pa_tagstruct_put_boolean(reply, false); /* don't inhibit auto suspend */
+        pa_tagstruct_put_boolean(reply, false); /* fail on suspend */
+    }
+
+#ifdef TUNNEL_SINK
+    if (u->version >= 17)
+        pa_tagstruct_put_boolean(reply, false); /* relative volume */
+
+    if (u->version >= 18)
+        pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
+#endif
+
+#ifdef TUNNEL_SINK
+    if (u->version >= 21) {
+        /* We're not using the extended API, so n_formats = 0 and that's that */
+        pa_tagstruct_putu8(reply, 0);
+    }
+#else
+    if (u->version >= 22) {
+        /* We're not using the extended API, so n_formats = 0 and that's that */
+        pa_tagstruct_putu8(reply, 0);
+        pa_cvolume_reset(&volume, u->source->sample_spec.channels);
+        pa_tagstruct_put_cvolume(reply, &volume);
+        pa_tagstruct_put_boolean(reply, false); /* muted */
+        pa_tagstruct_put_boolean(reply, false); /* volume_set */
+        pa_tagstruct_put_boolean(reply, false); /* muted_set */
+        pa_tagstruct_put_boolean(reply, false); /* relative volume */
+        pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
+    }
+#endif
+
     pa_pstream_send_tagstruct(u->pstream, reply);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
 
     pa_pstream_send_tagstruct(u->pstream, reply);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
 
@@ -1509,7 +1765,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     return;
 
 fail:
     return;
 
 fail:
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -1520,7 +1776,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) {
     pa_assert(u);
 
     pa_log_warn("Stream died.");
     pa_assert(u);
 
     pa_log_warn("Stream died.");
-    pa_module_unload_request(u->module, TRUE);
+    pa_module_unload_request(u->module, true);
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -1533,7 +1789,7 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_c
 
     if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) {
         pa_log("Invalid packet");
 
     if (pa_pdispatch_run(u->pdispatch, packet, creds, u) < 0) {
         pa_log("Invalid packet");
-        pa_module_unload_request(u->module, TRUE);
+        pa_module_unload_request(u->module, true);
         return;
     }
 }
         return;
     }
 }
@@ -1548,8 +1804,8 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
     pa_assert(u);
 
     if (channel != u->channel) {
     pa_assert(u);
 
     if (channel != u->channel) {
-        pa_log("Recieved memory block on bad channel.");
-        pa_module_unload_request(u->module, TRUE);
+        pa_log("Received memory block on bad channel.");
+        pa_module_unload_request(u->module, true);
         return;
     }
 
         return;
     }
 
@@ -1557,7 +1813,6 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
 
     u->counter_delta += (int64_t) chunk->length;
 }
 
     u->counter_delta += (int64_t) chunk->length;
 }
-
 #endif
 
 /* Called from main context */
 #endif
 
 /* Called from main context */
@@ -1575,17 +1830,17 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
 
     if (!io) {
         pa_log("Connection failed: %s", pa_cstrerror(errno));
 
     if (!io) {
         pa_log("Connection failed: %s", pa_cstrerror(errno));
-        pa_module_unload_request(u->module, TRUE);
+        pa_module_unload_request(u->module, true);
         return;
     }
 
     u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool);
         return;
     }
 
     u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool);
-    u->pdispatch = pa_pdispatch_new(u->core->mainloop, command_table, PA_COMMAND_MAX);
+    u->pdispatch = pa_pdispatch_new(u->core->mainloop, true, command_table, PA_COMMAND_MAX);
 
     pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
 
     pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
-    pa_pstream_set_recieve_packet_callback(u->pstream, pstream_packet_callback, u);
+    pa_pstream_set_receive_packet_callback(u->pstream, pstream_packet_callback, u);
 #ifndef TUNNEL_SINK
 #ifndef TUNNEL_SINK
-    pa_pstream_set_recieve_memblock_callback(u->pstream, pstream_memblock_callback, u);
+    pa_pstream_set_receive_memblock_callback(u->pstream, pstream_memblock_callback, u);
 #endif
 
     t = pa_tagstruct_new(NULL, 0);
 #endif
 
     t = pa_tagstruct_new(NULL, 0);
@@ -1619,10 +1874,9 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
 #ifdef TUNNEL_SINK
 
 /* Called from main context */
 #ifdef TUNNEL_SINK
 
 /* Called from main context */
-static int sink_set_volume(pa_sink *sink) {
+static void sink_set_volume(pa_sink *sink) {
     struct userdata *u;
     pa_tagstruct *t;
     struct userdata *u;
     pa_tagstruct *t;
-    uint32_t tag;
 
     pa_assert(sink);
     u = sink->userdata;
 
     pa_assert(sink);
     u = sink->userdata;
@@ -1630,35 +1884,30 @@ static int sink_set_volume(pa_sink *sink) {
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
-    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->ctag++);
     pa_tagstruct_putu32(t, u->device_index);
     pa_tagstruct_putu32(t, u->device_index);
-    pa_tagstruct_put_cvolume(t, &sink->volume);
+    pa_tagstruct_put_cvolume(t, &sink->real_volume);
     pa_pstream_send_tagstruct(u->pstream, t);
     pa_pstream_send_tagstruct(u->pstream, t);
-
-    return 0;
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
-static int sink_set_mute(pa_sink *sink) {
+static void sink_set_mute(pa_sink *sink) {
     struct userdata *u;
     pa_tagstruct *t;
     struct userdata *u;
     pa_tagstruct *t;
-    uint32_t tag;
 
     pa_assert(sink);
     u = sink->userdata;
     pa_assert(u);
 
     if (u->version < 11)
 
     pa_assert(sink);
     u = sink->userdata;
     pa_assert(u);
 
     if (u->version < 11)
-        return -1;
+        return;
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
-    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, u->ctag++);
     pa_tagstruct_putu32(t, u->device_index);
     pa_tagstruct_put_boolean(t, !!sink->muted);
     pa_pstream_send_tagstruct(u->pstream, t);
     pa_tagstruct_putu32(t, u->device_index);
     pa_tagstruct_put_boolean(t, !!sink->muted);
     pa_pstream_send_tagstruct(u->pstream, t);
-
-    return 0;
 }
 
 #endif
 }
 
 #endif
@@ -1666,6 +1915,8 @@ static int sink_set_mute(pa_sink *sink) {
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u = NULL;
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u = NULL;
+    char *server = NULL;
+    pa_strlist *server_list = NULL;
     pa_sample_spec ss;
     pa_channel_map map;
     char *dn = NULL;
     pa_sample_spec ss;
     pa_channel_map map;
     char *dn = NULL;
@@ -1674,6 +1925,11 @@ int pa__init(pa_module*m) {
 #else
     pa_source_new_data data;
 #endif
 #else
     pa_source_new_data data;
 #endif
+    bool automatic;
+#ifdef HAVE_X11
+    xcb_connection_t *xcb = NULL;
+#endif
+    const char *cookie_path;
 
     pa_assert(m);
 
 
     pa_assert(m);
 
@@ -1697,49 +1953,177 @@ int pa__init(pa_module*m) {
     u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
     u->source = NULL;
 #endif
     u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
     u->source = NULL;
 #endif
-    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            true,
+            true,
+            10,
+            pa_rtclock_now(),
+            false);
     u->ctag = 1;
     u->device_index = u->channel = PA_INVALID_INDEX;
     u->time_event = NULL;
     u->ignore_latency_before = 0;
     u->ctag = 1;
     u->device_index = u->channel = PA_INVALID_INDEX;
     u->time_event = NULL;
     u->ignore_latency_before = 0;
-    u->transport_usec = 0;
-    u->transport_usec_valid = FALSE;
-    u->remote_suspended = u->remote_corked = FALSE;
+    u->transport_usec = u->thread_transport_usec = 0;
+    u->remote_suspended = u->remote_corked = false;
     u->counter = u->counter_delta = 0;
 
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
     u->counter = u->counter_delta = 0;
 
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
-    if (!(u->auth_cookie = pa_auth_cookie_get(u->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), PA_NATIVE_COOKIE_LENGTH)))
+    if (pa_modargs_get_value_boolean(ma, "auto", &automatic) < 0) {
+        pa_log("Failed to parse argument \"auto\".");
         goto fail;
         goto fail;
+    }
 
 
-    if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
-        pa_log("No server specified.");
-        goto fail;
+    cookie_path = pa_modargs_get_value(ma, "cookie", NULL);
+    server = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL));
+
+    if (automatic) {
+#ifdef HAVE_X11
+        /* Need an X11 connection to get root properties */
+        if (getenv("DISPLAY") != NULL) {
+            if (!(xcb = xcb_connect(getenv("DISPLAY"), NULL)))
+                pa_log("xcb_connect() failed");
+            else {
+                if (xcb_connection_has_error(xcb)) {
+                    pa_log("xcb_connection_has_error() returned true");
+                    xcb_disconnect(xcb);
+                    xcb = NULL;
+                }
+            }
+        }
+#endif
+
+        /* Figure out the cookie the same way a normal client would */
+        if (!cookie_path)
+            cookie_path = getenv(ENV_COOKIE_FILE);
+
+#ifdef HAVE_X11
+        if (!cookie_path && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_COOKIE", t, sizeof(t))) {
+                uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
+
+                if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie))
+                    pa_log("Failed to parse cookie data");
+                else {
+                    if (!(u->auth_cookie = pa_auth_cookie_create(u->core, cookie, sizeof(cookie))))
+                        goto fail;
+                }
+            }
+        }
+#endif
+
+        /* Same thing for the server name */
+        if (!server)
+            server = pa_xstrdup(getenv(ENV_DEFAULT_SERVER));
+
+#ifdef HAVE_X11
+        if (!server && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_SERVER", t, sizeof(t)))
+                server = pa_xstrdup(t);
+        }
+#endif
+
+        /* Also determine the default sink/source on the other server */
+#ifdef TUNNEL_SINK
+        if (!u->sink_name)
+            u->sink_name = pa_xstrdup(getenv(ENV_DEFAULT_SINK));
+
+#ifdef HAVE_X11
+        if (!u->sink_name && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_SINK", t, sizeof(t)))
+                u->sink_name = pa_xstrdup(t);
+        }
+#endif
+#else
+        if (!u->source_name)
+            u->source_name = pa_xstrdup(getenv(ENV_DEFAULT_SOURCE));
+
+#ifdef HAVE_X11
+        if (!u->source_name && xcb) {
+            char t[1024];
+            if (pa_x11_get_prop(xcb, 0, "PULSE_SOURCE", t, sizeof(t)))
+                u->source_name = pa_xstrdup(t);
+        }
+#endif
+#endif
+    }
+
+    if (!cookie_path && !u->auth_cookie)
+        cookie_path = PA_NATIVE_COOKIE_FILE;
+
+    if (cookie_path) {
+        if (!(u->auth_cookie = pa_auth_cookie_get(u->core, cookie_path, true, PA_NATIVE_COOKIE_LENGTH)))
+            goto fail;
+    }
+
+    if (server) {
+        if (!(server_list = pa_strlist_parse(server))) {
+            pa_log("Invalid server specified.");
+            goto fail;
+        }
+    } else {
+        char *ufn;
+
+        if (!automatic) {
+            pa_log("No server specified.");
+            goto fail;
+        }
+
+        pa_log("No server address found. Attempting default local sockets.");
+
+        /* The system wide instance via PF_LOCAL */
+        server_list = pa_strlist_prepend(server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
+
+        /* The user instance via PF_LOCAL */
+        if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
+            server_list = pa_strlist_prepend(server_list, ufn);
+            pa_xfree(ufn);
+        }
     }
 
     ss = m->core->default_sample_spec;
     }
 
     ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
         pa_log("Invalid sample format specification");
         goto fail;
     }
 
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
         pa_log("Invalid sample format specification");
         goto fail;
     }
 
-    if (!(u->client = pa_socket_client_new_string(m->core->mainloop, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
-        pa_log("Failed to connect to server '%s'", u->server_name);
-        goto fail;
-    }
+    for (;;) {
+        server_list = pa_strlist_pop(server_list, &u->server_name);
+
+        if (!u->server_name) {
+            pa_log("Failed to connect to server '%s'", server);
+            goto fail;
+        }
+
+        pa_log_debug("Trying to connect to %s...", u->server_name);
+
+        if (!(u->client = pa_socket_client_new_string(m->core->mainloop, true, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
+            pa_xfree(u->server_name);
+            u->server_name = NULL;
+            continue;
+        }
+
+        break;
+     }
 
     pa_socket_client_set_callback(u->client, on_connection, u);
 
 #ifdef TUNNEL_SINK
 
     if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
 
     pa_socket_client_set_callback(u->client, on_connection, u);
 
 #ifdef TUNNEL_SINK
 
     if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
-        dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
+        dn = pa_sprintf_malloc("tunnel-sink.%s", u->server_name);
 
     pa_sink_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
 
     pa_sink_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
-    data.namereg_fail = TRUE;
+    data.namereg_fail = false;
     pa_sink_new_data_set_name(&data, dn);
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
     pa_sink_new_data_set_name(&data, dn);
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
@@ -1748,7 +2132,13 @@ int pa__init(pa_module*m) {
     if (u->sink_name)
         pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
 
     if (u->sink_name)
         pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
 
-    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
+    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY);
     pa_sink_new_data_done(&data);
 
     if (!u->sink) {
     pa_sink_new_data_done(&data);
 
     if (!u->sink) {
@@ -1759,12 +2149,12 @@ int pa__init(pa_module*m) {
     u->sink->parent.process_msg = sink_process_msg;
     u->sink->userdata = u;
     u->sink->set_state = sink_set_state;
     u->sink->parent.process_msg = sink_process_msg;
     u->sink->userdata = u;
     u->sink->set_state = sink_set_state;
-    u->sink->set_volume = sink_set_volume;
-    u->sink->set_mute = sink_set_mute;
+    pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
+    pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
 
 
-    u->sink->refresh_volume = u->sink->refresh_muted = FALSE;
+    u->sink->refresh_volume = u->sink->refresh_muted = false;
 
 
-    pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0);
+/*     pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); */
 
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
 
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
@@ -1772,12 +2162,12 @@ int pa__init(pa_module*m) {
 #else
 
     if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
 #else
 
     if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
-        dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
+        dn = pa_sprintf_malloc("tunnel-source.%s", u->server_name);
 
     pa_source_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
 
     pa_source_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
-    data.namereg_fail = TRUE;
+    data.namereg_fail = false;
     pa_source_new_data_set_name(&data, dn);
     pa_source_new_data_set_sample_spec(&data, &ss);
     pa_source_new_data_set_channel_map(&data, &map);
     pa_source_new_data_set_name(&data, dn);
     pa_source_new_data_set_sample_spec(&data, &ss);
     pa_source_new_data_set_channel_map(&data, &map);
@@ -1786,6 +2176,12 @@ int pa__init(pa_module*m) {
     if (u->source_name)
         pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
 
     if (u->source_name)
         pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
 
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
+
     u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
     pa_source_new_data_done(&data);
 
     u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
     pa_source_new_data_done(&data);
 
@@ -1798,26 +2194,26 @@ int pa__init(pa_module*m) {
     u->source->set_state = source_set_state;
     u->source->userdata = u;
 
     u->source->set_state = source_set_state;
     u->source->userdata = u;
 
-    pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0);
+/*     pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); */
 
     pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
     pa_source_set_rtpoll(u->source, u->rtpoll);
 
     pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
     pa_source_set_rtpoll(u->source, u->rtpoll);
+
+    u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec));
 #endif
 
     pa_xfree(dn);
 
     u->time_event = NULL;
 
 #endif
 
     pa_xfree(dn);
 
     u->time_event = NULL;
 
-    u->maxlength = 0;
+    u->maxlength = (uint32_t) -1;
 #ifdef TUNNEL_SINK
 #ifdef TUNNEL_SINK
-    u->tlength = u->minreq = u->prebuf = 0;
+    u->tlength = u->minreq = u->prebuf = (uint32_t) -1;
 #else
 #else
-    u->fragsize = 0;
+    u->fragsize = (uint32_t) -1;
 #endif
 
 #endif
 
-    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
-
-    if (!(u->thread = pa_thread_new(thread_func, u))) {
+    if (!(u->thread = pa_thread_new("module-tunnel", thread_func, u))) {
         pa_log("Failed to create thread.");
         goto fail;
     }
         pa_log("Failed to create thread.");
         goto fail;
     }
@@ -1828,6 +2224,17 @@ int pa__init(pa_module*m) {
     pa_source_put(u->source);
 #endif
 
     pa_source_put(u->source);
 #endif
 
+    if (server)
+        pa_xfree(server);
+
+    if (server_list)
+        pa_strlist_free(server_list);
+
+#ifdef HAVE_X11
+    if (xcb)
+        xcb_disconnect(xcb);
+#endif
+
     pa_modargs_free(ma);
 
     return 0;
     pa_modargs_free(ma);
 
     return 0;
@@ -1835,12 +2242,23 @@ int pa__init(pa_module*m) {
 fail:
     pa__done(m);
 
 fail:
     pa__done(m);
 
+    if (server)
+        pa_xfree(server);
+
+    if (server_list)
+        pa_strlist_free(server_list);
+
+#ifdef HAVE_X11
+    if (xcb)
+        xcb_disconnect(xcb);
+#endif
+
     if (ma)
         pa_modargs_free(ma);
 
     pa_xfree(dn);
 
     if (ma)
         pa_modargs_free(ma);
 
     pa_xfree(dn);
 
-    return  -1;
+    return -1;
 }
 
 void pa__done(pa_module*m) {
 }
 
 void pa__done(pa_module*m) {
@@ -1897,6 +2315,11 @@ void pa__done(pa_module*m) {
     if (u->time_event)
         u->core->mainloop->time_free(u->time_event);
 
     if (u->time_event)
         u->core->mainloop->time_free(u->time_event);
 
+#ifndef TUNNEL_SINK
+    if (u->mcalign)
+        pa_mcalign_free(u->mcalign);
+#endif
+
 #ifdef TUNNEL_SINK
     pa_xfree(u->sink_name);
 #else
 #ifdef TUNNEL_SINK
     pa_xfree(u->sink_name);
 #else