]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/protocol-native.c
sink: volume handling rework, new flat volume logic
[pulseaudio] / src / pulsecore / protocol-native.c
index 5412267991fd6b69afecb16249294f24e19d0e1e..b1285e1551f6458835f2611b978d88279bf20511 100644 (file)
@@ -6,7 +6,7 @@
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/version.h>
 #include <pulse/utf8.h>
 #include <pulse/timeval.h>
 #include <pulse/version.h>
 #include <pulse/utf8.h>
@@ -61,7 +62,7 @@
 #include "protocol-native.h"
 
 /* Kick a client if it doesn't authenticate within this time */
 #include "protocol-native.h"
 
 /* Kick a client if it doesn't authenticate within this time */
-#define AUTH_TIMEOUT 60
+#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC)
 
 /* Don't accept more connection than this */
 #define MAX_CONNECTIONS 64
 
 /* Don't accept more connection than this */
 #define MAX_CONNECTIONS 64
@@ -81,8 +82,20 @@ typedef struct record_stream {
 
     pa_source_output *source_output;
     pa_memblockq *memblockq;
 
     pa_source_output *source_output;
     pa_memblockq *memblockq;
-    size_t fragment_size;
-    pa_usec_t source_latency;
+
+    pa_bool_t adjust_latency:1;
+    pa_bool_t early_requests:1;
+
+    pa_buffer_attr buffer_attr;
+
+    pa_atomic_t on_the_fly;
+    pa_usec_t configured_source_latency;
+    size_t drop_initial;
+
+    /* Only updated after SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY */
+    size_t on_the_fly_snapshot;
+    pa_usec_t current_monitor_latency;
+    pa_usec_t current_source_latency;
 } record_stream;
 
 PA_DECLARE_CLASS(record_stream);
 } record_stream;
 
 PA_DECLARE_CLASS(record_stream);
@@ -105,18 +118,24 @@ typedef struct playback_stream {
 
     pa_sink_input *sink_input;
     pa_memblockq *memblockq;
 
     pa_sink_input *sink_input;
     pa_memblockq *memblockq;
+
+    pa_bool_t adjust_latency:1;
+    pa_bool_t early_requests:1;
+
     pa_bool_t is_underrun:1;
     pa_bool_t drain_request:1;
     uint32_t drain_tag;
     uint32_t syncid;
 
     pa_atomic_t missing;
     pa_bool_t is_underrun:1;
     pa_bool_t drain_request:1;
     uint32_t drain_tag;
     uint32_t syncid;
 
     pa_atomic_t missing;
-    size_t minreq;
-    pa_usec_t sink_latency;
+    pa_usec_t configured_sink_latency;
+    pa_buffer_attr buffer_attr;
 
     /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
     int64_t read_index, write_index;
     size_t render_memblockq_length;
 
     /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
     int64_t read_index, write_index;
     size_t render_memblockq_length;
+    pa_usec_t current_sink_latency;
+    uint64_t playing_for, underrun_for;
 } playback_stream;
 
 PA_DECLARE_CLASS(playback_stream);
 } playback_stream;
 
 PA_DECLARE_CLASS(playback_stream);
@@ -173,6 +192,10 @@ struct pa_native_protocol {
     pa_hashmap *extensions;
 };
 
     pa_hashmap *extensions;
 };
 
+enum {
+    SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
 enum {
     SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
     SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
 enum {
     SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
     SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
@@ -180,7 +203,8 @@ enum {
     SINK_INPUT_MESSAGE_TRIGGER,
     SINK_INPUT_MESSAGE_SEEK,
     SINK_INPUT_MESSAGE_PREBUF_FORCE,
     SINK_INPUT_MESSAGE_TRIGGER,
     SINK_INPUT_MESSAGE_SEEK,
     SINK_INPUT_MESSAGE_PREBUF_FORCE,
-    SINK_INPUT_MESSAGE_UPDATE_LATENCY
+    SINK_INPUT_MESSAGE_UPDATE_LATENCY,
+    SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR
 };
 
 enum {
 };
 
 enum {
@@ -188,7 +212,8 @@ enum {
     PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
     PLAYBACK_STREAM_MESSAGE_OVERFLOW,
     PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
     PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
     PLAYBACK_STREAM_MESSAGE_OVERFLOW,
     PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
-    PLAYBACK_STREAM_MESSAGE_STARTED
+    PLAYBACK_STREAM_MESSAGE_STARTED,
+    PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH
 };
 
 enum {
 };
 
 enum {
@@ -203,10 +228,11 @@ enum {
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
 static void sink_input_kill_cb(pa_sink_input *i);
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
 static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
 static void sink_input_kill_cb(pa_sink_input *i);
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
-static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest);
 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_update_max_request_cb(pa_sink_input *i, size_t nbytes);
 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_update_max_request_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
 
 static void native_connection_send_memblock(pa_native_connection *c);
 static void playback_stream_request_bytes(struct playback_stream*s);
 
 static void native_connection_send_memblock(pa_native_connection *c);
 static void playback_stream_request_bytes(struct playback_stream*s);
@@ -214,10 +240,12 @@ static void playback_stream_request_bytes(struct playback_stream*s);
 static void source_output_kill_cb(pa_source_output *o);
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
 static void source_output_kill_cb(pa_source_output *o);
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
-static void source_output_moved_cb(pa_source_output *o);
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
 static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 
 static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -256,6 +284,8 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command
 static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_sink_or_source_port(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] = {
     [PA_COMMAND_ERROR] = NULL,
 
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_ERROR] = NULL,
@@ -283,6 +313,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_GET_SINK_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
     [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
     [PA_COMMAND_GET_SINK_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
     [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
+    [PA_COMMAND_GET_CARD_INFO] = command_get_info,
     [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
     [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
     [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
     [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
@@ -291,6 +322,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
@@ -348,11 +380,17 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
     [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
 
     [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
     [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
 
+    [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+
+    [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
+    [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
+
     [PA_COMMAND_EXTENSION] = command_extension
 };
 
 /* structure management */
 
     [PA_COMMAND_EXTENSION] = command_extension
 };
 
 /* structure management */
 
+/* Called from main context */
 static void upload_stream_unlink(upload_stream *s) {
     pa_assert(s);
 
 static void upload_stream_unlink(upload_stream *s) {
     pa_assert(s);
 
@@ -364,6 +402,7 @@ static void upload_stream_unlink(upload_stream *s) {
     upload_stream_unref(s);
 }
 
     upload_stream_unref(s);
 }
 
+/* Called from main context */
 static void upload_stream_free(pa_object *o) {
     upload_stream *s = UPLOAD_STREAM(o);
     pa_assert(s);
 static void upload_stream_free(pa_object *o) {
     upload_stream *s = UPLOAD_STREAM(o);
     pa_assert(s);
@@ -381,6 +420,7 @@ static void upload_stream_free(pa_object *o) {
     pa_xfree(s);
 }
 
     pa_xfree(s);
 }
 
+/* Called from main context */
 static upload_stream* upload_stream_new(
         pa_native_connection *c,
         const pa_sample_spec *ss,
 static upload_stream* upload_stream_new(
         pa_native_connection *c,
         const pa_sample_spec *ss,
@@ -413,6 +453,7 @@ static upload_stream* upload_stream_new(
     return s;
 }
 
     return s;
 }
 
+/* Called from main context */
 static void record_stream_unlink(record_stream *s) {
     pa_assert(s);
 
 static void record_stream_unlink(record_stream *s) {
     pa_assert(s);
 
@@ -430,6 +471,7 @@ static void record_stream_unlink(record_stream *s) {
     record_stream_unref(s);
 }
 
     record_stream_unref(s);
 }
 
+/* Called from main context */
 static void record_stream_free(pa_object *o) {
     record_stream *s = RECORD_STREAM(o);
     pa_assert(s);
 static void record_stream_free(pa_object *o) {
     record_stream *s = RECORD_STREAM(o);
     pa_assert(s);
@@ -440,6 +482,7 @@ static void record_stream_free(pa_object *o) {
     pa_xfree(s);
 }
 
     pa_xfree(s);
 }
 
+/* Called from main context */
 static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     record_stream *s = RECORD_STREAM(o);
     record_stream_assert_ref(s);
 static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     record_stream *s = RECORD_STREAM(o);
     record_stream_assert_ref(s);
@@ -451,6 +494,10 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
 
         case RECORD_STREAM_MESSAGE_POST_DATA:
 
 
         case RECORD_STREAM_MESSAGE_POST_DATA:
 
+            /* We try to keep up to date with how many bytes are
+             * currently on the fly */
+            pa_atomic_sub(&s->on_the_fly, chunk->length);
+
             if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
 /*                 pa_log_warn("Failed to push data into output queue."); */
                 return -1;
             if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
 /*                 pa_log_warn("Failed to push data into output queue."); */
                 return -1;
@@ -465,35 +512,34 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
     return 0;
 }
 
     return 0;
 }
 
-static void fix_record_buffer_attr_pre(
-        record_stream *s,
-        pa_bool_t adjust_latency,
-        pa_bool_t early_requests,
-        uint32_t *maxlength,
-        uint32_t *fragsize) {
+/* Called from main context */
+static void fix_record_buffer_attr_pre(record_stream *s) {
 
     size_t frame_size;
     pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
 
     pa_assert(s);
 
     size_t frame_size;
     pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
 
     pa_assert(s);
-    pa_assert(maxlength);
-    pa_assert(fragsize);
+
+    /* This function will be called from the main thread, before as
+     * well as after the source output has been activated using
+     * pa_source_output_put()! That means it may not touch any
+     * ->thread_info data! */
 
     frame_size = pa_frame_size(&s->source_output->sample_spec);
 
 
     frame_size = pa_frame_size(&s->source_output->sample_spec);
 
-    if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
-        *maxlength = MAX_MEMBLOCKQ_LENGTH;
-    if (*maxlength <= 0)
-        *maxlength = (uint32_t) frame_size;
+    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+    if (s->buffer_attr.maxlength <= 0)
+        s->buffer_attr.maxlength = (uint32_t) frame_size;
 
 
-    if (*fragsize == (uint32_t) -1)
-        *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
-    if (*fragsize <= 0)
-        *fragsize = (uint32_t) frame_size;
+    if (s->buffer_attr.fragsize == (uint32_t) -1)
+        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = (uint32_t) frame_size;
 
 
-    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec);
 
 
-    if (early_requests) {
+    if (s->early_requests) {
 
         /* In early request mode we need to emulate the classic
          * fragment-based playback model. We do this setting the source
 
         /* In early request mode we need to emulate the classic
          * fragment-based playback model. We do this setting the source
@@ -501,7 +547,7 @@ static void fix_record_buffer_attr_pre(
 
         source_usec = fragsize_usec;
 
 
         source_usec = fragsize_usec;
 
-    } else if (adjust_latency) {
+    } else if (s->adjust_latency) {
 
         /* So, the user asked us to adjust the latency according to
          * what the source can provide. Half the latency will be
 
         /* So, the user asked us to adjust the latency according to
          * what the source can provide. Half the latency will be
@@ -515,88 +561,85 @@ static void fix_record_buffer_attr_pre(
         /* Ok, the user didn't ask us to adjust the latency, hence we
          * don't */
 
         /* Ok, the user didn't ask us to adjust the latency, hence we
          * don't */
 
-        source_usec = 0;
+        source_usec = (pa_usec_t) -1;
     }
 
     }
 
-    if (source_usec > 0)
-        s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
+    if (source_usec != (pa_usec_t) -1)
+        s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
     else
     else
-        s->source_latency = 0;
+        s->configured_source_latency = 0;
 
 
-    if (early_requests) {
+    if (s->early_requests) {
 
         /* Ok, we didn't necessarily get what we were asking for, so
          * let's tell the user */
 
 
         /* Ok, we didn't necessarily get what we were asking for, so
          * let's tell the user */
 
-        fragsize_usec = s->source_latency;
+        fragsize_usec = s->configured_source_latency;
 
 
-    } else if (adjust_latency) {
+    } else if (s->adjust_latency) {
 
         /* Now subtract what we actually got */
 
 
         /* Now subtract what we actually got */
 
-        if (fragsize_usec >= s->source_latency*2)
-            fragsize_usec -= s->source_latency;
+        if (fragsize_usec >= s->configured_source_latency*2)
+            fragsize_usec -= s->configured_source_latency;
         else
         else
-            fragsize_usec = s->source_latency;
+            fragsize_usec = s->configured_source_latency;
     }
 
     if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
         pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
 
     }
 
     if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
         pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
 
-        *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
 
 
-    if (*fragsize <= 0)
-        *fragsize = (uint32_t) frame_size;
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = (uint32_t) frame_size;
 }
 
 }
 
-static void fix_record_buffer_attr_post(
-        record_stream *s,
-        uint32_t *maxlength,
-        uint32_t *fragsize) {
-
+/* Called from main context */
+static void fix_record_buffer_attr_post(record_stream *s) {
     size_t base;
 
     pa_assert(s);
     size_t base;
 
     pa_assert(s);
-    pa_assert(maxlength);
-    pa_assert(fragsize);
 
 
-    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+    /* This function will be called from the main thread, before as
+     * well as after the source output has been activated using
+     * pa_source_output_put()! That means it may not touch and
+     * ->thread_info data! */
 
     base = pa_frame_size(&s->source_output->sample_spec);
 
 
     base = pa_frame_size(&s->source_output->sample_spec);
 
-    s->fragment_size = (*fragsize/base)*base;
-    if (s->fragment_size <= 0)
-        s->fragment_size = base;
-
-    if (s->fragment_size > *maxlength)
-        s->fragment_size = *maxlength;
+    s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = base;
 
 
-    *fragsize = (uint32_t) s->fragment_size;
+    if (s->buffer_attr.fragsize > s->buffer_attr.maxlength)
+        s->buffer_attr.fragsize = s->buffer_attr.maxlength;
 }
 
 }
 
+/* Called from main context */
 static record_stream* record_stream_new(
         pa_native_connection *c,
         pa_source *source,
         pa_sample_spec *ss,
         pa_channel_map *map,
         pa_bool_t peak_detect,
 static record_stream* record_stream_new(
         pa_native_connection *c,
         pa_source *source,
         pa_sample_spec *ss,
         pa_channel_map *map,
         pa_bool_t peak_detect,
-        uint32_t *maxlength,
-        uint32_t *fragsize,
+        pa_buffer_attr *attr,
         pa_source_output_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_sink_input *direct_on_input,
         pa_source_output_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_sink_input *direct_on_input,
-        pa_bool_t early_requests) {
+        pa_bool_t early_requests,
+        int *ret) {
 
     record_stream *s;
 
     record_stream *s;
-    pa_source_output *source_output;
+    pa_source_output *source_output = NULL;
     size_t base;
     pa_source_output_new_data data;
 
     pa_assert(c);
     pa_assert(ss);
     size_t base;
     pa_source_output_new_data data;
 
     pa_assert(c);
     pa_assert(ss);
-    pa_assert(maxlength);
     pa_assert(p);
     pa_assert(p);
+    pa_assert(ret);
 
     pa_source_output_new_data_init(&data);
 
 
     pa_source_output_new_data_init(&data);
 
@@ -611,7 +654,7 @@ static record_stream* record_stream_new(
     if (peak_detect)
         data.resample_method = PA_RESAMPLER_PEAKS;
 
     if (peak_detect)
         data.resample_method = PA_RESAMPLER_PEAKS;
 
-    source_output = pa_source_output_new(c->protocol->core, &data, flags);
+    *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags);
 
     pa_source_output_new_data_done(&data);
 
 
     pa_source_output_new_data_done(&data);
 
@@ -623,19 +666,25 @@ static record_stream* record_stream_new(
     s->parent.process_msg = record_stream_process_msg;
     s->connection = c;
     s->source_output = source_output;
     s->parent.process_msg = record_stream_process_msg;
     s->connection = c;
     s->source_output = source_output;
+    s->buffer_attr = *attr;
+    s->adjust_latency = adjust_latency;
+    s->early_requests = early_requests;
+    pa_atomic_store(&s->on_the_fly, 0);
 
 
+    s->source_output->parent.process_msg = source_output_process_msg;
     s->source_output->push = source_output_push_cb;
     s->source_output->kill = source_output_kill_cb;
     s->source_output->get_latency = source_output_get_latency_cb;
     s->source_output->push = source_output_push_cb;
     s->source_output->kill = source_output_kill_cb;
     s->source_output->get_latency = source_output_get_latency_cb;
-    s->source_output->moved = source_output_moved_cb;
+    s->source_output->moving = source_output_moving_cb;
     s->source_output->suspend = source_output_suspend_cb;
     s->source_output->suspend = source_output_suspend_cb;
+    s->source_output->send_event = source_output_send_event_cb;
     s->source_output->userdata = s;
 
     s->source_output->userdata = s;
 
-    fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
+    fix_record_buffer_attr_pre(s);
 
     s->memblockq = pa_memblockq_new(
             0,
 
     s->memblockq = pa_memblockq_new(
             0,
-            *maxlength,
+            s->buffer_attr.maxlength,
             0,
             base = pa_frame_size(&source_output->sample_spec),
             1,
             0,
             base = pa_frame_size(&source_output->sample_spec),
             1,
@@ -643,7 +692,8 @@ static record_stream* record_stream_new(
             0,
             NULL);
 
             0,
             NULL);
 
-    fix_record_buffer_attr_post(s, maxlength, fragsize);
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+    fix_record_buffer_attr_post(s);
 
     *ss = s->source_output->sample_spec;
     *map = s->source_output->channel_map;
 
     *ss = s->source_output->sample_spec;
     *map = s->source_output->channel_map;
@@ -651,14 +701,15 @@ static record_stream* record_stream_new(
     pa_idxset_put(c->record_streams, s, &s->index);
 
     pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
     pa_idxset_put(c->record_streams, s, &s->index);
 
     pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
-                ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
-                (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
-                (double) s->source_latency / PA_USEC_PER_MSEC);
+                ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+                (double) s->configured_source_latency / PA_USEC_PER_MSEC);
 
     pa_source_output_put(s->source_output);
     return s;
 }
 
 
     pa_source_output_put(s->source_output);
     return s;
 }
 
+/* Called from main context */
 static void record_stream_send_killed(record_stream *r) {
     pa_tagstruct *t;
     record_stream_assert_ref(r);
 static void record_stream_send_killed(record_stream *r) {
     pa_tagstruct *t;
     record_stream_assert_ref(r);
@@ -670,6 +721,7 @@ static void record_stream_send_killed(record_stream *r) {
     pa_pstream_send_tagstruct(r->connection->pstream, t);
 }
 
     pa_pstream_send_tagstruct(r->connection->pstream, t);
 }
 
+/* Called from main context */
 static void playback_stream_unlink(playback_stream *s) {
     pa_assert(s);
 
 static void playback_stream_unlink(playback_stream *s) {
     pa_assert(s);
 
@@ -690,6 +742,7 @@ static void playback_stream_unlink(playback_stream *s) {
     playback_stream_unref(s);
 }
 
     playback_stream_unref(s);
 }
 
+/* Called from main context */
 static void playback_stream_free(pa_object* o) {
     playback_stream *s = PLAYBACK_STREAM(o);
     pa_assert(s);
 static void playback_stream_free(pa_object* o) {
     playback_stream *s = PLAYBACK_STREAM(o);
     pa_assert(s);
@@ -700,6 +753,7 @@ static void playback_stream_free(pa_object* o) {
     pa_xfree(s);
 }
 
     pa_xfree(s);
 }
 
+/* Called from main context */
 static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     playback_stream *s = PLAYBACK_STREAM(o);
     playback_stream_assert_ref(s);
 static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     playback_stream *s = PLAYBACK_STREAM(o);
     playback_stream_assert_ref(s);
@@ -708,26 +762,24 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
         return -1;
 
     switch (code) {
         return -1;
 
     switch (code) {
+
         case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
             pa_tagstruct *t;
         case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
             pa_tagstruct *t;
-            uint32_t l = 0;
+            int l = 0;
 
             for (;;) {
 
             for (;;) {
-                if ((l = (uint32_t) pa_atomic_load(&s->missing)) <= 0)
-                    break;
+                if ((l = pa_atomic_load(&s->missing)) <= 0)
+                    return 0;
 
 
-                if (pa_atomic_cmpxchg(&s->missing, (int) l, 0))
+                if (pa_atomic_cmpxchg(&s->missing, l, 0))
                     break;
             }
 
                     break;
             }
 
-            if (l <= 0)
-                break;
-
             t = pa_tagstruct_new(NULL, 0);
             pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
             pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
             pa_tagstruct_putu32(t, s->index);
             t = pa_tagstruct_new(NULL, 0);
             pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
             pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
             pa_tagstruct_putu32(t, s->index);
-            pa_tagstruct_putu32(t, l);
+            pa_tagstruct_putu32(t, (uint32_t) l);
             pa_pstream_send_tagstruct(s->connection->pstream, t);
 
 /*             pa_log("Requesting %lu bytes", (unsigned long) l); */
             pa_pstream_send_tagstruct(s->connection->pstream, t);
 
 /*             pa_log("Requesting %lu bytes", (unsigned long) l); */
@@ -765,7 +817,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
             if (s->connection->version >= 13) {
                 pa_tagstruct *t;
 
             if (s->connection->version >= 13) {
                 pa_tagstruct *t;
 
-                /* Notify the user we're overflowed*/
+                /* Notify the user we started playback */
                 t = pa_tagstruct_new(NULL, 0);
                 pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
                 pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
                 t = pa_tagstruct_new(NULL, 0);
                 pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
                 pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
@@ -778,67 +830,79 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
         case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
             pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
             break;
         case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
             pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
             break;
+
+        case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: {
+            pa_tagstruct *t;
+
+            s->buffer_attr.tlength = (uint32_t) offset;
+
+            t = pa_tagstruct_new(NULL, 0);
+            pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
+            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+            pa_tagstruct_putu32(t, s->index);
+            pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+            pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+            pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+            pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+            pa_tagstruct_put_usec(t, s->configured_sink_latency);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+            break;
+        }
     }
 
     return 0;
 }
 
     }
 
     return 0;
 }
 
-static void fix_playback_buffer_attr_pre(
-        playback_stream *s,
-        pa_bool_t adjust_latency,
-        pa_bool_t early_requests,
-        uint32_t *maxlength,
-        uint32_t *tlength,
-        uint32_t* prebuf,
-        uint32_t* minreq) {
-
-    size_t frame_size;
+/* Called from main context */
+static void fix_playback_buffer_attr(playback_stream *s) {
+    size_t frame_size, max_prebuf;
     pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
 
     pa_assert(s);
     pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
 
     pa_assert(s);
-    pa_assert(maxlength);
-    pa_assert(tlength);
-    pa_assert(prebuf);
-    pa_assert(minreq);
+
+    /* This function will be called from the main thread, before as
+     * well as after the sink input has been activated using
+     * pa_sink_input_put()! That means it may not touch any
+     * ->thread_info data, such as the memblockq! */
 
     frame_size = pa_frame_size(&s->sink_input->sample_spec);
 
 
     frame_size = pa_frame_size(&s->sink_input->sample_spec);
 
-    if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
-        *maxlength = MAX_MEMBLOCKQ_LENGTH;
-    if (*maxlength <= 0)
-        *maxlength = (uint32_t) frame_size;
+    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+    if (s->buffer_attr.maxlength <= 0)
+        s->buffer_attr.maxlength = (uint32_t) frame_size;
 
 
-    if (*tlength == (uint32_t) -1)
-        *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
-    if (*tlength <= 0)
-        *tlength = (uint32_t) frame_size;
+    if (s->buffer_attr.tlength == (uint32_t) -1)
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+    if (s->buffer_attr.tlength <= 0)
+        s->buffer_attr.tlength = (uint32_t) frame_size;
 
 
-    if (*minreq == (uint32_t) -1)
-        *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
-    if (*minreq <= 0)
-        *minreq = (uint32_t) frame_size;
+    if (s->buffer_attr.minreq == (uint32_t) -1)
+        s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+    if (s->buffer_attr.minreq <= 0)
+        s->buffer_attr.minreq = (uint32_t) frame_size;
 
 
-    if (*tlength < *minreq+frame_size)
-        *tlength = *minreq+(uint32_t) frame_size;
+    if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size)
+        s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size;
 
 
-    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
-    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec);
+    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);
 
     pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
                 (double) tlength_usec / PA_USEC_PER_MSEC,
                 (double) minreq_usec / PA_USEC_PER_MSEC);
 
 
     pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
                 (double) tlength_usec / PA_USEC_PER_MSEC,
                 (double) minreq_usec / PA_USEC_PER_MSEC);
 
-    if (early_requests) {
+    if (s->early_requests) {
 
         /* In early request mode we need to emulate the classic
          * fragment-based playback model. We do this setting the sink
          * latency to the fragment size. */
 
         sink_usec = minreq_usec;
 
         /* In early request mode we need to emulate the classic
          * fragment-based playback model. We do this setting the sink
          * latency to the fragment size. */
 
         sink_usec = minreq_usec;
-
         pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
 
         pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
 
-    } else if (adjust_latency) {
+    } else if (s->adjust_latency) {
 
         /* So, the user asked us to adjust the latency of the stream
          * buffer according to the what the sink can provide. The
 
         /* So, the user asked us to adjust the latency of the stream
          * buffer according to the what the sink can provide. The
@@ -878,80 +942,60 @@ static void fix_playback_buffer_attr_pre(
         pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
     }
 
         pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
     }
 
-    s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+    s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
 
 
-    if (early_requests) {
+    if (s->early_requests) {
 
         /* Ok, we didn't necessarily get what we were asking for, so
          * let's tell the user */
 
 
         /* Ok, we didn't necessarily get what we were asking for, so
          * let's tell the user */
 
-        minreq_usec = s->sink_latency;
+        minreq_usec = s->configured_sink_latency;
 
 
-    } else if (adjust_latency) {
+    } else if (s->adjust_latency) {
 
         /* Ok, we didn't necessarily get what we were asking for, so
          * let's subtract from what we asked for for the remaining
          * buffer space */
 
 
         /* Ok, we didn't necessarily get what we were asking for, so
          * let's subtract from what we asked for for the remaining
          * buffer space */
 
-        if (tlength_usec >= s->sink_latency)
-            tlength_usec -= s->sink_latency;
+        if (tlength_usec >= s->configured_sink_latency)
+            tlength_usec -= s->configured_sink_latency;
     }
 
     /* FIXME: This is actually larger than necessary, since not all of
      * the sink latency is actually rewritable. */
     }
 
     /* FIXME: This is actually larger than necessary, since not all of
      * the sink latency is actually rewritable. */
-    if (tlength_usec < s->sink_latency + 2*minreq_usec)
-        tlength_usec = s->sink_latency + 2*minreq_usec;
+    if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
+        tlength_usec = s->configured_sink_latency + 2*minreq_usec;
 
     if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
         pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
 
     if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
         pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
-        *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
 
     if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
         pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
 
     if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
         pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
-        *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+        s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
 
 
-    if (*minreq <= 0) {
-        *minreq = (uint32_t) frame_size;
-        *tlength += (uint32_t) frame_size*2;
+    if (s->buffer_attr.minreq <= 0) {
+        s->buffer_attr.minreq = (uint32_t) frame_size;
+        s->buffer_attr.tlength += (uint32_t) frame_size*2;
     }
 
     }
 
-    if (*tlength <= *minreq)
-        *tlength = *minreq*2 + (uint32_t) frame_size;
-
-    if (*prebuf == (uint32_t) -1 || *prebuf > *tlength)
-        *prebuf = *tlength;
-}
-
-static void fix_playback_buffer_attr_post(
-        playback_stream *s,
-        uint32_t *maxlength,
-        uint32_t *tlength,
-        uint32_t* prebuf,
-        uint32_t* minreq) {
-
-    pa_assert(s);
-    pa_assert(maxlength);
-    pa_assert(tlength);
-    pa_assert(prebuf);
-    pa_assert(minreq);
+    if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
+        s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
 
 
-    *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
-    *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
-    *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
-    *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+    max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
 
 
-    s->minreq = *minreq;
+    if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+        s->buffer_attr.prebuf > max_prebuf)
+        s->buffer_attr.prebuf = max_prebuf;
 }
 
 }
 
+/* Called from main context */
 static playback_stream* playback_stream_new(
         pa_native_connection *c,
         pa_sink *sink,
         pa_sample_spec *ss,
         pa_channel_map *map,
 static playback_stream* playback_stream_new(
         pa_native_connection *c,
         pa_sink *sink,
         pa_sample_spec *ss,
         pa_channel_map *map,
-        uint32_t *maxlength,
-        uint32_t *tlength,
-        uint32_t *prebuf,
-        uint32_t *minreq,
+        pa_buffer_attr *a,
         pa_cvolume *volume,
         pa_bool_t muted,
         pa_bool_t muted_set,
         pa_cvolume *volume,
         pa_bool_t muted,
         pa_bool_t muted_set,
@@ -960,10 +1004,11 @@ static playback_stream* playback_stream_new(
         pa_sink_input_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_sink_input_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
-        pa_bool_t early_requests) {
+        pa_bool_t early_requests,
+        int *ret) {
 
     playback_stream *s, *ssync;
 
     playback_stream *s, *ssync;
-    pa_sink_input *sink_input;
+    pa_sink_input *sink_input = NULL;
     pa_memchunk silence;
     uint32_t idx;
     int64_t start_index;
     pa_memchunk silence;
     uint32_t idx;
     int64_t start_index;
@@ -971,12 +1016,9 @@ static playback_stream* playback_stream_new(
 
     pa_assert(c);
     pa_assert(ss);
 
     pa_assert(c);
     pa_assert(ss);
-    pa_assert(maxlength);
-    pa_assert(tlength);
-    pa_assert(prebuf);
-    pa_assert(minreq);
     pa_assert(missing);
     pa_assert(p);
     pa_assert(missing);
     pa_assert(p);
+    pa_assert(ret);
 
     /* Find syncid group */
     for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
 
     /* Find syncid group */
     for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
@@ -993,8 +1035,10 @@ static playback_stream* playback_stream_new(
 
         if (!sink)
             sink = ssync->sink_input->sink;
 
         if (!sink)
             sink = ssync->sink_input->sink;
-        else if (sink != ssync->sink_input->sink)
+        else if (sink != ssync->sink_input->sink) {
+            *ret = PA_ERR_INVALID;
             return NULL;
             return NULL;
+        }
     }
 
     pa_sink_input_new_data_init(&data);
     }
 
     pa_sink_input_new_data_init(&data);
@@ -1012,7 +1056,7 @@ static playback_stream* playback_stream_new(
         pa_sink_input_new_data_set_muted(&data, muted);
     data.sync_base = ssync ? ssync->sink_input : NULL;
 
         pa_sink_input_new_data_set_muted(&data, muted);
     data.sync_base = ssync ? ssync->sink_input : NULL;
 
-    sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+    *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
 
     pa_sink_input_new_data_done(&data);
 
 
     pa_sink_input_new_data_done(&data);
 
@@ -1028,6 +1072,9 @@ static playback_stream* playback_stream_new(
     s->is_underrun = TRUE;
     s->drain_request = FALSE;
     pa_atomic_store(&s->missing, 0);
     s->is_underrun = TRUE;
     s->drain_request = FALSE;
     pa_atomic_store(&s->missing, 0);
+    s->buffer_attr = *a;
+    s->adjust_latency = adjust_latency;
+    s->early_requests = early_requests;
 
     s->sink_input->parent.process_msg = sink_input_process_msg;
     s->sink_input->pop = sink_input_pop_cb;
 
     s->sink_input->parent.process_msg = sink_input_process_msg;
     s->sink_input->pop = sink_input_pop_cb;
@@ -1035,27 +1082,28 @@ static playback_stream* playback_stream_new(
     s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
     s->sink_input->update_max_request = sink_input_update_max_request_cb;
     s->sink_input->kill = sink_input_kill_cb;
     s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
     s->sink_input->update_max_request = sink_input_update_max_request_cb;
     s->sink_input->kill = sink_input_kill_cb;
-    s->sink_input->moved = sink_input_moved_cb;
+    s->sink_input->moving = sink_input_moving_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
+    s->sink_input->send_event = sink_input_send_event_cb;
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
 
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
 
-    fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq);
-    pa_sink_input_get_silence(sink_input, &silence);
+    fix_playback_buffer_attr(s);
 
 
+    pa_sink_input_get_silence(sink_input, &silence);
     s->memblockq = pa_memblockq_new(
             start_index,
     s->memblockq = pa_memblockq_new(
             start_index,
-            *maxlength,
-            *tlength,
+            s->buffer_attr.maxlength,
+            s->buffer_attr.tlength,
             pa_frame_size(&sink_input->sample_spec),
             pa_frame_size(&sink_input->sample_spec),
-            *prebuf,
-            *minreq,
+            s->buffer_attr.prebuf,
+            s->buffer_attr.minreq,
             0,
             &silence);
             0,
             &silence);
-
     pa_memblock_unref(silence.memblock);
     pa_memblock_unref(silence.memblock);
-    fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
+
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
 
     *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
 
 
     *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
 
@@ -1065,36 +1113,44 @@ static playback_stream* playback_stream_new(
     pa_idxset_put(c->output_streams, s, &s->index);
 
     pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
     pa_idxset_put(c->output_streams, s, &s->index);
 
     pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
-                ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
-                (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
-                (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
-                (double) s->sink_latency / PA_USEC_PER_MSEC);
+                ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+                (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+                (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
 
     pa_sink_input_put(s->sink_input);
     return s;
 }
 
 
     pa_sink_input_put(s->sink_input);
     return s;
 }
 
-/* Called from thread context */
+/* Called from IO context */
 static void playback_stream_request_bytes(playback_stream *s) {
 static void playback_stream_request_bytes(playback_stream *s) {
-    size_t m, previous_missing;
+    size_t m, minreq;
+    int previous_missing;
 
     playback_stream_assert_ref(s);
 
     m = pa_memblockq_pop_missing(s->memblockq);
 
 
     playback_stream_assert_ref(s);
 
     m = pa_memblockq_pop_missing(s->memblockq);
 
+    /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu)", */
+    /*        (unsigned long) m, */
+    /*        pa_memblockq_get_tlength(s->memblockq), */
+    /*        pa_memblockq_get_minreq(s->memblockq), */
+    /*        pa_memblockq_get_length(s->memblockq)); */
+
     if (m <= 0)
         return;
 
 /*     pa_log("request_bytes(%lu)", (unsigned long) m); */
 
     if (m <= 0)
         return;
 
 /*     pa_log("request_bytes(%lu)", (unsigned long) m); */
 
-    previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m);
+    previous_missing = pa_atomic_add(&s->missing, (int) m);
+    minreq = pa_memblockq_get_minreq(s->memblockq);
 
     if (pa_memblockq_prebuf_active(s->memblockq) ||
 
     if (pa_memblockq_prebuf_active(s->memblockq) ||
-        (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+        (previous_missing < (int) minreq && previous_missing + (int) m >= (int) minreq))
         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
 }
 
         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
 }
 
-
+/* Called from main context */
 static void playback_stream_send_killed(playback_stream *p) {
     pa_tagstruct *t;
     playback_stream_assert_ref(p);
 static void playback_stream_send_killed(playback_stream *p) {
     pa_tagstruct *t;
     playback_stream_assert_ref(p);
@@ -1106,6 +1162,7 @@ static void playback_stream_send_killed(playback_stream *p) {
     pa_pstream_send_tagstruct(p->connection->pstream, t);
 }
 
     pa_pstream_send_tagstruct(p->connection->pstream, t);
 }
 
+/* Called from main context */
 static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(o);
     pa_native_connection_assert_ref(c);
 static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(o);
     pa_native_connection_assert_ref(c);
@@ -1127,6 +1184,7 @@ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdat
     return 0;
 }
 
     return 0;
 }
 
+/* Called from main context */
 static void native_connection_unlink(pa_native_connection *c) {
     record_stream *r;
     output_stream *o;
 static void native_connection_unlink(pa_native_connection *c) {
     record_stream *r;
     output_stream *o;
@@ -1166,6 +1224,7 @@ static void native_connection_unlink(pa_native_connection *c) {
     pa_native_connection_unref(c);
 }
 
     pa_native_connection_unref(c);
 }
 
+/* Called from main context */
 static void native_connection_free(pa_object *o) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(o);
 
 static void native_connection_free(pa_object *o) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(o);
 
@@ -1183,6 +1242,7 @@ static void native_connection_free(pa_object *o) {
     pa_xfree(c);
 }
 
     pa_xfree(c);
 }
 
+/* Called from main context */
 static void native_connection_send_memblock(pa_native_connection *c) {
     uint32_t start;
     record_stream *r;
 static void native_connection_send_memblock(pa_native_connection *c) {
     uint32_t start;
     record_stream *r;
@@ -1202,8 +1262,8 @@ static void native_connection_send_memblock(pa_native_connection *c) {
         if (pa_memblockq_peek(r->memblockq,  &chunk) >= 0) {
             pa_memchunk schunk = chunk;
 
         if (pa_memblockq_peek(r->memblockq,  &chunk) >= 0) {
             pa_memchunk schunk = chunk;
 
-            if (schunk.length > r->fragment_size)
-                schunk.length = r->fragment_size;
+            if (schunk.length > r->buffer_attr.fragsize)
+                schunk.length = r->buffer_attr.fragsize;
 
             pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
 
 
             pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
 
@@ -1217,6 +1277,7 @@ static void native_connection_send_memblock(pa_native_connection *c) {
 
 /*** sink input callbacks ***/
 
 
 /*** sink input callbacks ***/
 
+/* Called from thread context */
 static void handle_seek(playback_stream *s, int64_t indexw) {
     playback_stream_assert_ref(s);
 
 static void handle_seek(playback_stream *s, int64_t indexw) {
     playback_stream_assert_ref(s);
 
@@ -1269,7 +1330,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
             int64_t windex;
 
             windex = pa_memblockq_get_write_index(s->memblockq);
             int64_t windex;
 
             windex = pa_memblockq_get_write_index(s->memblockq);
-            pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+
+            /* The client side is incapable of accounting correctly
+             * for seeks of a type != PA_SEEK_RELATIVE. We need to be
+             * able to deal with that. */
+
+            pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
 
             handle_seek(s, windex);
             return 0;
 
             handle_seek(s, windex);
             return 0;
@@ -1285,9 +1351,11 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
 /*             pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
 
             if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
 /*             pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
 
             if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
-                pa_log_warn("Failed to push data into queue");
+
+                if (pa_log_ratelimit())
+                    pa_log_warn("Failed to push data into queue");
                 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
                 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
-                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
+                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
             }
 
             handle_seek(s, windex);
             }
 
             handle_seek(s, windex);
@@ -1356,10 +1424,14 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
         }
 
         case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
         }
 
         case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
-
+            /* Atomically get a snapshot of all timing parameters... */
             s->read_index = pa_memblockq_get_read_index(s->memblockq);
             s->write_index = pa_memblockq_get_write_index(s->memblockq);
             s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
             s->read_index = pa_memblockq_get_read_index(s->memblockq);
             s->write_index = pa_memblockq_get_write_index(s->memblockq);
             s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+            s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink);
+            s->underrun_for = s->sink_input->thread_info.underrun_for;
+            s->playing_for = s->sink_input->thread_info.playing_for;
+
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_SET_STATE: {
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_SET_STATE: {
@@ -1384,6 +1456,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
              * latency added by the resampler */
             break;
         }
              * latency added by the resampler */
             break;
         }
+
+        case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: {
+            pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+            pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+            return 0;
+        }
     }
 
     return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
     }
 
     return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
@@ -1403,7 +1481,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     if (pa_memblockq_is_readable(s->memblockq))
         s->is_underrun = FALSE;
     else {
     if (pa_memblockq_is_readable(s->memblockq))
         s->is_underrun = FALSE;
     else {
-/*         pa_log("%s, UNDERRUN: %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+        if (!s->is_underrun)
+            pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
 
         if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
             s->drain_request = FALSE;
 
         if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
             s->drain_request = FALSE;
@@ -1432,6 +1511,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     return 0;
 }
 
     return 0;
 }
 
+/* Called from thread context */
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
 
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
 
@@ -1446,6 +1526,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_memblockq_rewind(s->memblockq, nbytes);
 }
 
     pa_memblockq_rewind(s->memblockq, nbytes);
 }
 
+/* Called from thread context */
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
 
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
 
@@ -1456,18 +1537,30 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_memblockq_set_maxrewind(s->memblockq, nbytes);
 }
 
     pa_memblockq_set_maxrewind(s->memblockq, nbytes);
 }
 
+/* Called from thread context */
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
-    size_t tlength;
+    size_t new_tlength, old_tlength;
 
     pa_sink_input_assert_ref(i);
     s = PLAYBACK_STREAM(i->userdata);
     playback_stream_assert_ref(s);
 
 
     pa_sink_input_assert_ref(i);
     s = PLAYBACK_STREAM(i->userdata);
     playback_stream_assert_ref(s);
 
-    tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+    old_tlength = pa_memblockq_get_tlength(s->memblockq);
+    new_tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+
+    if (old_tlength < new_tlength) {
+        pa_log_debug("max_request changed, trying to update from %zu to %zu.", old_tlength, new_tlength);
+        pa_memblockq_set_tlength(s->memblockq, new_tlength);
+        new_tlength = pa_memblockq_get_tlength(s->memblockq);
 
 
-    if (pa_memblockq_get_tlength(s->memblockq) < tlength)
-        pa_memblockq_set_tlength(s->memblockq, tlength);
+        if (new_tlength == old_tlength)
+            pa_log_debug("Failed to increase tlength");
+        else {
+            pa_log_debug("Notifying client about increased tlength");
+            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL);
+        }
+    }
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
@@ -1482,6 +1575,27 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     playback_stream_unlink(s);
 }
 
     playback_stream_unlink(s);
 }
 
+/* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
 /* Called from main context */
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
     playback_stream *s;
 /* Called from main context */
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
     playback_stream *s;
@@ -1503,26 +1617,20 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
-static void sink_input_moved_cb(pa_sink_input *i) {
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
     playback_stream *s;
     pa_tagstruct *t;
     playback_stream *s;
     pa_tagstruct *t;
-    uint32_t maxlength, tlength, prebuf, minreq;
 
     pa_sink_input_assert_ref(i);
     s = PLAYBACK_STREAM(i->userdata);
     playback_stream_assert_ref(s);
 
 
     pa_sink_input_assert_ref(i);
     s = PLAYBACK_STREAM(i->userdata);
     playback_stream_assert_ref(s);
 
-    maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
-    tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
-    prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
-    minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+    if (!dest)
+        return;
 
 
-    fix_playback_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &tlength, &prebuf, &minreq);
-    pa_memblockq_set_maxlength(s->memblockq, maxlength);
-    pa_memblockq_set_tlength(s->memblockq, tlength);
-    pa_memblockq_set_prebuf(s->memblockq, prebuf);
-    pa_memblockq_set_minreq(s->memblockq, minreq);
-    fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+    fix_playback_buffer_attr(s);
+    pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
 
     if (s->connection->version < 12)
       return;
 
     if (s->connection->version < 12)
       return;
@@ -1531,16 +1639,16 @@ static void sink_input_moved_cb(pa_sink_input *i) {
     pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
     pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
     pa_tagstruct_putu32(t, s->index);
     pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
     pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
     pa_tagstruct_putu32(t, s->index);
-    pa_tagstruct_putu32(t, i->sink->index);
-    pa_tagstruct_puts(t, i->sink->name);
-    pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+    pa_tagstruct_putu32(t, dest->index);
+    pa_tagstruct_puts(t, dest->name);
+    pa_tagstruct_put_boolean(t, pa_sink_get_state(dest) == PA_SINK_SUSPENDED);
 
     if (s->connection->version >= 13) {
 
     if (s->connection->version >= 13) {
-        pa_tagstruct_putu32(t, maxlength);
-        pa_tagstruct_putu32(t, tlength);
-        pa_tagstruct_putu32(t, prebuf);
-        pa_tagstruct_putu32(t, minreq);
-        pa_tagstruct_put_usec(t, s->sink_latency);
+        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+        pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+        pa_tagstruct_put_usec(t, s->configured_sink_latency);
     }
 
     pa_pstream_send_tagstruct(s->connection->pstream, t);
     }
 
     pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1548,6 +1656,27 @@ static void sink_input_moved_cb(pa_sink_input *i) {
 
 /*** source_output callbacks ***/
 
 
 /*** source_output callbacks ***/
 
+/* Called from thread context */
+static int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(_o);
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    switch (code) {
+        case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
+            /* Atomically get a snapshot of all timing parameters... */
+            s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of) : 0;
+            s->current_source_latency = pa_source_get_latency_within_thread(o->source);
+            s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
+            return 0;
+    }
+
+    return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
+}
+
 /* Called from thread context */
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
     record_stream *s;
 /* Called from thread context */
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
     record_stream *s;
@@ -1557,6 +1686,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
     record_stream_assert_ref(s);
     pa_assert(chunk);
 
     record_stream_assert_ref(s);
     pa_assert(chunk);
 
+    pa_atomic_add(&s->on_the_fly, chunk->length);
     pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
 }
 
     pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
 }
 
@@ -1583,6 +1713,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
     return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
 }
 
     return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
 }
 
+/* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
 /* Called from main context */
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
     record_stream *s;
 /* Called from main context */
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
     record_stream *s;
@@ -1604,21 +1755,21 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
 }
 
 /* Called from main context */
 }
 
 /* Called from main context */
-static void source_output_moved_cb(pa_source_output *o) {
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     record_stream *s;
     pa_tagstruct *t;
     record_stream *s;
     pa_tagstruct *t;
-    uint32_t maxlength, fragsize;
 
     pa_source_output_assert_ref(o);
     s = RECORD_STREAM(o->userdata);
     record_stream_assert_ref(s);
 
 
     pa_source_output_assert_ref(o);
     s = RECORD_STREAM(o->userdata);
     record_stream_assert_ref(s);
 
-    fragsize = (uint32_t) s->fragment_size;
-    maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
+    if (!dest)
+        return;
 
 
-    fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize);
-    pa_memblockq_set_maxlength(s->memblockq, maxlength);
-    fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+    fix_record_buffer_attr_pre(s);
+    pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
+    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+    fix_record_buffer_attr_post(s);
 
     if (s->connection->version < 12)
       return;
 
     if (s->connection->version < 12)
       return;
@@ -1627,14 +1778,14 @@ static void source_output_moved_cb(pa_source_output *o) {
     pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
     pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
     pa_tagstruct_putu32(t, s->index);
     pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
     pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
     pa_tagstruct_putu32(t, s->index);
-    pa_tagstruct_putu32(t, o->source->index);
-    pa_tagstruct_puts(t, o->source->name);
-    pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+    pa_tagstruct_putu32(t, dest->index);
+    pa_tagstruct_puts(t, dest->name);
+    pa_tagstruct_put_boolean(t, pa_source_get_state(dest) == PA_SOURCE_SUSPENDED);
 
     if (s->connection->version >= 13) {
 
     if (s->connection->version >= 13) {
-        pa_tagstruct_putu32(t, maxlength);
-        pa_tagstruct_putu32(t, fragsize);
-        pa_tagstruct_put_usec(t, s->source_latency);
+        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+        pa_tagstruct_put_usec(t, s->configured_source_latency);
     }
 
     pa_pstream_send_tagstruct(s->connection->pstream, t);
     }
 
     pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1666,7 +1817,8 @@ static pa_tagstruct *reply_new(uint32_t tag) {
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     playback_stream *s;
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     playback_stream *s;
-    uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
+    uint32_t sink_index, syncid, missing;
+    pa_buffer_attr attr;
     const char *name = NULL, *sink_name;
     pa_sample_spec ss;
     pa_channel_map map;
     const char *name = NULL, *sink_name;
     pa_sample_spec ss;
     pa_channel_map map;
@@ -1686,14 +1838,16 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         adjust_latency = FALSE,
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
         adjust_latency = FALSE,
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
-        muted_set = FALSE;
-
+        muted_set = FALSE,
+        fail_on_suspend = FALSE;
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
+    int ret = PA_ERR_INVALID;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
+    memset(&attr, 0, sizeof(attr));
 
     if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
         pa_tagstruct_get(
 
     if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
         pa_tagstruct_get(
@@ -1702,11 +1856,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
                 PA_TAG_CHANNEL_MAP, &map,
                 PA_TAG_U32, &sink_index,
                 PA_TAG_STRING, &sink_name,
                 PA_TAG_CHANNEL_MAP, &map,
                 PA_TAG_U32, &sink_index,
                 PA_TAG_STRING, &sink_name,
-                PA_TAG_U32, &maxlength,
+                PA_TAG_U32, &attr.maxlength,
                 PA_TAG_BOOLEAN, &corked,
                 PA_TAG_BOOLEAN, &corked,
-                PA_TAG_U32, &tlength,
-                PA_TAG_U32, &prebuf,
-                PA_TAG_U32, &minreq,
+                PA_TAG_U32, &attr.tlength,
+                PA_TAG_U32, &attr.prebuf,
+                PA_TAG_U32, &attr.minreq,
                 PA_TAG_U32, &syncid,
                 PA_TAG_CVOLUME, &volume,
                 PA_TAG_INVALID) < 0) {
                 PA_TAG_U32, &syncid,
                 PA_TAG_CVOLUME, &volume,
                 PA_TAG_INVALID) < 0) {
@@ -1770,7 +1924,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     if (c->version >= 15) {
 
         if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
     if (c->version >= 15) {
 
         if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
-            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
             protocol_error(c);
             pa_proplist_free(p);
             return;
             protocol_error(c);
             pa_proplist_free(p);
             return;
@@ -1809,16 +1964,17 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         (fix_channels ?  PA_SINK_INPUT_FIX_CHANNELS : 0) |
         (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0) |
         (fix_channels ?  PA_SINK_INPUT_FIX_CHANNELS : 0) |
         (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0) |
-        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+        (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0);
 
     /* Only since protocol version 15 there's a seperate muted_set
      * flag. For older versions we synthesize it here */
     muted_set = muted_set || muted;
 
 
     /* Only since protocol version 15 there's a seperate muted_set
      * flag. For older versions we synthesize it here */
     muted_set = muted_set || muted;
 
-    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests);
+    s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
     pa_proplist_free(p);
 
     pa_proplist_free(p);
 
-    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, s, tag, ret);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
@@ -1831,10 +1987,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     if (c->version >= 9) {
         /* Since 0.9.0 we support sending the buffer metrics back to the client */
 
     if (c->version >= 9) {
         /* Since 0.9.0 we support sending the buffer metrics back to the client */
 
-        pa_tagstruct_putu32(reply, (uint32_t) maxlength);
-        pa_tagstruct_putu32(reply, (uint32_t) tlength);
-        pa_tagstruct_putu32(reply, (uint32_t) prebuf);
-        pa_tagstruct_putu32(reply, (uint32_t) minreq);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);
     }
 
     if (c->version >= 12) {
     }
 
     if (c->version >= 12) {
@@ -1852,7 +2008,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     }
 
     if (c->version >= 13)
     }
 
     if (c->version >= 13)
-        pa_tagstruct_put_usec(reply, s->sink_latency);
+        pa_tagstruct_put_usec(reply, s->configured_sink_latency);
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
@@ -1918,7 +2074,7 @@ static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t t
 static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     record_stream *s;
 static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     record_stream *s;
-    uint32_t maxlength, fragment_size;
+    pa_buffer_attr attr;
     uint32_t source_index;
     const char *name = NULL, *source_name;
     pa_sample_spec ss;
     uint32_t source_index;
     const char *name = NULL, *source_name;
     pa_sample_spec ss;
@@ -1937,23 +2093,27 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         adjust_latency = FALSE,
         peak_detect = FALSE,
         early_requests = FALSE,
         adjust_latency = FALSE,
         peak_detect = FALSE,
         early_requests = FALSE,
-        dont_inhibit_auto_suspend = FALSE;
+        dont_inhibit_auto_suspend = FALSE,
+        fail_on_suspend = FALSE;
     pa_source_output_flags_t flags = 0;
     pa_proplist *p;
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
     pa_sink_input *direct_on_input = NULL;
     pa_source_output_flags_t flags = 0;
     pa_proplist *p;
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
     pa_sink_input *direct_on_input = NULL;
+    int ret = PA_ERR_INVALID;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
+    memset(&attr, 0, sizeof(attr));
+
     if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_get_channel_map(t, &map) < 0 ||
         pa_tagstruct_getu32(t, &source_index) < 0 ||
         pa_tagstruct_gets(t, &source_name) < 0 ||
     if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_get_channel_map(t, &map) < 0 ||
         pa_tagstruct_getu32(t, &source_index) < 0 ||
         pa_tagstruct_gets(t, &source_name) < 0 ||
-        pa_tagstruct_getu32(t, &maxlength) < 0 ||
+        pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
         pa_tagstruct_get_boolean(t, &corked) < 0 ||
         pa_tagstruct_get_boolean(t, &corked) < 0 ||
-        pa_tagstruct_getu32(t, &fragment_size) < 0) {
+        pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
         protocol_error(c);
         return;
     }
         protocol_error(c);
         return;
     }
@@ -2011,7 +2171,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
 
     if (c->version >= 15) {
 
 
     if (c->version >= 15) {
 
-        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
             protocol_error(c);
             pa_proplist_free(p);
             return;
             protocol_error(c);
             pa_proplist_free(p);
             return;
@@ -2059,12 +2220,13 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (fix_channels ?  PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
         (no_move ?  PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
         (fix_channels ?  PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
         (no_move ?  PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
-        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+        (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0);
 
 
-    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
+    s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
     pa_proplist_free(p);
 
     pa_proplist_free(p);
 
-    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, s, tag, ret);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
@@ -2074,8 +2236,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     if (c->version >= 9) {
         /* Since 0.9 we support sending the buffer metrics back to the client */
 
     if (c->version >= 9) {
         /* Since 0.9 we support sending the buffer metrics back to the client */
 
-        pa_tagstruct_putu32(reply, (uint32_t) maxlength);
-        pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);
     }
 
     if (c->version >= 12) {
     }
 
     if (c->version >= 12) {
@@ -2093,7 +2255,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     }
 
     if (c->version >= 13)
     }
 
     if (c->version >= 13)
-        pa_tagstruct_put_usec(reply, s->source_latency);
+        pa_tagstruct_put_usec(reply, s->configured_source_latency);
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
@@ -2277,11 +2439,9 @@ static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t
             return;
         }
 
             return;
         }
 
-    pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+    pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p);
     pa_proplist_free(p);
 
     pa_proplist_free(p);
 
-    pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
-
     reply = reply_new(tag);
 
     if (c->version >= 13)
     reply = reply_new(tag);
 
     if (c->version >= 13)
@@ -2382,7 +2542,6 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
     playback_stream *s;
     struct timeval tv, now;
     uint32_t idx;
     playback_stream *s;
     struct timeval tv, now;
     uint32_t idx;
-    pa_usec_t latency;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2398,25 +2557,27 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
     s = pa_idxset_get_by_index(c->output_streams, idx);
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
     CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
     s = pa_idxset_get_by_index(c->output_streams, idx);
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
     CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
-    CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
 
 
-    reply = reply_new(tag);
-
-    latency = pa_sink_get_latency(s->sink_input->sink);
-    latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
-
-    pa_tagstruct_put_usec(reply, latency);
+    /* Get an atomic snapshot of all timing parameters */
+    pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
 
 
+    reply = reply_new(tag);
+    pa_tagstruct_put_usec(reply,
+                          s->current_sink_latency +
+                          pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sink->sample_spec));
     pa_tagstruct_put_usec(reply, 0);
     pa_tagstruct_put_usec(reply, 0);
-    pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
+    pa_tagstruct_put_boolean(reply,
+                             s->playing_for > 0 &&
+                             pa_sink_get_state(s->sink_input->sink) == PA_SINK_RUNNING &&
+                             pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, s->write_index);
     pa_tagstruct_puts64(reply, s->read_index);
 
     if (c->version >= 13) {
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, s->write_index);
     pa_tagstruct_puts64(reply, s->read_index);
 
     if (c->version >= 13) {
-        pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
-        pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+        pa_tagstruct_putu64(reply, s->underrun_for);
+        pa_tagstruct_putu64(reply, s->playing_for);
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
@@ -2443,10 +2604,17 @@ static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint3
     s = pa_idxset_get_by_index(c->record_streams, idx);
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 
     s = pa_idxset_get_by_index(c->record_streams, idx);
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 
+    /* Get an atomic snapshot of all timing parameters */
+    pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+
     reply = reply_new(tag);
     reply = reply_new(tag);
-    pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
-    pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
-    pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
+    pa_tagstruct_put_usec(reply, s->current_monitor_latency);
+    pa_tagstruct_put_usec(reply,
+                          s->current_source_latency +
+                          pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
+    pa_tagstruct_put_boolean(reply,
+                             pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING &&
+                             pa_source_output_get_state(s->source_output) == PA_SOURCE_OUTPUT_RUNNING);
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -2484,7 +2652,9 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
 
     p = pa_proplist_new();
 
 
     p = pa_proplist_new();
 
-    if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+
         protocol_error(c);
         pa_proplist_free(p);
         return;
         protocol_error(c);
         pa_proplist_free(p);
         return;
@@ -2496,7 +2666,10 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
         if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
             name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
 
         if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
             name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
 
-    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    if (!name || !pa_namereg_is_valid_name(name)) {
+        pa_proplist_free(p);
+        CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_INVALID);
+    }
 
     s = upload_stream_new(c, &ss, &map, name, length, p);
     pa_proplist_free(p);
 
     s = upload_stream_new(c, &ss, &map, name, length, p);
     pa_proplist_free(p);
@@ -2530,7 +2703,9 @@ static void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uin
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
     CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
 
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
     CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
 
-    if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
+    if (!s->memchunk.memblock)
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_TOOLARGE);
+    else if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
         pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
     else
         pa_pstream_send_simple_ack(c->pstream, tag);
         pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
     else
         pa_pstream_send_simple_ack(c->pstream, tag);
@@ -2679,8 +2854,31 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
         pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
     }
 
         pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
     }
 
-    if (c->version >= 15)
+    if (c->version >= 15) {
         pa_tagstruct_put_volume(t, sink->base_volume);
         pa_tagstruct_put_volume(t, sink->base_volume);
+        if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE))
+            pa_log_error("Internal sink state is invalid.");
+        pa_tagstruct_putu32(t, pa_sink_get_state(sink));
+        pa_tagstruct_putu32(t, sink->n_volume_steps);
+        pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
+    }
+
+    if (c->version >= 16) {
+        pa_tagstruct_putu32(t, sink->ports ? pa_hashmap_size(sink->ports) : 0);
+
+        if (sink->ports) {
+            void *state;
+            pa_device_port *p;
+
+            PA_HASHMAP_FOREACH(p, sink->ports, state) {
+                pa_tagstruct_puts(t, p->name);
+                pa_tagstruct_puts(t, p->description);
+                pa_tagstruct_putu32(t, p->priority);
+            }
+        }
+
+        pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
+    }
 }
 
 static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
 }
 
 static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@@ -2713,8 +2911,32 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
         pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
     }
 
         pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
     }
 
-    if (c->version >= 15)
+    if (c->version >= 15) {
         pa_tagstruct_put_volume(t, source->base_volume);
         pa_tagstruct_put_volume(t, source->base_volume);
+        if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE))
+            pa_log_error("Internal source state is invalid.");
+        pa_tagstruct_putu32(t, pa_source_get_state(source));
+        pa_tagstruct_putu32(t, source->n_volume_steps);
+        pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
+    }
+
+    if (c->version >= 16) {
+
+        pa_tagstruct_putu32(t, source->ports ? pa_hashmap_size(source->ports) : 0);
+
+        if (source->ports) {
+            void *state;
+            pa_device_port *p;
+
+            PA_HASHMAP_FOREACH(p, source->ports, state) {
+                pa_tagstruct_puts(t, p->name);
+                pa_tagstruct_puts(t, p->description);
+                pa_tagstruct_putu32(t, p->priority);
+            }
+        }
+
+        pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
+    }
 }
 
 static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
 }
 
 static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
@@ -2728,10 +2950,37 @@ static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_c
 
     if (c->version >= 13)
         pa_tagstruct_put_proplist(t, client->proplist);
 
     if (c->version >= 13)
         pa_tagstruct_put_proplist(t, client->proplist);
+}
+
+static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) {
+    void *state = NULL;
+    pa_card_profile *p;
 
 
+    pa_assert(t);
+    pa_assert(card);
+
+    pa_tagstruct_putu32(t, card->index);
+    pa_tagstruct_puts(t, card->name);
+    pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, card->driver);
+
+    pa_tagstruct_putu32(t, card->profiles ? pa_hashmap_size(card->profiles) : 0);
+
+    if (card->profiles) {
+        while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) {
+            pa_tagstruct_puts(t, p->name);
+            pa_tagstruct_puts(t, p->description);
+            pa_tagstruct_putu32(t, p->n_sinks);
+            pa_tagstruct_putu32(t, p->n_sources);
+            pa_tagstruct_putu32(t, p->priority);
+        }
+    }
+
+    pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL);
+    pa_tagstruct_put_proplist(t, card->proplist);
 }
 
 }
 
-static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
+static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
     pa_assert(t);
     pa_assert(module);
 
     pa_assert(t);
     pa_assert(module);
 
@@ -2739,12 +2988,18 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
     pa_tagstruct_puts(t, module->name);
     pa_tagstruct_puts(t, module->argument);
     pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
     pa_tagstruct_puts(t, module->name);
     pa_tagstruct_puts(t, module->argument);
     pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
-    pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */
+
+    if (c->version < 15)
+        pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */
+
+    if (c->version >= 15)
+        pa_tagstruct_put_proplist(t, module->proplist);
 }
 
 static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
     pa_sample_spec fixed_ss;
     pa_usec_t sink_latency;
 }
 
 static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
     pa_sample_spec fixed_ss;
     pa_usec_t sink_latency;
+    pa_cvolume v;
 
     pa_assert(t);
     pa_sink_input_assert_ref(s);
 
     pa_assert(t);
     pa_sink_input_assert_ref(s);
@@ -2758,7 +3013,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
     pa_tagstruct_putu32(t, s->sink->index);
     pa_tagstruct_put_sample_spec(t, &fixed_ss);
     pa_tagstruct_put_channel_map(t, &s->channel_map);
     pa_tagstruct_putu32(t, s->sink->index);
     pa_tagstruct_put_sample_spec(t, &fixed_ss);
     pa_tagstruct_put_channel_map(t, &s->channel_map);
-    pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));
+    pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE));
     pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
     pa_tagstruct_put_usec(t, sink_latency);
     pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
     pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
     pa_tagstruct_put_usec(t, sink_latency);
     pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
@@ -2796,6 +3051,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
 
 static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
     pa_sample_spec fixed_ss;
 
 static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
     pa_sample_spec fixed_ss;
+    pa_cvolume v;
 
     pa_assert(t);
     pa_assert(e);
 
     pa_assert(t);
     pa_assert(e);
@@ -2807,7 +3063,13 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
 
     pa_tagstruct_putu32(t, e->index);
     pa_tagstruct_puts(t, e->name);
 
     pa_tagstruct_putu32(t, e->index);
     pa_tagstruct_puts(t, e->name);
-    pa_tagstruct_put_cvolume(t, &e->volume);
+
+    if (e->volume_is_set)
+        v = e->volume;
+    else
+        pa_cvolume_init(&v);
+
+    pa_tagstruct_put_cvolume(t, &v);
     pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
     pa_tagstruct_put_sample_spec(t, &fixed_ss);
     pa_tagstruct_put_channel_map(t, &e->channel_map);
     pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
     pa_tagstruct_put_sample_spec(t, &fixed_ss);
     pa_tagstruct_put_channel_map(t, &e->channel_map);
@@ -2825,6 +3087,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     pa_sink *sink = NULL;
     pa_source *source = NULL;
     pa_client *client = NULL;
     pa_sink *sink = NULL;
     pa_source *source = NULL;
     pa_client *client = NULL;
+    pa_card *card = NULL;
     pa_module *module = NULL;
     pa_sink_input *si = NULL;
     pa_source_output *so = NULL;
     pa_module *module = NULL;
     pa_sink_input *si = NULL;
     pa_source_output *so = NULL;
@@ -2862,6 +3125,11 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
             source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
         else
             source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
             source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
         else
             source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+    } else if (command == PA_COMMAND_GET_CARD_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+        else
+            card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
     } else if (command == PA_COMMAND_GET_CLIENT_INFO)
         client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
     else if (command == PA_COMMAND_GET_MODULE_INFO)
     } else if (command == PA_COMMAND_GET_CLIENT_INFO)
         client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
     else if (command == PA_COMMAND_GET_MODULE_INFO)
@@ -2878,7 +3146,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
             sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
     }
 
             sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
     }
 
-    if (!sink && !source && !client && !module && !si && !so && !sce) {
+    if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
         pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
         return;
     }
         pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
         return;
     }
@@ -2890,8 +3158,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         source_fill_tagstruct(c, reply, source);
     else if (client)
         client_fill_tagstruct(c, reply, client);
         source_fill_tagstruct(c, reply, source);
     else if (client)
         client_fill_tagstruct(c, reply, client);
+    else if (card)
+        card_fill_tagstruct(c, reply, card);
     else if (module)
     else if (module)
-        module_fill_tagstruct(reply, module);
+        module_fill_tagstruct(c, reply, module);
     else if (si)
         sink_input_fill_tagstruct(c, reply, si);
     else if (so)
     else if (si)
         sink_input_fill_tagstruct(c, reply, si);
     else if (so)
@@ -2926,6 +3196,8 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
         i = c->protocol->core->sources;
     else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
         i = c->protocol->core->clients;
         i = c->protocol->core->sources;
     else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
         i = c->protocol->core->clients;
+    else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+        i = c->protocol->core->cards;
     else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
         i = c->protocol->core->modules;
     else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
     else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
         i = c->protocol->core->modules;
     else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
@@ -2945,8 +3217,10 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
                 source_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
                 client_fill_tagstruct(c, reply, p);
                 source_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
                 client_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+                card_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
             else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
-                module_fill_tagstruct(reply, p);
+                module_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
                 sink_input_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
             else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
                 sink_input_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
@@ -2964,9 +3238,10 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
 static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
 static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
-    char txt[256];
-    const char *n;
+    pa_sink *def_sink;
+    pa_source *def_source;
     pa_sample_spec fixed_ss;
     pa_sample_spec fixed_ss;
+    char *h, *u;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2981,19 +3256,28 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
     reply = reply_new(tag);
     pa_tagstruct_puts(reply, PACKAGE_NAME);
     pa_tagstruct_puts(reply, PACKAGE_VERSION);
     reply = reply_new(tag);
     pa_tagstruct_puts(reply, PACKAGE_NAME);
     pa_tagstruct_puts(reply, PACKAGE_VERSION);
-    pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt)));
-    pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt)));
+
+    u = pa_get_user_name_malloc();
+    pa_tagstruct_puts(reply, u);
+    pa_xfree(u);
+
+    h = pa_get_host_name_malloc();
+    pa_tagstruct_puts(reply, h);
+    pa_xfree(h);
 
     fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
     pa_tagstruct_put_sample_spec(reply, &fixed_ss);
 
 
     fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
     pa_tagstruct_put_sample_spec(reply, &fixed_ss);
 
-    n = pa_namereg_get_default_sink_name(c->protocol->core);
-    pa_tagstruct_puts(reply, n);
-    n = pa_namereg_get_default_source_name(c->protocol->core);
-    pa_tagstruct_puts(reply, n);
+    def_sink = pa_namereg_get_default_sink(c->protocol->core);
+    pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL);
+    def_source = pa_namereg_get_default_source(c->protocol->core);
+    pa_tagstruct_puts(reply, def_source ? def_source->name : NULL);
 
     pa_tagstruct_putu32(reply, c->protocol->core->cookie);
 
 
     pa_tagstruct_putu32(reply, c->protocol->core->cookie);
 
+    if (c->version >= 15)
+        pa_tagstruct_put_channel_map(reply, &c->protocol->core->default_channel_map);
+
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
@@ -3053,6 +3337,7 @@ static void command_set_volume(
     pa_source *source = NULL;
     pa_sink_input *si = NULL;
     const char *name = NULL;
     pa_source *source = NULL;
     pa_sink_input *si = NULL;
     const char *name = NULL;
+    const char *client_name;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -3099,12 +3384,20 @@ static void command_set_volume(
 
     CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
 
 
     CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
 
-    if (sink)
-        pa_sink_set_volume(sink, &volume);
-    else if (source)
-        pa_source_set_volume(source, &volume);
-    else if (si)
-        pa_sink_input_set_volume(si, &volume);
+    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
+
+    if (sink) {
+        pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
+        pa_sink_set_volume(sink, &volume, TRUE, TRUE);
+    } else if (source) {
+        pa_log_debug("Client %s changes volume of sink %s.", client_name, source->name);
+        pa_source_set_volume(source, &volume, TRUE);
+    } else if (si) {
+        pa_log_debug("Client %s changes volume of sink %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
+        pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
+    }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
@@ -3172,11 +3465,11 @@ static void command_set_mute(
     CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
 
     if (sink)
     CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
 
     if (sink)
-        pa_sink_set_mute(sink, mute);
+        pa_sink_set_mute(sink, mute, TRUE);
     else if (source)
     else if (source)
-        pa_source_set_mute(source, mute);
+        pa_source_set_mute(source, mute, TRUE);
     else if (si)
     else if (si)
-        pa_sink_input_set_mute(si, mute);
+        pa_sink_input_set_mute(si, mute, TRUE);
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
@@ -3301,12 +3594,14 @@ static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint
 static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
 static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
-    uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+    pa_buffer_attr a;
     pa_tagstruct *reply;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     pa_tagstruct *reply;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
 
+    memset(&a, 0, sizeof(a));
+
     if (pa_tagstruct_getu32(t, &idx) < 0) {
         protocol_error(c);
         return;
     if (pa_tagstruct_getu32(t, &idx) < 0) {
         protocol_error(c);
         return;
@@ -3324,10 +3619,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
 
         if (pa_tagstruct_get(
                     t,
 
         if (pa_tagstruct_get(
                     t,
-                    PA_TAG_U32, &maxlength,
-                    PA_TAG_U32, &tlength,
-                    PA_TAG_U32, &prebuf,
-                    PA_TAG_U32, &minreq,
+                    PA_TAG_U32, &a.maxlength,
+                    PA_TAG_U32, &a.tlength,
+                    PA_TAG_U32, &a.prebuf,
+                    PA_TAG_U32, &a.minreq,
                     PA_TAG_INVALID) < 0 ||
             (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
             (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
                     PA_TAG_INVALID) < 0 ||
             (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
             (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
@@ -3336,21 +3631,21 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
             return;
         }
 
             return;
         }
 
-        fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq);
-        pa_memblockq_set_maxlength(s->memblockq, maxlength);
-        pa_memblockq_set_tlength(s->memblockq, tlength);
-        pa_memblockq_set_prebuf(s->memblockq, prebuf);
-        pa_memblockq_set_minreq(s->memblockq, minreq);
-        fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+        s->adjust_latency = adjust_latency;
+        s->early_requests = early_requests;
+        s->buffer_attr = a;
+
+        fix_playback_buffer_attr(s);
+        pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);
 
         reply = reply_new(tag);
 
         reply = reply_new(tag);
-        pa_tagstruct_putu32(reply, maxlength);
-        pa_tagstruct_putu32(reply, tlength);
-        pa_tagstruct_putu32(reply, prebuf);
-        pa_tagstruct_putu32(reply, minreq);
+        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, s->buffer_attr.tlength);
+        pa_tagstruct_putu32(reply, s->buffer_attr.prebuf);
+        pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
 
         if (c->version >= 13)
 
         if (c->version >= 13)
-            pa_tagstruct_put_usec(reply, s->sink_latency);
+            pa_tagstruct_put_usec(reply, s->configured_sink_latency);
 
     } else {
         record_stream *s;
 
     } else {
         record_stream *s;
@@ -3362,8 +3657,8 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
 
         if (pa_tagstruct_get(
                     t,
 
         if (pa_tagstruct_get(
                     t,
-                    PA_TAG_U32, &maxlength,
-                    PA_TAG_U32, &fragsize,
+                    PA_TAG_U32, &a.maxlength,
+                    PA_TAG_U32, &a.fragsize,
                     PA_TAG_INVALID) < 0 ||
             (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
             (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
                     PA_TAG_INVALID) < 0 ||
             (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
             (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
@@ -3372,16 +3667,21 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
             return;
         }
 
             return;
         }
 
-        fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize);
-        pa_memblockq_set_maxlength(s->memblockq, maxlength);
-        fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+        s->adjust_latency = adjust_latency;
+        s->early_requests = early_requests;
+        s->buffer_attr = a;
+
+        fix_record_buffer_attr_pre(s);
+        pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
+        pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+        fix_record_buffer_attr_post(s);
 
         reply = reply_new(tag);
 
         reply = reply_new(tag);
-        pa_tagstruct_putu32(reply, maxlength);
-        pa_tagstruct_putu32(reply, fragsize);
+        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
 
         if (c->version >= 13)
 
         if (c->version >= 13)
-            pa_tagstruct_put_usec(reply, s->source_latency);
+            pa_tagstruct_put_usec(reply, s->configured_source_latency);
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
@@ -3462,34 +3762,38 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
         }
     }
 
         }
     }
 
-    CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+    if (!(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE)) {
+        pa_proplist_free(p);
+        CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_INVALID);
+    }
 
     if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
         playback_stream *s;
 
         s = pa_idxset_get_by_index(c->output_streams, idx);
 
     if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
         playback_stream *s;
 
         s = pa_idxset_get_by_index(c->output_streams, idx);
-        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
-        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
-
-        pa_proplist_update(s->sink_input->proplist, mode, p);
-        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+        if (!s || !playback_stream_isinstance(s)) {
+            pa_proplist_free(p);
+            CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_NOENTITY);
+        }
+        pa_sink_input_update_proplist(s->sink_input, mode, p);
 
     } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
         record_stream *s;
 
 
     } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
         record_stream *s;
 
-        s = pa_idxset_get_by_index(c->record_streams, idx);
-        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+            pa_proplist_free(p);
+            CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_NOENTITY);
+        }
+        pa_source_output_update_proplist(s->source_output, mode, p);
 
 
-        pa_proplist_update(s->source_output->proplist, mode, p);
-        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
     } else {
         pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
 
     } else {
         pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
 
-        pa_proplist_update(c->client->proplist, mode, p);
-        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+        pa_client_update_proplist(c->client, mode, p);
     }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
     }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
+    pa_proplist_free(p);
 }
 
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
 }
 
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -3605,7 +3909,23 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
     CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
     CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
 
-    pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
+    if (command == PA_COMMAND_SET_DEFAULT_SOURCE) {
+        pa_source *source;
+
+        source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
+        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+        pa_namereg_set_default_source(c->protocol->core, source);
+    } else {
+        pa_sink *sink;
+        pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK);
+
+        sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+        pa_namereg_set_default_sink(c->protocol->core, sink);
+    }
+
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
@@ -3786,7 +4106,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
 
         CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
 
 
         CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
 
-        if (pa_sink_input_move_to(si, sink) < 0) {
+        if (pa_sink_input_move_to(si, sink, TRUE) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
             return;
         }
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
             return;
         }
@@ -3805,7 +4125,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
 
         CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
 
 
         CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
 
-        if (pa_source_output_move_to(so, source) < 0) {
+        if (pa_source_output_move_to(so, source, TRUE) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
             return;
         }
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
             return;
         }
@@ -3843,7 +4163,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
 
 
             pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
 
-            if (pa_sink_suspend_all(c->protocol->core, b) < 0) {
+            if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
@@ -3857,7 +4177,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
 
             CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
-            if (pa_sink_suspend(sink, b) < 0) {
+            if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
@@ -3870,7 +4190,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
 
 
             pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
 
-            if (pa_source_suspend_all(c->protocol->core, b) < 0) {
+            if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
@@ -3885,7 +4205,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 
             CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 
 
             CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 
-            if (pa_source_suspend(source, b) < 0) {
+            if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
                 pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
                 return;
             }
@@ -3928,13 +4248,109 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
     CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
 
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
     CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
 
-    cb = (pa_native_protocol_ext_cb_t) pa_hashmap_get(c->protocol->extensions, m);
-    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
+    cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
+    CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
 
     if (cb(c->protocol, m, c, tag, t) < 0)
         protocol_error(c);
 }
 
 
     if (cb(c->protocol, m, c, tag, t) < 0)
         protocol_error(c);
 }
 
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL, *profile = NULL;
+    pa_card *card = NULL;
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &profile) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+    if (idx != PA_INVALID_INDEX)
+        card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+    else
+        card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
+
+    CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
+
+    if ((ret = pa_card_set_profile(card, profile, TRUE)) < 0) {
+        pa_pstream_send_error(c->pstream, tag, -ret);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL, *port = NULL;
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &port) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+    if (command == PA_COMMAND_SET_SINK_PORT) {
+        pa_sink *sink;
+
+        if (idx != PA_INVALID_INDEX)
+            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+        else
+            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+        if ((ret = pa_sink_set_port(sink, port, TRUE)) < 0) {
+            pa_pstream_send_error(c->pstream, tag, -ret);
+            return;
+        }
+    } else {
+        pa_source *source;
+
+        pa_assert(command = PA_COMMAND_SET_SOURCE_PORT);
+
+        if (idx != PA_INVALID_INDEX)
+            source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+        else
+            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+        if ((ret = pa_source_set_port(source, port, TRUE)) < 0) {
+            pa_pstream_send_error(c->pstream, tag, -ret);
+            return;
+        }
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
 
 /*** pstream callbacks ***/
 
 
 /*** pstream callbacks ***/
 
@@ -3960,7 +4376,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
     pa_native_connection_assert_ref(c);
 
     if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
     pa_native_connection_assert_ref(c);
 
     if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
-        pa_log("client sent block for invalid stream.");
+        pa_log_debug("Client sent block for invalid stream.");
         /* Ignoring */
         return;
     }
         /* Ignoring */
         return;
     }
@@ -3970,17 +4386,20 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
     if (playback_stream_isinstance(stream)) {
         playback_stream *ps = PLAYBACK_STREAM(stream);
 
     if (playback_stream_isinstance(stream)) {
         playback_stream *ps = PLAYBACK_STREAM(stream);
 
-        if (seek != PA_SEEK_RELATIVE || offset != 0)
-            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
+        if (chunk->memblock) {
+            if (seek != PA_SEEK_RELATIVE || offset != 0)
+                pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL);
 
 
-        pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
+        } else
+            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset+chunk->length, NULL, NULL);
 
     } else {
         upload_stream *u = UPLOAD_STREAM(stream);
         size_t l;
 
         if (!u->memchunk.memblock) {
 
     } else {
         upload_stream *u = UPLOAD_STREAM(stream);
         size_t l;
 
         if (!u->memchunk.memblock) {
-            if (u->length == chunk->length) {
+            if (u->length == chunk->length && chunk->memblock) {
                 u->memchunk = *chunk;
                 pa_memblock_ref(u->memchunk.memblock);
                 u->length = 0;
                 u->memchunk = *chunk;
                 pa_memblock_ref(u->memchunk.memblock);
                 u->length = 0;
@@ -3996,17 +4415,22 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
         if (l > chunk->length)
             l = chunk->length;
 
         if (l > chunk->length)
             l = chunk->length;
 
-
         if (l > 0) {
         if (l > 0) {
-            void *src, *dst;
+            void *dst;
             dst = pa_memblock_acquire(u->memchunk.memblock);
             dst = pa_memblock_acquire(u->memchunk.memblock);
-            src = pa_memblock_acquire(chunk->memblock);
 
 
-            memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
-                   (uint8_t*) src+chunk->index, l);
+            if (chunk->memblock) {
+                void *src;
+                src = pa_memblock_acquire(chunk->memblock);
+
+                memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
+                       (uint8_t*) src + chunk->index, l);
+
+                pa_memblock_release(chunk->memblock);
+            } else
+                pa_silence_memory((uint8_t*) dst + u->memchunk.index + u->memchunk.length, l, &u->sample_spec);
 
             pa_memblock_release(u->memchunk.memblock);
 
             pa_memblock_release(u->memchunk.memblock);
-            pa_memblock_release(chunk->memblock);
 
             u->memchunk.length += l;
             u->length -= l;
 
             u->memchunk.length += l;
             u->length -= l;
@@ -4060,13 +4484,31 @@ static void client_kill_cb(pa_client *c) {
     pa_log_info("Connection killed.");
 }
 
     pa_log_info("Connection killed.");
 }
 
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+    pa_tagstruct *t;
+    pa_native_connection *c;
+
+    pa_assert(client);
+    c = PA_NATIVE_CONNECTION(client->userdata);
+    pa_native_connection_assert_ref(c);
+
+    if (c->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
 /*** module entry points ***/
 
 /*** module 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) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
     pa_assert(m);
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
     pa_assert(m);
-    pa_assert(tv);
     pa_native_connection_assert_ref(c);
     pa_assert(c->auth_timeout_event == e);
 
     pa_native_connection_assert_ref(c);
     pa_assert(c->auth_timeout_event == e);
 
@@ -4124,12 +4566,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
         c->authorized = TRUE;
     }
 
         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->is_local = pa_iochannel_socket_is_local(io);
         c->auth_timeout_event = NULL;
 
     c->is_local = pa_iochannel_socket_is_local(io);
@@ -4137,6 +4576,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
 
     c->client = client;
     c->client->kill = client_kill_cb;
 
     c->client = client;
     c->client->kill = client_kill_cb;
+    c->client->send_event = client_send_event_cb;
     c->client->userdata = c;
 
     c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
     c->client->userdata = c;
 
     c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
@@ -4147,7 +4587,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
     pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
     pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
 
     pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
     pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
 
-    c->pdispatch = pa_pdispatch_new(p->core->mainloop, command_table, PA_COMMAND_MAX);
+    c->pdispatch = pa_pdispatch_new(p->core->mainloop, TRUE, command_table, PA_COMMAND_MAX);
 
     c->record_streams = pa_idxset_new(NULL, NULL);
     c->output_streams = pa_idxset_new(NULL, NULL);
 
     c->record_streams = pa_idxset_new(NULL, NULL);
     c->output_streams = pa_idxset_new(NULL, NULL);
@@ -4286,7 +4726,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ
     pa_assert(cb);
     pa_assert(!pa_hashmap_get(p->extensions, m));
 
     pa_assert(cb);
     pa_assert(!pa_hashmap_get(p->extensions, m));
 
-    pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) cb) == 0);
+    pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) (unsigned long) cb) == 0);
     return 0;
 }
 
     return 0;
 }