]> code.delx.au - pulseaudio/blobdiff - src/modules/module-tunnel.c
catch up with trunk HEAD (i.e. 2118:2213)
[pulseaudio] / src / modules / module-tunnel.c
index 1682007ac67c44c6a8b16ca3d6eba8e243a79d7b..5483be39906eca1e8994a7143442d12e3c4b5419 100644 (file)
@@ -59,7 +59,8 @@
 
 #ifdef TUNNEL_SINK
 #include "module-tunnel-sink-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sinks")
+PA_MODULE_DESCRIPTION("Tunnel module for sinks");
+PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
         "server=<address> "
         "sink=<remote sink name> "
@@ -68,10 +69,10 @@ PA_MODULE_USAGE(
         "channels=<number of channels> "
         "rate=<sample rate> "
         "sink_name=<name for the local sink> "
-        "channel_map=<channel map>")
+        "channel_map=<channel map>");
 #else
 #include "module-tunnel-source-symdef.h"
-PA_MODULE_DESCRIPTION("Tunnel module for sources")
+PA_MODULE_DESCRIPTION("Tunnel module for sources");
 PA_MODULE_USAGE(
         "server=<address> "
         "source=<remote source name> "
@@ -80,11 +81,11 @@ PA_MODULE_USAGE(
         "channels=<number of channels> "
         "rate=<sample rate> "
         "source_name=<name for the local source> "
-        "channel_map=<channel map>")
+        "channel_map=<channel map>");
 #endif
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_VERSION(PACKAGE_VERSION);
 
 #define DEFAULT_TLENGTH_MSEC 100
 #define DEFAULT_MINREQ_MSEC 10
@@ -123,21 +124,27 @@ enum {
 
 #ifdef TUNNEL_SINK
 static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 #endif
+static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_overflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_suspend(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 const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 #ifdef TUNNEL_SINK
     [PA_COMMAND_REQUEST] = command_request,
-    [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
 #endif
+    [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
     [PA_COMMAND_OVERFLOW] = command_overflow,
     [PA_COMMAND_UNDERFLOW] = command_underflow,
     [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
     [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
+    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspend,
+    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspend,
+    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
+    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
 };
 
 struct userdata {
@@ -177,6 +184,10 @@ struct userdata {
 
     pa_smoother *smoother;
 
+    char *device_description;
+    char *server_fqdn;
+    char *user_name;
+
     uint32_t maxlength;
 #ifdef TUNNEL_SINK
     uint32_t tlength;
@@ -221,6 +232,28 @@ static void command_underflow(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command,
     pa_log_warn("Server signalled buffer underrun.");
 }
 
+static void command_suspend(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_debug("Server reports a stream suspension.");
+}
+
+static void command_moved(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(pd);
+    pa_assert(t);
+    pa_assert(u);
+    pa_assert(u->pdispatch == pd);
+
+    pa_log_debug("Server reports a stream move.");
+}
+
 static void stream_cork(struct userdata *u, pa_bool_t cork) {
     pa_tagstruct *t;
     pa_assert(u);
@@ -230,6 +263,9 @@ static void stream_cork(struct userdata *u, pa_bool_t cork) {
     else
         pa_smoother_resume(u->smoother, pa_rtclock_usec());
 
+    if (!u->pstream)
+        return;
+
     t = pa_tagstruct_new(NULL, 0);
 #ifdef TUNNEL_SINK
     pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
@@ -411,8 +447,7 @@ static void command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED ui
     pa_assert(u->pdispatch == pd);
 
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        pa_tagstruct_getu32(t, &bytes) < 0 ||
-        !pa_tagstruct_eof(t)) {
+        pa_tagstruct_getu32(t, &bytes) < 0) {
         pa_log("Invalid protocol reply");
         goto fail;
     }
@@ -445,7 +480,7 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_G
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get latency.");
         else
-            pa_log("Protocol error.");
+            pa_log("Protocol error 1.");
         goto fail;
     }
 
@@ -455,8 +490,7 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, PA_G
         pa_tagstruct_get_timeval(t, &local) < 0 ||
         pa_tagstruct_get_timeval(t, &remote) < 0 ||
         pa_tagstruct_gets64(t, &write_index) < 0 ||
-        pa_tagstruct_gets64(t, &read_index) < 0 ||
-        !pa_tagstruct_eof(t)) {
+        pa_tagstruct_gets64(t, &read_index) < 0) {
         pa_log("Invalid reply. (latency)");
         goto fail;
     }
@@ -568,8 +602,141 @@ static pa_usec_t source_get_latency(pa_source *s) {
 }
 #endif
 
+static void update_description(struct userdata *u) {
+    char *d;
+    char un[128], hn[128];
+    pa_tagstruct *t;
+
+    pa_assert(u);
+
+    if (!u->server_fqdn || !u->user_name || !u->device_description)
+        return;
+
+    d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
+
+#ifdef TUNNEL_SINK
+    pa_sink_set_description(u->sink, d);
+#else
+    pa_source_set_description(u->source, d);
+#endif
+
+    pa_xfree(d);
+
+    d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
+                          pa_get_user_name(un, sizeof(un)),
+                          pa_get_host_name(hn, sizeof(hn)));
+
+    t = pa_tagstruct_new(NULL, 0);
+#ifdef TUNNEL_SINK
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
+#else
+    pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
+#endif
+    pa_tagstruct_putu32(t, u->ctag++);
+    pa_tagstruct_putu32(t, u->channel);
+    pa_tagstruct_puts(t, d);
+    pa_pstream_send_tagstruct(u->pstream, t);
+
+    pa_xfree(d);
+}
+
+static void server_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    pa_sample_spec ss;
+    const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
+    uint32_t cookie;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error 6.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_gets(t, &server_name) < 0 ||
+        pa_tagstruct_gets(t, &server_version) < 0 ||
+        pa_tagstruct_gets(t, &user_name) < 0 ||
+        pa_tagstruct_gets(t, &host_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_log("Invalid reply. (get_server_info)");
+        goto fail;
+    }
+
+    pa_xfree(u->server_fqdn);
+    u->server_fqdn = pa_xstrdup(host_name);
+
+    pa_xfree(u->user_name);
+    u->user_name = pa_xstrdup(user_name);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+}
+
 #ifdef TUNNEL_SINK
 
+static void sink_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, monitor_source, flags;
+    const char *name, *description, *monitor_source_name, *driver;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_cvolume volume;
+    int mute;
+    pa_usec_t latency;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error 5.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
+        pa_tagstruct_getu32(t, &monitor_source) < 0 ||
+        pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
+        pa_tagstruct_get_usec(t, &latency) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0 ||
+        pa_tagstruct_getu32(t, &flags) < 0) {
+        pa_log("Invalid reply. (get_sink_info)");
+        goto fail;
+    }
+
+    if (!u->sink_name || strcmp(name, u->sink_name))
+        return;
+
+    pa_xfree(u->device_description);
+    u->device_description = pa_xstrdup(description);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+}
+
 static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
     uint32_t idx, owner_module, client, sink;
@@ -587,7 +754,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to get info.");
         else
-            pa_log("Protocol error.");
+            pa_log("Protocol error 2.");
         goto fail;
     }
 
@@ -603,12 +770,14 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
         pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
         pa_tagstruct_gets(t, &resample_method) < 0 ||
         pa_tagstruct_gets(t, &driver) < 0 ||
-        (u->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0) ||
-        !pa_tagstruct_eof(t)) {
+        (u->version >= 11 && pa_tagstruct_get_boolean(t, &mute) < 0)) {
         pa_log("Invalid reply. (get_info)");
         goto fail;
     }
 
+    if (idx != u->device_index)
+        return;
+
     pa_assert(u->sink);
 
     if ((u->version < 11 || !!mute == !!u->sink->muted) &&
@@ -627,17 +796,97 @@ fail:
     pa_module_unload_request(u->module);
 }
 
+#else
+
+static void source_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
+    struct userdata *u = userdata;
+    uint32_t idx, owner_module, monitor_of_sink, flags;
+    const char *name, *description, *monitor_of_sink_name, *driver;
+    pa_sample_spec ss;
+    pa_channel_map cm;
+    pa_cvolume volume;
+    int mute;
+    pa_usec_t latency;
+
+    pa_assert(pd);
+    pa_assert(u);
+
+    if (command != PA_COMMAND_REPLY) {
+        if (command == PA_COMMAND_ERROR)
+            pa_log("Failed to get info.");
+        else
+            pa_log("Protocol error 5.");
+        goto fail;
+    }
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
+        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
+        pa_tagstruct_getu32(t, &owner_module) < 0 ||
+        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &mute) < 0 ||
+        pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
+        pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
+        pa_tagstruct_get_usec(t, &latency) < 0 ||
+        pa_tagstruct_gets(t, &driver) < 0 ||
+        pa_tagstruct_getu32(t, &flags) < 0) {
+        pa_log("Invalid reply. (get_source_info)");
+        goto fail;
+    }
+
+    if (!u->source_name || strcmp(name, u->source_name))
+        return;
+
+    pa_xfree(u->device_description);
+    u->device_description = pa_xstrdup(description);
+
+    update_description(u);
+
+    return;
+
+fail:
+    pa_module_unload_request(u->module);
+}
+
+#endif
+
 static void request_info(struct userdata *u) {
     pa_tagstruct *t;
     uint32_t tag;
     pa_assert(u);
 
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
+
+#ifdef TUNNEL_SINK
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
     pa_tagstruct_putu32(t, tag = u->ctag++);
     pa_tagstruct_putu32(t, u->device_index);
     pa_pstream_send_tagstruct(u->pstream, t);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, u->sink_name);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
+#else
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
+    pa_tagstruct_putu32(t, tag = u->ctag++);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, u->source_name);
+    pa_pstream_send_tagstruct(u->pstream, t);
+    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
+#endif
 }
 
 static void command_subscribe_event(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -651,14 +900,20 @@ static void command_subscribe_event(pa_pdispatch *pd, PA_GCC_UNUSED uint32_t com
     pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
 
     if (pa_tagstruct_getu32(t, &e) < 0 ||
-        pa_tagstruct_getu32(t, &idx) < 0 ||
-        !pa_tagstruct_eof(t)) {
+        pa_tagstruct_getu32(t, &idx) < 0) {
         pa_log("Invalid protocol reply");
         pa_module_unload_request(u->module);
         return;
     }
 
-    if (e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
+    if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+#ifdef TUNNEL_SINK
+        e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
+#else
+        e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
+#endif
+        )
         return;
 
     request_info(u);
@@ -672,10 +927,16 @@ static void start_subscribe(struct userdata *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, PA_SUBSCRIPTION_MASK_SINK_INPUT);
+    pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
+#ifdef TUNNEL_SINK
+                        PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
+#else
+                        PA_SUBSCRIPTION_MASK_SOURCE
+#endif
+                        );
+
     pa_pstream_send_tagstruct(u->pstream, t);
 }
-#endif
 
 static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
@@ -692,7 +953,7 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to create stream.");
         else
-            pa_log("Protocol error.");
+            pa_log("Protocol error 3.");
         goto fail;
     }
 
@@ -722,13 +983,8 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN
 #endif
     }
 
-    if (!pa_tagstruct_eof(t))
-        goto parse_error;
-
-#ifdef TUNNEL_SINK
     start_subscribe(u);
     request_info(u);
-#endif
 
     pa_assert(!u->time_event);
     pa_gettimeofday(&ntv);
@@ -765,12 +1021,11 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_assert(u->pdispatch == pd);
 
     if (command != PA_COMMAND_REPLY ||
-        pa_tagstruct_getu32(t, &u->version) < 0 ||
-        !pa_tagstruct_eof(t)) {
+        pa_tagstruct_getu32(t, &u->version) < 0) {
         if (command == PA_COMMAND_ERROR)
             pa_log("Failed to authenticate");
         else
-            pa_log("Protocol error.");
+            pa_log("Protocol error 4.");
 
         goto fail;
     }
@@ -782,21 +1037,21 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     }
 
 #ifdef TUNNEL_SINK
-    pa_snprintf(name, sizeof(name), "Tunnel from host %s, user %s, sink %s",
-             pa_get_host_name(hn, sizeof(hn)),
-             pa_get_user_name(un, sizeof(un)),
-             u->sink->name);
+    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), "Tunnel from host %s, user %s, source %s",
-             pa_get_host_name(hn, sizeof(hn)),
-             pa_get_user_name(un, sizeof(un)),
-             u->source->name);
+    pa_snprintf(name, sizeof(name), "%s for %s@%s",
+                u->source_name,
+                pa_get_user_name(un, sizeof(un)),
+                pa_get_host_name(hn, sizeof(hn)));
 #endif
 
     reply = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
     pa_tagstruct_putu32(reply, tag = u->ctag++);
-    pa_tagstruct_puts(reply, name);
+    pa_tagstruct_puts(reply, "PulseAudio");
     pa_pstream_send_tagstruct(u->pstream, reply);
     /* We ignore the server's reply here */
 
@@ -811,7 +1066,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
     pa_tagstruct_puts(reply, u->sink_name);
     pa_tagstruct_putu32(reply, u->maxlength);
-    pa_tagstruct_put_boolean(reply, FALSE);
+    pa_tagstruct_put_boolean(reply, !PA_SINK_OPENED(pa_sink_get_state(u->sink)));
     pa_tagstruct_putu32(reply, u->tlength);
     pa_tagstruct_putu32(reply, u->prebuf);
     pa_tagstruct_putu32(reply, u->minreq);
@@ -827,10 +1082,22 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
     pa_tagstruct_puts(reply, u->source_name);
     pa_tagstruct_putu32(reply, u->maxlength);
-    pa_tagstruct_put_boolean(reply, 0);
+    pa_tagstruct_put_boolean(reply, !PA_SOURCE_OPENED(pa_source_get_state(u->source)));
     pa_tagstruct_putu32(reply, u->fragsize);
 #endif
 
+    /* New flags added in 0.9.8 */
+    if (u->version >= 12) {
+        /* TODO: set these to useful values */
+        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, FALSE); /*no_move*/
+        pa_tagstruct_put_boolean(reply, FALSE); /*variable_rate*/
+    }
+
     pa_pstream_send_tagstruct(u->pstream, reply);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
 
@@ -1001,7 +1268,7 @@ static int load_key(struct userdata *u, const char*fn) {
     u->auth_cookie_in_property = FALSE;
 
     if (!fn && pa_authkey_prop_get(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0) {
-        pa_log_debug("using already loaded auth cookie.");
+        pa_log_debug("Using already loaded auth cookie.");
         pa_authkey_prop_ref(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME);
         u->auth_cookie_in_property = 1;
         return 0;
@@ -1013,7 +1280,7 @@ static int load_key(struct userdata *u, const char*fn) {
     if (pa_authkey_load_auto(fn, u->auth_cookie, sizeof(u->auth_cookie)) < 0)
         return -1;
 
-    pa_log_debug("loading cookie from disk.");
+    pa_log_debug("Loading cookie from disk.");
 
     if (pa_authkey_prop_put(u->core, PA_NATIVE_COOKIE_PROPERTY_NAME, u->auth_cookie, sizeof(u->auth_cookie)) >= 0)
         u->auth_cookie_in_property = TRUE;
@@ -1027,6 +1294,11 @@ int pa__init(pa_module*m) {
     pa_sample_spec ss;
     pa_channel_map map;
     char *t, *dn = NULL;
+#ifdef TUNNEL_SINK
+    pa_sink_new_data data;
+#else
+    pa_source_new_data data;
+#endif
 
     pa_assert(m);
 
@@ -1035,7 +1307,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    u = pa_xnew(struct userdata, 1);
+    u = pa_xnew0(struct userdata, 1);
     m->userdata = u;
     u->module = m;
     u->core = m->core;
@@ -1087,7 +1359,18 @@ int pa__init(pa_module*m) {
     if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
         dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
 
-    if (!(u->sink = pa_sink_new(m->core, __FILE__, dn, 0, &ss, &map))) {
+    pa_sink_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    data.namereg_fail = TRUE;
+    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);
+
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
+    pa_sink_new_data_done(&data);
+
+    if (!u->sink) {
         pa_log("Failed to create sink.");
         goto fail;
     }
@@ -1101,10 +1384,9 @@ int pa__init(pa_module*m) {
     u->sink->set_volume = sink_set_volume;
     u->sink->set_mute = sink_set_mute;
 
-    pa_sink_set_module(u->sink, m);
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
-    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Tunnel to %s%s%s", u->sink_name ? u->sink_name : "", u->sink_name ? " on " : "", u->server_name));
+    pa_sink_set_description(u->sink, t = pa_sprintf_malloc("%s%s%s", u->sink_name ? u->sink_name : "", u->sink_name ? " on " : "", u->server_name));
     pa_xfree(t);
 
 #else
@@ -1112,7 +1394,18 @@ int pa__init(pa_module*m) {
     if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
         dn = pa_sprintf_malloc("tunnel.%s", u->server_name);
 
-    if (!(u->source = pa_source_new(m->core, __FILE__, dn, 0, &ss, &map))) {
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    data.namereg_fail = TRUE;
+    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);
+
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
+
+    if (!u->source) {
         pa_log("Failed to create source.");
         goto fail;
     }
@@ -1122,10 +1415,9 @@ int pa__init(pa_module*m) {
     u->source->set_state = source_set_state;
     u->source->get_latency = source_get_latency;
 
-    pa_source_set_module(u->source, m);
     pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
     pa_source_set_rtpoll(u->source, u->rtpoll);
-    pa_source_set_description(u->source, t = pa_sprintf_malloc("Tunnel to %s%s%s", u->source_name ? u->source_name : "", u->source_name ? " on " : "", u->server_name));
+    pa_source_set_description(u->source, t = pa_sprintf_malloc("%s%s%s", u->source_name ? u->source_name : "", u->source_name ? " on " : "", u->server_name));
     pa_xfree(t);
 #endif
 
@@ -1232,5 +1524,9 @@ void pa__done(pa_module*m) {
 #endif
     pa_xfree(u->server_name);
 
+    pa_xfree(u->device_description);
+    pa_xfree(u->server_fqdn);
+    pa_xfree(u->user_name);
+
     pa_xfree(u);
 }