]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/protocol-esound.c
core: Add extended stream API to support compressed formats
[pulseaudio] / src / pulsecore / protocol-esound.c
index 59a4208f9ed2783dedf2eed31c95995920015a25..c2af77c2ce1ba7421440783ba9399bd39642f2f8 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
 /***
   This file is part of PulseAudio.
 
@@ -8,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 <stdlib.h>
 #include <limits.h>
 
 #include <stdlib.h>
 #include <limits.h>
 
+#include <pulse/rtclock.h>
 #include <pulse/sample.h>
 #include <pulse/timeval.h>
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
 #include <pulse/sample.h>
 #include <pulse/timeval.h>
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
 
 #include <pulsecore/esound.h>
 #include <pulsecore/memblock.h>
 
 #include <pulsecore/esound.h>
 #include <pulsecore/memblock.h>
@@ -54,8 +54,8 @@
 #include <pulsecore/ipacl.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/ipacl.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/thread-mq.h>
-
-#include "endianmacros.h"
+#include <pulsecore/shared.h>
+#include <pulsecore/endianmacros.h>
 
 #include "protocol-esound.h"
 
 
 #include "protocol-esound.h"
 
 #define MAX_CONNECTIONS 64
 
 /* Kick a client if it doesn't authenticate within this time */
 #define MAX_CONNECTIONS 64
 
 /* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 5
+#define AUTH_TIMEOUT (5*PA_USEC_PER_SEC)
 
 #define DEFAULT_COOKIE_FILE ".esd_auth"
 
 #define PLAYBACK_BUFFER_SECONDS (.25)
 #define PLAYBACK_BUFFER_FRAGMENTS (10)
 #define RECORD_BUFFER_SECONDS (5)
 
 #define DEFAULT_COOKIE_FILE ".esd_auth"
 
 #define PLAYBACK_BUFFER_SECONDS (.25)
 #define PLAYBACK_BUFFER_FRAGMENTS (10)
 #define RECORD_BUFFER_SECONDS (5)
-#define RECORD_BUFFER_FRAGMENTS (100)
 
 #define MAX_CACHE_SAMPLE_SIZE (2048000)
 
 
 #define MAX_CACHE_SAMPLE_SIZE (2048000)
 
+#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
+#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
+
 #define SCACHE_PREFIX "esound."
 
 /* This is heavily based on esound's code */
 #define SCACHE_PREFIX "esound."
 
 /* This is heavily based on esound's code */
@@ -83,7 +85,8 @@ typedef struct connection {
 
     uint32_t index;
     pa_bool_t dead;
 
     uint32_t index;
     pa_bool_t dead;
-    pa_protocol_esound *protocol;
+    pa_esound_protocol *protocol;
+    pa_esound_options *options;
     pa_iochannel *io;
     pa_client *client;
     pa_bool_t authorized, swap_byte_order;
     pa_iochannel *io;
     pa_client *client;
     pa_bool_t authorized, swap_byte_order;
@@ -102,8 +105,9 @@ typedef struct connection {
 
     struct {
         pa_memblock *current_memblock;
 
     struct {
         pa_memblock *current_memblock;
-        size_t memblock_index, fragment_size;
+        size_t memblock_index;
         pa_atomic_t missing;
         pa_atomic_t missing;
+        pa_bool_t underrun;
     } playback;
 
     struct {
     } playback;
 
     struct {
@@ -115,21 +119,15 @@ typedef struct connection {
     pa_time_event *auth_timeout_event;
 } connection;
 
     pa_time_event *auth_timeout_event;
 } connection;
 
-PA_DECLARE_CLASS(connection);
+PA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
 #define CONNECTION(o) (connection_cast(o))
 #define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
 
 
-struct pa_protocol_esound {
-    pa_module *module;
+struct pa_esound_protocol {
+    PA_REFCNT_DECLARE;
+
     pa_core *core;
     pa_core *core;
-    int public;
-    pa_socket_server *server;
     pa_idxset *connections;
     pa_idxset *connections;
-
-    char *sink_name, *source_name;
     unsigned n_player;
     unsigned n_player;
-    uint8_t esd_key[ESD_KEY_LEN];
-    pa_ip_acl *auth_ip_acl;
 };
 
 enum {
 };
 
 enum {
@@ -150,6 +148,8 @@ typedef struct proto_handler {
 } esd_proto_handler_info_t;
 
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
 } esd_proto_handler_info_t;
 
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_kill_cb(pa_sink_input *i);
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
 static void sink_input_kill_cb(pa_sink_input *i);
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
@@ -164,10 +164,12 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
 static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length);
 static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
+static int esd_proto_standby_mode(connection *c, esd_proto_t request, const void *data, size_t length);
 
 /* the big map of protocol handler info */
 static struct proto_handler proto_map[ESD_PROTO_MAX] = {
 
 /* the big map of protocol handler info */
 static struct proto_handler proto_map[ESD_PROTO_MAX] = {
@@ -184,23 +186,23 @@ static struct proto_handler proto_map[ESD_PROTO_MAX] = {
     { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */
     { sizeof(int),                    NULL, "sample loop" },
     { sizeof(int),                    NULL, "sample stop" },
     { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */
     { sizeof(int),                    NULL, "sample loop" },
     { sizeof(int),                    NULL, "sample stop" },
-    { -1,                             NULL, "TODO: sample kill" },
+    { (size_t) -1,                    NULL, "TODO: sample kill" },
 
 
-    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },  /* NOOP! */
-    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },   /* NOOP! */         /* 13 */
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },
+    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },                       /* 13 */
 
     { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },                     /* 14 */
     { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
 
     { sizeof(int),                    esd_proto_server_info, "server info" },
     { sizeof(int),                    esd_proto_all_info, "all info" },
 
     { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },                     /* 14 */
     { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
 
     { sizeof(int),                    esd_proto_server_info, "server info" },
     { sizeof(int),                    esd_proto_all_info, "all info" },
-    { -1,                             NULL, "TODO: subscribe" },
-    { -1,                             NULL, "TODO: unsubscribe" },
+    { (size_t) -1,                    NULL, "TODO: subscribe" },
+    { (size_t) -1,                    NULL, "TODO: unsubscribe" },
 
     { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},
 
     { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},
-    { 3 * sizeof(int),                NULL, "sample pan" },
+    { 3 * sizeof(int),                esd_proto_sample_pan, "sample pan" },
 
 
-    { sizeof(int),                    NULL, "standby mode" },
+    { sizeof(int),                    esd_proto_standby_mode, "standby mode" },
     { 0,                              esd_proto_get_latency, "get latency" }
 };
 
     { 0,                              esd_proto_get_latency, "get latency" }
 };
 
@@ -210,6 +212,11 @@ static void connection_unlink(connection *c) {
     if (!c->protocol)
         return;
 
     if (!c->protocol)
         return;
 
+    if (c->options) {
+        pa_esound_options_unref(c->options);
+        c->options = NULL;
+    }
+
     if (c->sink_input) {
         pa_sink_input_unlink(c->sink_input);
         pa_sink_input_unref(c->sink_input);
     if (c->sink_input) {
         pa_sink_input_unlink(c->sink_input);
         pa_sink_input_unref(c->sink_input);
@@ -304,7 +311,7 @@ static void connection_write(connection *c, const void *data, size_t length) {
 static void format_esd2native(int format, pa_bool_t swap_bytes, pa_sample_spec *ss) {
     pa_assert(ss);
 
 static void format_esd2native(int format, pa_bool_t swap_bytes, pa_sample_spec *ss) {
     pa_assert(ss);
 
-    ss->channels = ((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1;
+    ss->channels = (uint8_t) (((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1);
     if ((format & ESD_MASK_BITS) == ESD_BITS16)
         ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
     else
     if ((format & ESD_MASK_BITS) == ESD_BITS16)
         ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
     else
@@ -320,16 +327,16 @@ static int format_native2esd(pa_sample_spec *ss) {
     return format;
 }
 
     return format;
 }
 
-#define CHECK_VALIDITY(expression, ...) do { \
-    if (!(expression)) { \
-        pa_log_warn(__FILE__ ": " __VA_ARGS__); \
-        return -1; \
-    } \
-} while(0);
+#define CHECK_VALIDITY(expression, ...) do {            \
+        if (PA_UNLIKELY(!(expression))) {               \
+            pa_log_warn(__FILE__ ": " __VA_ARGS__);     \
+            return -1;                                  \
+        }                                               \
+    } while(0);
 
 /*** esound commands ***/
 
 
 /*** esound commands ***/
 
-static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length) {
     uint32_t ekey;
     int ok;
 
     uint32_t ekey;
     int ok;
 
@@ -337,17 +344,22 @@ static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, c
     pa_assert(data);
     pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
 
     pa_assert(data);
     pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
 
+    if (!c->authorized && c->options->auth_cookie) {
+        const uint8_t*key;
+
+        if ((key = pa_auth_cookie_read(c->options->auth_cookie, ESD_KEY_LEN)))
+            if (memcmp(data, key, ESD_KEY_LEN) == 0)
+                c->authorized = TRUE;
+    }
+
     if (!c->authorized) {
     if (!c->authorized) {
-        if (memcmp(data, c->protocol->esd_key, ESD_KEY_LEN) != 0) {
-            pa_log("kicked client with invalid authorization key.");
-            return -1;
-        }
+        pa_log("Kicked client with invalid authorization key.");
+        return -1;
+    }
 
 
-        c->authorized = TRUE;
-        if (c->auth_timeout_event) {
-            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
-            c->auth_timeout_event = NULL;
-        }
+    if (c->auth_timeout_event) {
+        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
+        c->auth_timeout_event = NULL;
     }
 
     data = (const char*)data + ESD_KEY_LEN;
     }
 
     data = (const char*)data + ESD_KEY_LEN;
@@ -362,18 +374,21 @@ static int esd_proto_connect(connection *c, PA_GCC_UNUSED esd_proto_t request, c
         return -1;
     }
 
         return -1;
     }
 
+    pa_proplist_sets(c->client->proplist, "esound.byte_order", c->swap_byte_order ? "reverse" : "native");
+
     ok = 1;
     connection_write(c, &ok, sizeof(int));
     return 0;
 }
 
     ok = 1;
     connection_write(c, &ok, sizeof(int));
     return 0;
 }
 
-static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length) {
     char name[ESD_NAME_MAX], *utf8_name;
     int32_t format, rate;
     pa_sample_spec ss;
     size_t l;
     pa_sink *sink = NULL;
     pa_sink_input_new_data sdata;
     char name[ESD_NAME_MAX], *utf8_name;
     int32_t format, rate;
     pa_sample_spec ss;
     size_t l;
     pa_sink *sink = NULL;
     pa_sink_input_new_data sdata;
+    pa_memchunk silence;
 
     connection_assert_ref(c);
     pa_assert(data);
 
     connection_assert_ref(c);
     pa_assert(data);
@@ -387,18 +402,17 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
     rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
     data = (const char*) data + sizeof(int32_t);
 
     rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
     data = (const char*) data + sizeof(int32_t);
 
-    ss.rate = rate;
+    ss.rate = (uint32_t) rate;
     format_esd2native(format, c->swap_byte_order, &ss);
 
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
 
     format_esd2native(format, c->swap_byte_order, &ss);
 
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
 
-    if (c->protocol->sink_name) {
-        sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1);
-        CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name);
+    if (c->options->default_sink) {
+        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(sink, "No such sink: %s", pa_strnull(c->options->default_sink));
     }
 
     }
 
-    strncpy(name, data, sizeof(name));
-    name[sizeof(name)-1] = 0;
+    pa_strlcpy(name, data, sizeof(name));
 
     utf8_name = pa_utf8_filter(name);
     pa_client_set_name(c->client, utf8_name);
 
     utf8_name = pa_utf8_filter(name);
     pa_client_set_name(c->client, utf8_name);
@@ -410,40 +424,44 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques
 
     pa_sink_input_new_data_init(&sdata);
     sdata.driver = __FILE__;
 
     pa_sink_input_new_data_init(&sdata);
     sdata.driver = __FILE__;
-    sdata.module = c->protocol->module;
+    sdata.module = c->options->module;
     sdata.client = c->client;
     sdata.client = c->client;
-    sdata.sink = sink;
-    pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
+    pa_sink_input_new_data_set_sink(&sdata, sink, FALSE);
     pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
 
     pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
 
-    c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+    pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);
     pa_sink_input_new_data_done(&sdata);
 
     CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
 
     pa_sink_input_new_data_done(&sdata);
 
     CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
 
-    l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+    l = (size_t) ((double) pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
+    pa_sink_input_get_silence(c->sink_input, &silence);
     c->input_memblockq = pa_memblockq_new(
             0,
             l,
     c->input_memblockq = pa_memblockq_new(
             0,
             l,
-            0,
+            l,
             pa_frame_size(&ss),
             (size_t) -1,
             l/PLAYBACK_BUFFER_FRAGMENTS,
             0,
             pa_frame_size(&ss),
             (size_t) -1,
             l/PLAYBACK_BUFFER_FRAGMENTS,
             0,
-            NULL);
-    pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2);
-    c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS;
+            &silence);
+    pa_memblock_unref(silence.memblock);
+    pa_iochannel_socket_set_rcvbuf(c->io, l);
 
     c->sink_input->parent.process_msg = sink_input_process_msg;
     c->sink_input->pop = sink_input_pop_cb;
 
     c->sink_input->parent.process_msg = sink_input_process_msg;
     c->sink_input->pop = sink_input_pop_cb;
+    c->sink_input->process_rewind = sink_input_process_rewind_cb;
+    c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
     c->sink_input->kill = sink_input_kill_cb;
     c->sink_input->userdata = c;
 
     c->sink_input->kill = sink_input_kill_cb;
     c->sink_input->userdata = c;
 
+    pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
+
     c->state = ESD_STREAMING_DATA;
 
     c->protocol->n_player++;
 
     c->state = ESD_STREAMING_DATA;
 
     c->protocol->n_player++;
 
-    pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq));
+    pa_atomic_store(&c->playback.missing, (int) pa_memblockq_pop_missing(c->input_memblockq));
 
     pa_sink_input_put(c->sink_input);
 
 
     pa_sink_input_put(c->sink_input);
 
@@ -470,7 +488,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
     data = (const char*) data + sizeof(int32_t);
 
     rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
     data = (const char*) data + sizeof(int32_t);
 
-    ss.rate = rate;
+    ss.rate = (uint32_t) rate;
     format_esd2native(format, c->swap_byte_order, &ss);
 
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
     format_esd2native(format, c->swap_byte_order, &ss);
 
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
@@ -478,28 +496,21 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     if (request == ESD_PROTO_STREAM_MON) {
         pa_sink* sink;
 
     if (request == ESD_PROTO_STREAM_MON) {
         pa_sink* sink;
 
-        if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
-            pa_log("no such sink.");
-            return -1;
-        }
+        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(sink, "No such sink: %s", pa_strnull(c->options->default_sink));
 
 
-        if (!(source = sink->monitor_source)) {
-            pa_log("no such monitor source.");
-            return -1;
-        }
+        source = sink->monitor_source;
+        CHECK_VALIDITY(source, "No such source.");
     } else {
         pa_assert(request == ESD_PROTO_STREAM_REC);
 
     } else {
         pa_assert(request == ESD_PROTO_STREAM_REC);
 
-        if (c->protocol->source_name) {
-            if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, 1))) {
-                pa_log("no such source.");
-                return -1;
-            }
+        if (c->options->default_source) {
+            source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE);
+            CHECK_VALIDITY(source, "No such source: %s", pa_strnull(c->options->default_source));
         }
     }
 
         }
     }
 
-    strncpy(name, data, sizeof(name));
-    name[sizeof(name)-1] = 0;
+    pa_strlcpy(name, data, sizeof(name));
 
     utf8_name = pa_utf8_filter(name);
     pa_client_set_name(c->client, utf8_name);
 
     utf8_name = pa_utf8_filter(name);
     pa_client_set_name(c->client, utf8_name);
@@ -511,34 +522,35 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
 
     pa_source_output_new_data_init(&sdata);
     sdata.driver = __FILE__;
 
     pa_source_output_new_data_init(&sdata);
     sdata.driver = __FILE__;
-    sdata.module = c->protocol->module;
+    sdata.module = c->options->module;
     sdata.client = c->client;
     sdata.source = source;
     sdata.client = c->client;
     sdata.source = source;
-    pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
     pa_source_output_new_data_set_sample_spec(&sdata, &ss);
 
     pa_source_output_new_data_set_sample_spec(&sdata, &ss);
 
-    c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+    pa_source_output_new(&c->source_output, c->protocol->core, &sdata);
     pa_source_output_new_data_done(&sdata);
 
     pa_source_output_new_data_done(&sdata);
 
-    CHECK_VALIDITY(c->source_output, "Failed to create source_output.");
+    CHECK_VALIDITY(c->source_output, "Failed to create source output.");
 
     l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
     c->output_memblockq = pa_memblockq_new(
             0,
             l,
 
     l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
     c->output_memblockq = pa_memblockq_new(
             0,
             l,
-            0,
+            l,
             pa_frame_size(&ss),
             1,
             0,
             0,
             NULL);
             pa_frame_size(&ss),
             1,
             0,
             0,
             NULL);
-    pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2);
+    pa_iochannel_socket_set_sndbuf(c->io, l);
 
     c->source_output->push = source_output_push_cb;
     c->source_output->kill = source_output_kill_cb;
     c->source_output->get_latency = source_output_get_latency_cb;
     c->source_output->userdata = c;
 
 
     c->source_output->push = source_output_push_cb;
     c->source_output->kill = source_output_kill_cb;
     c->source_output->get_latency = source_output_get_latency_cb;
     c->source_output->userdata = c;
 
+    pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
+
     c->state = ESD_STREAMING_DATA;
 
     c->protocol->n_player++;
     c->state = ESD_STREAMING_DATA;
 
     c->protocol->n_player++;
@@ -548,37 +560,38 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     return 0;
 }
 
     return 0;
 }
 
-static int esd_proto_get_latency(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length) {
     pa_sink *sink;
     int32_t latency;
 
     pa_sink *sink;
     int32_t latency;
 
-    connection_ref(c);
+    connection_assert_ref(c);
     pa_assert(!data);
     pa_assert(length == 0);
 
     pa_assert(!data);
     pa_assert(length == 0);
 
-    if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+    if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
         latency = 0;
     else {
         latency = 0;
     else {
-        double usec = pa_sink_get_latency(sink);
+        double usec = (double) pa_sink_get_requested_latency(sink);
         latency = (int) ((usec*44100)/1000000);
     }
 
     latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
     connection_write(c, &latency, sizeof(int32_t));
         latency = (int) ((usec*44100)/1000000);
     }
 
     latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
     connection_write(c, &latency, sizeof(int32_t));
+
     return 0;
 }
 
     return 0;
 }
 
-static int esd_proto_server_info(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length) {
     int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
     int32_t response;
     pa_sink *sink;
 
     int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
     int32_t response;
     pa_sink *sink;
 
-    connection_ref(c);
+    connection_assert_ref(c);
     pa_assert(data);
     pa_assert(length == sizeof(int32_t));
 
     pa_assert(data);
     pa_assert(length == sizeof(int32_t));
 
-    if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) {
-        rate = sink->sample_spec.rate;
+    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
+        rate = (int32_t) sink->sample_spec.rate;
         format = format_native2esd(&sink->sample_spec);
     }
 
         format = format_native2esd(&sink->sample_spec);
     }
 
@@ -601,7 +614,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
     unsigned nsamples;
     char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
 
     unsigned nsamples;
     char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
 
-    connection_ref(c);
+    connection_assert_ref(c);
     pa_assert(data);
     pa_assert(length == sizeof(int32_t));
 
     pa_assert(data);
     pa_assert(length == sizeof(int32_t));
 
@@ -610,14 +623,14 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
 
     k = sizeof(int32_t)*5+ESD_NAME_MAX;
     s = sizeof(int32_t)*6+ESD_NAME_MAX;
 
     k = sizeof(int32_t)*5+ESD_NAME_MAX;
     s = sizeof(int32_t)*6+ESD_NAME_MAX;
-    nsamples = c->protocol->core->scache ? pa_idxset_size(c->protocol->core->scache) : 0;
+    nsamples = pa_idxset_size(c->protocol->core->scache);
     t = s*(nsamples+1) + k*(c->protocol->n_player+1);
 
     connection_write_prepare(c, t);
 
     memset(terminator, 0, sizeof(terminator));
 
     t = s*(nsamples+1) + k*(c->protocol->n_player+1);
 
     connection_write_prepare(c, t);
 
     memset(terminator, 0, sizeof(terminator));
 
-    for (conn = pa_idxset_first(c->protocol->connections, &idx); conn; conn = pa_idxset_next(c->protocol->connections, &idx)) {
+    PA_IDXSET_FOREACH(conn, c->protocol->connections, idx) {
         int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
         char name[ESD_NAME_MAX];
 
         int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
         char name[ESD_NAME_MAX];
 
@@ -627,10 +640,11 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
         pa_assert(t >= k*2+s);
 
         if (conn->sink_input) {
         pa_assert(t >= k*2+s);
 
         if (conn->sink_input) {
-            pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
-            rate = conn->sink_input->sample_spec.rate;
-            lvolume = (volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
-            rvolume = (volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM;
+            pa_cvolume volume;
+            pa_sink_input_get_volume(conn->sink_input, &volume, TRUE);
+            rate = (int32_t) conn->sink_input->sample_spec.rate;
+            lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            rvolume = (int32_t) ((volume.values[volume.channels == 2 ? 1 : 0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
             format = format_native2esd(&conn->sink_input->sample_spec);
         }
 
             format = format_native2esd(&conn->sink_input->sample_spec);
         }
 
@@ -674,12 +688,30 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
         pa_scache_entry *ce;
 
         idx = PA_IDXSET_INVALID;
         pa_scache_entry *ce;
 
         idx = PA_IDXSET_INVALID;
-        for (ce = pa_idxset_first(c->protocol->core->scache, &idx); ce; ce = pa_idxset_next(c->protocol->core->scache, &idx)) {
+
+        PA_IDXSET_FOREACH(ce, c->protocol->core->scache, idx) {
             int32_t id, rate, lvolume, rvolume, format, len;
             char name[ESD_NAME_MAX];
             int32_t id, rate, lvolume, rvolume, format, len;
             char name[ESD_NAME_MAX];
+            pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } };
+            pa_cvolume volume;
+            pa_sample_spec ss;
 
             pa_assert(t >= s*2);
 
 
             pa_assert(t >= s*2);
 
+            if (ce->volume_is_set) {
+                volume = ce->volume;
+                pa_cvolume_remap(&volume, &ce->channel_map, &stereo);
+            } else
+                pa_cvolume_reset(&volume, 2);
+
+            if (ce->memchunk.memblock)
+                ss = ce->sample_spec;
+            else {
+                ss.format = PA_SAMPLE_S16NE;
+                ss.rate = 44100;
+                ss.channels = 2;
+            }
+
             /* id */
             id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
             connection_write(c, &id, sizeof(int32_t));
             /* id */
             id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
             connection_write(c, &id, sizeof(int32_t));
@@ -693,19 +725,19 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
             connection_write(c, name, ESD_NAME_MAX);
 
             /* rate */
             connection_write(c, name, ESD_NAME_MAX);
 
             /* rate */
-            rate = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, ce->sample_spec.rate);
+            rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ss.rate);
             connection_write(c, &rate, sizeof(int32_t));
 
             /* left */
             connection_write(c, &rate, sizeof(int32_t));
 
             /* left */
-            lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
             connection_write(c, &lvolume, sizeof(int32_t));
 
             /*right*/
             connection_write(c, &lvolume, sizeof(int32_t));
 
             /*right*/
-            rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, (ce->volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
+            rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
             connection_write(c, &rvolume, sizeof(int32_t));
 
             /*format*/
             connection_write(c, &rvolume, sizeof(int32_t));
 
             /*format*/
-            format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ce->sample_spec));
+            format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ss));
             connection_write(c, &format, sizeof(int32_t));
 
             /*length*/
             connection_write(c, &format, sizeof(int32_t));
 
             /*length*/
@@ -723,7 +755,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
     return 0;
 }
 
     return 0;
 }
 
-static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
     int32_t ok;
     uint32_t idx, lvolume, rvolume;
     connection *conn;
     int32_t ok;
     uint32_t idx, lvolume, rvolume;
     connection *conn;
@@ -742,14 +774,14 @@ static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request
 
     memcpy(&rvolume, data, sizeof(uint32_t));
     rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
 
     memcpy(&rvolume, data, sizeof(uint32_t));
     rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
-    data = (const char*)data + sizeof(uint32_t);
 
     if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
         pa_cvolume volume;
         volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
         volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
 
     if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
         pa_cvolume volume;
         volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
         volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
-        volume.channels = 2;
-        pa_sink_input_set_volume(conn->sink_input, &volume);
+        volume.channels = conn->sink_input->sample_spec.channels;
+
+        pa_sink_input_set_volume(conn->sink_input, &volume, TRUE, TRUE);
         ok = 1;
     } else
         ok = 0;
         ok = 1;
     } else
         ok = 0;
@@ -759,7 +791,46 @@ static int esd_proto_stream_pan(connection *c, PA_GCC_UNUSED esd_proto_t request
     return 0;
 }
 
     return 0;
 }
 
-static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok = 0;
+    uint32_t idx, lvolume, rvolume;
+    pa_cvolume volume;
+    pa_scache_entry *ce;
+
+    connection_assert_ref(c);
+    pa_assert(data);
+    pa_assert(length == sizeof(int32_t)*3);
+
+    memcpy(&idx, data, sizeof(uint32_t));
+    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&lvolume, data, sizeof(uint32_t));
+    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
+    data = (const char*)data + sizeof(uint32_t);
+
+    memcpy(&rvolume, data, sizeof(uint32_t));
+    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
+
+    volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+    volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
+    volume.channels = 2;
+
+    if ((ce = pa_idxset_get_by_index(c->protocol->core->scache, idx))) {
+        pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } };
+
+        pa_cvolume_remap(&volume, &stereo, &ce->channel_map);
+        ce->volume = volume;
+        ce->volume_is_set = TRUE;
+        ok = 1;
+    }
+
+    connection_write(c, &ok, sizeof(int32_t));
+
+    return 0;
+}
+
+static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length) {
     pa_sample_spec ss;
     int32_t format, rate, sc_length;
     uint32_t idx;
     pa_sample_spec ss;
     int32_t format, rate, sc_length;
     uint32_t idx;
@@ -777,7 +848,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
     rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
     data = (const char*)data + sizeof(int32_t);
 
     rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
     data = (const char*)data + sizeof(int32_t);
 
-    ss.rate = rate;
+    ss.rate = (uint32_t) rate;
     format_esd2native(format, c->swap_byte_order, &ss);
 
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
     format_esd2native(format, c->swap_byte_order, &ss);
 
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
@@ -789,15 +860,14 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
     CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
 
     strcpy(name, SCACHE_PREFIX);
     CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
 
     strcpy(name, SCACHE_PREFIX);
-    strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
-    name[sizeof(name)-1] = 0;
+    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
 
     CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
 
     pa_assert(!c->scache.memchunk.memblock);
 
     CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
 
     pa_assert(!c->scache.memchunk.memblock);
-    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, sc_length);
+    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) sc_length);
     c->scache.memchunk.index = 0;
     c->scache.memchunk.index = 0;
-    c->scache.memchunk.length = sc_length;
+    c->scache.memchunk.length = (size_t) sc_length;
     c->scache.sample_spec = ss;
     pa_assert(!c->scache.name);
     c->scache.name = pa_xstrdup(name);
     c->scache.sample_spec = ss;
     pa_assert(!c->scache.name);
     c->scache.name = pa_xstrdup(name);
@@ -812,7 +882,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque
     return 0;
 }
 
     return 0;
 }
 
-static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t request, const void *data, size_t length) {
+static int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length) {
     int32_t ok;
     uint32_t idx;
     char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
     int32_t ok;
     uint32_t idx;
     char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
@@ -822,14 +892,13 @@ static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t requ
     pa_assert(length == ESD_NAME_MAX);
 
     strcpy(name, SCACHE_PREFIX);
     pa_assert(length == ESD_NAME_MAX);
 
     strcpy(name, SCACHE_PREFIX);
-    strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
-    name[sizeof(name)-1] = 0;
+    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
 
     CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
 
     ok = -1;
     if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
 
     CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
 
     ok = -1;
     if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
-        ok = idx + 1;
+        ok = (int32_t) idx + 1;
 
     connection_write(c, &ok, sizeof(int32_t));
 
 
     connection_write(c, &ok, sizeof(int32_t));
 
@@ -854,14 +923,14 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
         if (request == ESD_PROTO_SAMPLE_PLAY) {
             pa_sink *sink;
 
         if (request == ESD_PROTO_SAMPLE_PLAY) {
             pa_sink *sink;
 
-            if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1)))
+            if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
                 if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
                 if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
-                    ok = idx + 1;
+                    ok = (int32_t) idx + 1;
         } else {
             pa_assert(request == ESD_PROTO_SAMPLE_FREE);
 
             if (pa_scache_remove_item(c->protocol->core, name) >= 0)
         } else {
             pa_assert(request == ESD_PROTO_SAMPLE_FREE);
 
             if (pa_scache_remove_item(c->protocol->core, name) >= 0)
-                ok = idx + 1;
+                ok = (int32_t) idx + 1;
         }
     }
 
         }
     }
 
@@ -870,20 +939,48 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
     return 0;
 }
 
     return 0;
 }
 
-static int esd_proto_standby_or_resume(connection *c, PA_GCC_UNUSED esd_proto_t request, PA_GCC_UNUSED const void *data, PA_GCC_UNUSED size_t length) {
-    int32_t ok;
+static int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t ok = 1;
 
     connection_assert_ref(c);
 
     connection_write_prepare(c, sizeof(int32_t) * 2);
 
     connection_assert_ref(c);
 
     connection_write_prepare(c, sizeof(int32_t) * 2);
-
-    ok = 1;
     connection_write(c, &ok, sizeof(int32_t));
     connection_write(c, &ok, sizeof(int32_t));
+
+    if (request == ESD_PROTO_STANDBY)
+        ok = pa_sink_suspend_all(c->protocol->core, TRUE, PA_SUSPEND_USER) >= 0;
+    else {
+        pa_assert(request == ESD_PROTO_RESUME);
+        ok = pa_sink_suspend_all(c->protocol->core, FALSE, PA_SUSPEND_USER) >= 0;
+    }
+
     connection_write(c, &ok, sizeof(int32_t));
 
     return 0;
 }
 
     connection_write(c, &ok, sizeof(int32_t));
 
     return 0;
 }
 
+static int esd_proto_standby_mode(connection *c, esd_proto_t request, const void *data, size_t length) {
+    int32_t mode;
+    pa_sink *sink, *source;
+
+    connection_assert_ref(c);
+
+    mode = ESM_RUNNING;
+
+    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
+        if (pa_sink_get_state(sink) == PA_SINK_SUSPENDED)
+            mode = ESM_ON_STANDBY;
+
+    if ((source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE)))
+        if (pa_source_get_state(source) == PA_SOURCE_SUSPENDED)
+            mode = ESM_ON_STANDBY;
+
+    mode = PA_MAYBE_INT32_SWAP(c->swap_byte_order, mode);
+
+    connection_write(c, &mode, sizeof(mode));
+    return 0;
+}
+
 /*** client callbacks ***/
 
 static void client_kill_cb(pa_client *c) {
 /*** client callbacks ***/
 
 static void client_kill_cb(pa_client *c) {
@@ -903,17 +1000,25 @@ static int do_read(connection *c) {
         ssize_t r;
         pa_assert(c->read_data_length < sizeof(c->request));
 
         ssize_t r;
         pa_assert(c->read_data_length < sizeof(c->request));
 
-        if ((r = pa_iochannel_read(c->io, ((uint8_t*) &c->request) + c->read_data_length, sizeof(c->request) - c->read_data_length)) <= 0) {
+        if ((r = pa_iochannel_read(c->io,
+                                   ((uint8_t*) &c->request) + c->read_data_length,
+                                   sizeof(c->request) - c->read_data_length)) <= 0) {
+
+            if (r < 0 && (errno == EINTR || errno == EAGAIN))
+                return 0;
+
             pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
             pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
             return -1;
         }
 
-        if ((c->read_data_length+= r) >= sizeof(c->request)) {
+        c->read_data_length += (size_t) r;
+
+        if (c->read_data_length >= sizeof(c->request)) {
             struct proto_handler *handler;
 
             c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
 
             struct proto_handler *handler;
 
             c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
 
-            if (c->request < ESD_PROTO_CONNECT || c->request > ESD_PROTO_MAX) {
+            if (c->request < ESD_PROTO_CONNECT || c->request >= ESD_PROTO_MAX) {
                 pa_log("recieved invalid request.");
                 return -1;
             }
                 pa_log("recieved invalid request.");
                 return -1;
             }
@@ -951,7 +1056,10 @@ static int do_read(connection *c) {
 
         pa_assert(c->read_data && c->read_data_length < handler->data_length);
 
 
         pa_assert(c->read_data && c->read_data_length < handler->data_length);
 
-        if ((r = pa_iochannel_read(c->io, (uint8_t*) c->read_data + c->read_data_length, handler->data_length - c->read_data_length)) <= 0) {
+        if ((r = pa_iochannel_read(c->io,
+                                   (uint8_t*) c->read_data + c->read_data_length,
+                                   handler->data_length - c->read_data_length)) <= 0) {
+
             if (r < 0 && (errno == EINTR || errno == EAGAIN))
                 return 0;
 
             if (r < 0 && (errno == EINTR || errno == EAGAIN))
                 return 0;
 
@@ -959,7 +1067,8 @@ static int do_read(connection *c) {
             return -1;
         }
 
             return -1;
         }
 
-        if ((c->read_data_length += r) >= handler->data_length) {
+        c->read_data_length += (size_t) r;
+        if (c->read_data_length >= handler->data_length) {
             size_t l = c->read_data_length;
             pa_assert(handler->proc);
 
             size_t l = c->read_data_length;
             pa_assert(handler->proc);
 
@@ -989,7 +1098,7 @@ static int do_read(connection *c) {
             return -1;
         }
 
             return -1;
         }
 
-        c->scache.memchunk.index += r;
+        c->scache.memchunk.index += (size_t) r;
         pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
 
         if (c->scache.memchunk.index == c->scache.memchunk.length) {
         pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
 
         if (c->scache.memchunk.index == c->scache.memchunk.length) {
@@ -999,8 +1108,7 @@ static int do_read(connection *c) {
             pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
 
             pa_memblock_unref(c->scache.memchunk.memblock);
             pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
 
             pa_memblock_unref(c->scache.memchunk.memblock);
-            c->scache.memchunk.memblock = NULL;
-            c->scache.memchunk.index = c->scache.memchunk.length = 0;
+            pa_memchunk_reset(&c->scache.memchunk);
 
             pa_xfree(c->scache.name);
             c->scache.name = NULL;
 
             pa_xfree(c->scache.name);
             c->scache.name = NULL;
@@ -1016,29 +1124,35 @@ static int do_read(connection *c) {
         ssize_t r;
         size_t l;
         void *p;
         ssize_t r;
         size_t l;
         void *p;
+        size_t space = 0;
 
         pa_assert(c->input_memblockq);
 
 /*         pa_log("STREAMING_DATA"); */
 
 
         pa_assert(c->input_memblockq);
 
 /*         pa_log("STREAMING_DATA"); */
 
-        if (!(l = pa_atomic_load(&c->playback.missing)))
+        if ((l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0)
             return 0;
 
             return 0;
 
-        if (l > c->playback.fragment_size)
-            l = c->playback.fragment_size;
+        if (c->playback.current_memblock) {
 
 
-        if (c->playback.current_memblock)
-            if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) {
+            space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
+
+            if (space <= 0) {
                 pa_memblock_unref(c->playback.current_memblock);
                 c->playback.current_memblock = NULL;
                 pa_memblock_unref(c->playback.current_memblock);
                 c->playback.current_memblock = NULL;
-                c->playback.memblock_index = 0;
             }
             }
+        }
 
         if (!c->playback.current_memblock) {
 
         if (!c->playback.current_memblock) {
-            pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2));
+            pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
             c->playback.memblock_index = 0;
             c->playback.memblock_index = 0;
+
+            space = pa_memblock_get_length(c->playback.current_memblock);
         }
 
         }
 
+        if (l > space)
+            l = space;
+
         p = pa_memblock_acquire(c->playback.current_memblock);
         r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
         pa_memblock_release(c->playback.current_memblock);
         p = pa_memblock_acquire(c->playback.current_memblock);
         r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
         pa_memblock_release(c->playback.current_memblock);
@@ -1054,12 +1168,12 @@ static int do_read(connection *c) {
 
         chunk.memblock = c->playback.current_memblock;
         chunk.index = c->playback.memblock_index;
 
         chunk.memblock = c->playback.current_memblock;
         chunk.index = c->playback.memblock_index;
-        chunk.length = r;
+        chunk.length = (size_t) r;
 
 
-        c->playback.memblock_index += r;
+        c->playback.memblock_index += (size_t) r;
 
 
+        pa_atomic_sub(&c->playback.missing, (int) r);
         pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
         pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
-        pa_atomic_sub(&c->playback.missing, r);
     }
 
     return 0;
     }
 
     return 0;
@@ -1083,7 +1197,8 @@ static int do_write(connection *c) {
             return -1;
         }
 
             return -1;
         }
 
-        if ((c->write_data_index +=r) >= c->write_data_length)
+        c->write_data_index += (size_t) r;
+        if (c->write_data_index >= c->write_data_length)
             c->write_data_length = c->write_data_index = 0;
 
     } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
             c->write_data_length = c->write_data_index = 0;
 
     } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
@@ -1112,7 +1227,7 @@ static int do_write(connection *c) {
             return -1;
         }
 
             return -1;
         }
 
-        pa_memblockq_drop(c->output_memblockq, r);
+        pa_memblockq_drop(c->output_memblockq, (size_t) r);
     }
 
     return 0;
     }
 
     return 0;
@@ -1126,12 +1241,11 @@ static void do_work(connection *c) {
     if (c->dead)
         return;
 
     if (c->dead)
         return;
 
-    if (pa_iochannel_is_readable(c->io)) {
+    if (pa_iochannel_is_readable(c->io))
         if (do_read(c) < 0)
             goto fail;
         if (do_read(c) < 0)
             goto fail;
-    }
 
 
-    if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io))
+    if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
         /* In case we are in capture mode we will never call read()
          * on the socket, hence we need to detect the hangup manually
          * here, instead of simply waiting for read() to return 0. */
         /* In case we are in capture mode we will never call read()
          * on the socket, hence we need to detect the hangup manually
          * here, instead of simply waiting for read() to return 0. */
@@ -1178,6 +1292,9 @@ static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int6
     connection *c = CONNECTION(o);
     connection_assert_ref(c);
 
     connection *c = CONNECTION(o);
     connection_assert_ref(c);
 
+    if (!c->protocol)
+        return -1;
+
     switch (code) {
         case CONNECTION_MESSAGE_REQUEST_DATA:
             do_work(c);
     switch (code) {
         case CONNECTION_MESSAGE_REQUEST_DATA:
             do_work(c);
@@ -1216,15 +1333,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
             /* New data from the main loop */
             pa_memblockq_push_align(c->input_memblockq, chunk);
 
             /* New data from the main loop */
             pa_memblockq_push_align(c->input_memblockq, chunk);
 
+            if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
+                pa_log_debug("Requesting rewind due to end of underrun.");
+                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE);
+            }
+
 /*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
 
             return 0;
         }
 
 /*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
 
             return 0;
         }
 
-        case SINK_INPUT_MESSAGE_DISABLE_PREBUF: {
+        case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
             pa_memblockq_prebuf_disable(c->input_memblockq);
             return 0;
             pa_memblockq_prebuf_disable(c->input_memblockq);
             return 0;
-        }
 
         case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
             pa_usec_t *r = userdata;
 
         case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
             pa_usec_t *r = userdata;
@@ -1243,32 +1364,61 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
 /* Called from thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
     connection*c;
 /* Called from thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
     connection*c;
-    int r;
 
 
-    pa_assert(i);
+    pa_sink_input_assert_ref(i);
     c = CONNECTION(i->userdata);
     connection_assert_ref(c);
     pa_assert(chunk);
 
     c = CONNECTION(i->userdata);
     connection_assert_ref(c);
     pa_assert(chunk);
 
-    if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0) {
+    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
 
 
+        c->playback.underrun = TRUE;
 
         if (c->dead && pa_sink_input_safe_to_remove(i))
             pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
 
         if (c->dead && pa_sink_input_safe_to_remove(i))
             pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
+
+        return -1;
     } else {
     } else {
-        size_t old, new;
+        size_t m;
+
+        c->playback.underrun = FALSE;
 
 
-        old = pa_memblockq_missing(c->input_memblockq);
+        chunk->length = PA_MIN(length, chunk->length);
         pa_memblockq_drop(c->input_memblockq, chunk->length);
         pa_memblockq_drop(c->input_memblockq, chunk->length);
-        new = pa_memblockq_missing(c->input_memblockq);
+        m = pa_memblockq_pop_missing(c->input_memblockq);
 
 
-        if (new > old) {
-            if (pa_atomic_add(&c->playback.missing, new - old) <= 0)
+        if (m > 0)
+            if (pa_atomic_add(&c->playback.missing, (int) m) <= 0)
                 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
                 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
-        }
+
+        return 0;
     }
     }
+}
+
+/* Called from thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    /* If we are in an underrun, then we don't rewind */
+    if (i->thread_info.underrun_for > 0)
+        return;
 
 
-    return r;
+    pa_memblockq_rewind(c->input_memblockq, nbytes);
+}
+
+/* Called from thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    connection *c;
+
+    pa_sink_input_assert_ref(i);
+    c = CONNECTION(i->userdata);
+    connection_assert_ref(c);
+
+    pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
 }
 
 static void sink_input_kill_cb(pa_sink_input *i) {
 }
 
 static void sink_input_kill_cb(pa_sink_input *i) {
@@ -1283,7 +1433,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
     connection *c;
 
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
     connection *c;
 
-    pa_assert(o);
+    pa_source_output_assert_ref(o);
     c = CONNECTION(o->userdata);
     pa_assert(c);
     pa_assert(chunk);
     c = CONNECTION(o->userdata);
     pa_assert(c);
     pa_assert(chunk);
@@ -1300,20 +1450,19 @@ static void source_output_kill_cb(pa_source_output *o) {
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
     connection*c;
 
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
     connection*c;
 
-    pa_assert(o);
+    pa_source_output_assert_ref(o);
     c = CONNECTION(o->userdata);
     pa_assert(c);
 
     return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
 }
 
     c = CONNECTION(o->userdata);
     pa_assert(c);
 
     return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
 }
 
-/*** socket server callback ***/
+/*** entry points ***/
 
 
-static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
+static void auth_timeout(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
     connection *c = CONNECTION(userdata);
 
     pa_assert(m);
     connection *c = CONNECTION(userdata);
 
     pa_assert(m);
-    pa_assert(tv);
     connection_assert_ref(c);
     pa_assert(c->auth_timeout_event == e);
 
     connection_assert_ref(c);
     pa_assert(c->auth_timeout_event == e);
 
@@ -1321,14 +1470,15 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev
         connection_unlink(c);
 }
 
         connection_unlink(c);
 }
 
-static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) {
+void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) {
     connection *c;
     connection *c;
-    pa_protocol_esound *p = userdata;
-    char cname[256], pname[128];
+    char pname[128];
+    pa_client_new_data data;
+    pa_client *client;
 
 
-    pa_assert(s);
-    pa_assert(io);
     pa_assert(p);
     pa_assert(p);
+    pa_assert(io);
+    pa_assert(o);
 
     if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
         pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
 
     if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
         pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
@@ -1336,6 +1486,18 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
         return;
     }
 
         return;
     }
 
+    pa_client_new_data_init(&data);
+    data.module = o->module;
+    data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname);
+    pa_proplist_sets(data.proplist, "esound-protocol.peer", pname);
+    client = pa_client_new(p->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return;
+
     c = pa_msgobject_new(connection);
     c->parent.parent.free = connection_free;
     c->parent.process_msg = connection_process_msg;
     c = pa_msgobject_new(connection);
     c->parent.parent.free = connection_free;
     c->parent.process_msg = connection_process_msg;
@@ -1343,14 +1505,12 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
     c->io = io;
     pa_iochannel_set_callback(c->io, io_callback, c);
 
     c->io = io;
     pa_iochannel_set_callback(c->io, io_callback, c);
 
-    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
-    pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
-    c->client = pa_client_new(p->core, __FILE__, cname);
-    c->client->module = p->module;
+    c->client = client;
     c->client->kill = client_kill_cb;
     c->client->userdata = c;
 
     c->client->kill = client_kill_cb;
     c->client->userdata = c;
 
-    c->authorized = !!p->public;
+    c->options = pa_esound_options_ref(o);
+    c->authorized = FALSE;
     c->swap_byte_order = FALSE;
     c->dead = FALSE;
 
     c->swap_byte_order = FALSE;
     c->dead = FALSE;
 
@@ -1371,26 +1531,30 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
 
     c->playback.current_memblock = NULL;
     c->playback.memblock_index = 0;
 
     c->playback.current_memblock = NULL;
     c->playback.memblock_index = 0;
-    c->playback.fragment_size = 0;
+    c->playback.underrun = TRUE;
     pa_atomic_store(&c->playback.missing, 0);
 
     pa_atomic_store(&c->playback.missing, 0);
 
-    c->scache.memchunk.length = c->scache.memchunk.index = 0;
-    c->scache.memchunk.memblock = NULL;
+    pa_memchunk_reset(&c->scache.memchunk);
     c->scache.name = NULL;
 
     c->original_name = NULL;
 
     c->scache.name = NULL;
 
     c->original_name = NULL;
 
-    if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+    if (o->auth_anonymous) {
+        pa_log_info("Client authenticated anonymously.");
+        c->authorized = TRUE;
+    }
+
+    if (!c->authorized &&
+        o->auth_ip_acl &&
+        pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
+
         pa_log_info("Client authenticated by IP ACL.");
         c->authorized = TRUE;
     }
 
         pa_log_info("Client authenticated by IP ACL.");
         c->authorized = TRUE;
     }
 
-    if (!c->authorized) {
-        struct timeval tv;
-        pa_gettimeofday(&tv);
-        tv.tv_sec += AUTH_TIMEOUT;
-        c->auth_timeout_event = p->core->mainloop->time_new(p->core->mainloop, &tv, auth_timeout, c);
-    } else
+    if (!c->authorized)
+        c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
+    else
         c->auth_timeout_event = NULL;
 
     c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
         c->auth_timeout_event = NULL;
 
     c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
@@ -1399,70 +1563,163 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata)
     pa_idxset_put(p->connections, c, &c->index);
 }
 
     pa_idxset_put(p->connections, c, &c->index);
 }
 
-/*** entry points ***/
-
-pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *server, pa_module *m, pa_modargs *ma) {
-    pa_protocol_esound *p = NULL;
-    pa_bool_t public = FALSE;
-    const char *acl;
+void pa_esound_protocol_disconnect(pa_esound_protocol *p, pa_module *m) {
+    connection *c;
+    void *state = NULL;
 
 
-    pa_assert(core);
-    pa_assert(server);
+    pa_assert(p);
     pa_assert(m);
     pa_assert(m);
-    pa_assert(ma);
 
 
-    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &public) < 0) {
-        pa_log("auth-anonymous= expects a boolean argument.");
-        goto fail;
-    }
-
-    p = pa_xnew(pa_protocol_esound, 1);
+    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
+        if (c->options->module == m)
+            connection_unlink(c);
+}
 
 
-    if (pa_authkey_load_auto(pa_modargs_get_value(ma, "cookie", DEFAULT_COOKIE_FILE), p->esd_key, sizeof(p->esd_key)) < 0)
-        goto fail;
+static pa_esound_protocol* esound_protocol_new(pa_core *c) {
+    pa_esound_protocol *p;
 
 
-    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
-
-        if (!(p->auth_ip_acl = pa_ip_acl_new(acl))) {
-            pa_log("Failed to parse IP ACL '%s'", acl);
-            goto fail;
-        }
-    } else
-        p->auth_ip_acl = NULL;
+    pa_assert(c);
 
 
-    p->core = core;
-    p->module = m;
-    p->public = public;
-    p->server = server;
-    pa_socket_server_set_callback(p->server, on_connection, p);
+    p = pa_xnew(pa_esound_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
     p->connections = pa_idxset_new(NULL, NULL);
     p->connections = pa_idxset_new(NULL, NULL);
-
-    p->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    p->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
     p->n_player = 0;
 
     p->n_player = 0;
 
+    pa_assert_se(pa_shared_set(c, "esound-protocol", p) >= 0);
+
     return p;
     return p;
+}
 
 
-fail:
-    pa_xfree(p);
-    return NULL;
+pa_esound_protocol* pa_esound_protocol_get(pa_core *c) {
+    pa_esound_protocol *p;
+
+    if ((p = pa_shared_get(c, "esound-protocol")))
+        return pa_esound_protocol_ref(p);
+
+    return esound_protocol_new(c);
+}
+
+pa_esound_protocol* pa_esound_protocol_ref(pa_esound_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
 }
 
 }
 
-void pa_protocol_esound_free(pa_protocol_esound *p) {
+void pa_esound_protocol_unref(pa_esound_protocol *p) {
     connection *c;
     pa_assert(p);
     connection *c;
     pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
 
     while ((c = pa_idxset_first(p->connections, NULL)))
         connection_unlink(c);
 
     while ((c = pa_idxset_first(p->connections, NULL)))
         connection_unlink(c);
+
     pa_idxset_free(p->connections, NULL, NULL);
 
     pa_idxset_free(p->connections, NULL, NULL);
 
-    pa_socket_server_unref(p->server);
+    pa_assert_se(pa_shared_remove(p->core, "esound-protocol") >= 0);
 
 
-    if (p->auth_ip_acl)
-        pa_ip_acl_free(p->auth_ip_acl);
+    pa_xfree(p);
+}
 
 
-    pa_xfree(p->sink_name);
-    pa_xfree(p->source_name);
+pa_esound_options* pa_esound_options_new(void) {
+    pa_esound_options *o;
 
 
-    pa_xfree(p);
+    o = pa_xnew0(pa_esound_options, 1);
+    PA_REFCNT_INIT(o);
+
+    return o;
+}
+
+pa_esound_options* pa_esound_options_ref(pa_esound_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    PA_REFCNT_INC(o);
+
+    return o;
+}
+
+void pa_esound_options_unref(pa_esound_options *o) {
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (PA_REFCNT_DEC(o) > 0)
+        return;
+
+    if (o->auth_ip_acl)
+        pa_ip_acl_free(o->auth_ip_acl);
+
+    if (o->auth_cookie)
+        pa_auth_cookie_unref(o->auth_cookie);
+
+    pa_xfree(o->default_sink);
+    pa_xfree(o->default_source);
+
+    pa_xfree(o);
+}
+
+int pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma) {
+    pa_bool_t enabled;
+    const char *acl;
+
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+    pa_assert(ma);
+
+    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
+        pa_log("auth-anonymous= expects a boolean argument.");
+        return -1;
+    }
+
+    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
+        pa_ip_acl *ipa;
+
+        if (!(ipa = pa_ip_acl_new(acl))) {
+            pa_log("Failed to parse IP ACL '%s'", acl);
+            return -1;
+        }
+
+        if (o->auth_ip_acl)
+            pa_ip_acl_free(o->auth_ip_acl);
+
+        o->auth_ip_acl = ipa;
+    }
+
+    enabled = TRUE;
+    if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
+        pa_log("auth-cookie-enabled= expects a boolean argument.");
+        return -1;
+    }
+
+    if (o->auth_cookie)
+        pa_auth_cookie_unref(o->auth_cookie);
+
+    if (enabled) {
+        const char *cn;
+
+        /* The new name for this is 'auth-cookie', for compat reasons
+         * we check the old name too */
+        if (!(cn = pa_modargs_get_value(ma, "auth-cookie", NULL)))
+            if (!(cn = pa_modargs_get_value(ma, "cookie", NULL)))
+                cn = DEFAULT_COOKIE_FILE;
+
+        if (!(o->auth_cookie = pa_auth_cookie_get(c, cn, ESD_KEY_LEN)))
+            return -1;
+
+    } else
+        o->auth_cookie = NULL;
+
+    pa_xfree(o->default_sink);
+    o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
+
+    pa_xfree(o->default_source);
+    o->default_source = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
+
+    return 0;
 }
 }