]> code.delx.au - pulseaudio/blobdiff - src/pulse/stream.c
Remove pa_bool_t and replace it with bool.
[pulseaudio] / src / pulse / stream.c
index 0f9e50abcd4f74ee863f828815da1e9c39317e43..746179b44ef0e0297180d7a855f17044736a2018 100644 (file)
 #include <pulse/timeval.h>
 #include <pulse/rtclock.h>
 #include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
 
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-rtclock.h>
+#include <pulsecore/core-util.h>
 
-#include "fork-detect.h"
 #include "internal.h"
+#include "stream.h"
+
+/* #define STREAM_DEBUG */
 
 #define AUTO_TIMING_INTERVAL_START_USEC (10*PA_USEC_PER_MSEC)
 #define AUTO_TIMING_INTERVAL_END_USEC (1500*PA_USEC_PER_MSEC)
@@ -78,31 +82,26 @@ static void reset_callbacks(pa_stream *s) {
     s->buffer_attr_userdata = NULL;
 }
 
-pa_stream *pa_stream_new_with_proplist(
+static pa_stream *pa_stream_new_with_proplist_internal(
         pa_context *c,
         const char *name,
         const pa_sample_spec *ss,
         const pa_channel_map *map,
+        pa_format_info * const *formats,
+        unsigned int n_formats,
         pa_proplist *p) {
 
     pa_stream *s;
-    int i;
-    pa_channel_map tmap;
+    unsigned int i;
 
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert((ss == NULL && map == NULL) || (formats == NULL && n_formats == 0));
+    pa_assert(n_formats < PA_MAX_FORMATS);
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
     PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
 
-    if (!map)
-        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
-
     s = pa_xnew(pa_stream, 1);
     PA_REFCNT_INIT(s);
     s->context = c;
@@ -112,8 +111,25 @@ pa_stream *pa_stream_new_with_proplist(
     s->state = PA_STREAM_UNCONNECTED;
     s->flags = 0;
 
-    s->sample_spec = *ss;
-    s->channel_map = *map;
+    if (ss)
+        s->sample_spec = *ss;
+    else
+        pa_sample_spec_init(&s->sample_spec);
+
+    if (map)
+        s->channel_map = *map;
+    else
+        pa_channel_map_init(&s->channel_map);
+
+    s->n_formats = 0;
+    if (formats) {
+        s->n_formats = n_formats;
+        for (i = 0; i < n_formats; i++)
+            s->req_formats[i] = pa_format_info_copy(formats[i]);
+    }
+
+    /* We'll get the final negotiated format after connecting */
+    s->format = NULL;
 
     s->direct_on_input = PA_INVALID_INDEX;
 
@@ -122,27 +138,38 @@ pa_stream *pa_stream_new_with_proplist(
         pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name);
 
     s->channel = 0;
-    s->channel_valid = FALSE;
+    s->channel_valid = false;
     s->syncid = c->csyncid++;
     s->stream_index = PA_INVALID_INDEX;
 
     s->requested_bytes = 0;
     memset(&s->buffer_attr, 0, sizeof(s->buffer_attr));
 
-    /* We initialize der target length here, so that if the user
+    /* We initialize the target length here, so that if the user
      * passes no explicit buffering metrics the default is similar to
      * what older PA versions provided. */
 
     s->buffer_attr.maxlength = (uint32_t) -1;
-    s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
+    if (ss)
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
+    else {
+        /* FIXME: We assume a worst-case compressed format corresponding to
+         * 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */
+        pa_sample_spec tmp_ss = {
+            .format   = PA_SAMPLE_S16NE,
+            .rate     = 48000,
+            .channels = 2,
+        };
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */
+    }
     s->buffer_attr.minreq = (uint32_t) -1;
     s->buffer_attr.prebuf = (uint32_t) -1;
     s->buffer_attr.fragsize = (uint32_t) -1;
 
     s->device_index = PA_INVALID_INDEX;
     s->device_name = NULL;
-    s->suspended = FALSE;
-    s->corked = FALSE;
+    s->suspended = false;
+    s->corked = false;
 
     s->write_memblock = NULL;
     s->write_data = NULL;
@@ -152,9 +179,10 @@ pa_stream *pa_stream_new_with_proplist(
     s->record_memblockq = NULL;
 
     memset(&s->timing_info, 0, sizeof(s->timing_info));
-    s->timing_info_valid = FALSE;
+    s->timing_info_valid = false;
 
     s->previous_time = 0;
+    s->latest_underrun_at_index = -1;
 
     s->read_index_not_before = 0;
     s->write_index_not_before = 0;
@@ -163,7 +191,7 @@ pa_stream *pa_stream_new_with_proplist(
     s->current_write_index_correction = 0;
 
     s->auto_timing_update_event = NULL;
-    s->auto_timing_update_requested = FALSE;
+    s->auto_timing_update_requested = false;
     s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
 
     reset_callbacks(s);
@@ -177,6 +205,39 @@ pa_stream *pa_stream_new_with_proplist(
     return s;
 }
 
+pa_stream *pa_stream_new_with_proplist(
+        pa_context *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_proplist *p) {
+
+    pa_channel_map tmap;
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+
+    if (!map)
+        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+    return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, 0, p);
+}
+
+pa_stream *pa_stream_new_extended(
+        pa_context *c,
+        const char *name,
+        pa_format_info * const *formats,
+        unsigned int n_formats,
+        pa_proplist *p) {
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
+
+    return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, n_formats, p);
+}
+
 static void stream_unlink(pa_stream *s) {
     pa_operation *o, *n;
     pa_assert(s);
@@ -186,7 +247,7 @@ static void stream_unlink(pa_stream *s) {
 
     /* Detach from context */
 
-    /* Unref all operatio object that point to us */
+    /* Unref all operation objects that point to us */
     for (o = s->context->operations; o; o = n) {
         n = o->next;
 
@@ -199,9 +260,9 @@ static void stream_unlink(pa_stream *s) {
         pa_pdispatch_unregister_reply(s->context->pdispatch, s);
 
     if (s->channel_valid) {
-        pa_hashmap_remove((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, PA_UINT32_TO_PTR(s->channel));
+        pa_hashmap_remove((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, PA_UINT32_TO_PTR(s->channel));
         s->channel = 0;
-        s->channel_valid = FALSE;
+        s->channel_valid = false;
     }
 
     PA_LLIST_REMOVE(pa_stream, s->context->streams, s);
@@ -218,13 +279,16 @@ static void stream_unlink(pa_stream *s) {
 }
 
 static void stream_free(pa_stream *s) {
+    unsigned int i;
+
     pa_assert(s);
 
     stream_unlink(s);
 
     if (s->write_memblock) {
-        pa_memblock_release(s->write_memblock);
-        pa_memblock_unref(s->write_data);
+        if (s->write_data)
+            pa_memblock_release(s->write_memblock);
+        pa_memblock_unref(s->write_memblock);
     }
 
     if (s->peek_memchunk.memblock) {
@@ -242,6 +306,12 @@ static void stream_free(pa_stream *s) {
     if (s->smoother)
         pa_smoother_free(s->smoother);
 
+    for (i = 0; i < s->n_formats; i++)
+        pa_format_info_free(s->req_formats[i]);
+
+    if (s->format)
+        pa_format_info_free(s->format);
+
     pa_xfree(s->device_name);
     pa_xfree(s);
 }
@@ -306,7 +376,7 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) {
     pa_stream_unref(s);
 }
 
-static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
+static void request_auto_timing_update(pa_stream *s, bool force) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
@@ -317,21 +387,29 @@ static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
         (force || !s->auto_timing_update_requested)) {
         pa_operation *o;
 
-/*         pa_log("Automatically requesting new timing data"); */
+#ifdef STREAM_DEBUG
+        pa_log_debug("Automatically requesting new timing data");
+#endif
 
         if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
             pa_operation_unref(o);
-            s->auto_timing_update_requested = TRUE;
+            s->auto_timing_update_requested = true;
         }
     }
 
     if (s->auto_timing_update_event) {
-        if (force)
-            s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        if (s->suspended && !force) {
+            pa_assert(s->mainloop);
+            s->mainloop->time_free(s->auto_timing_update_event);
+            s->auto_timing_update_event = NULL;
+        } else {
+            if (force)
+                s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
 
-        pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec);
+            pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec);
 
-        s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2);
+            s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2);
+        }
     }
 }
 
@@ -367,7 +445,7 @@ finish:
     pa_context_unref(c);
 }
 
-static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t force_start, pa_bool_t force_stop) {
+static void check_smoother_status(pa_stream *s, bool aposteriori, bool force_start, bool force_stop) {
     pa_usec_t x;
 
     pa_assert(s);
@@ -399,25 +477,27 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t
              * indications when audio really starts/stops playing, if
              * we don't have any timing info yet -- instead of trying
              * to be smart and guessing the server time. Otherwise the
-             * unknown transport delay add too much noise to our time
+             * unknown transport delay adds too much noise to our time
              * calculations. */
 
             return;
         }
 
-        pa_smoother_resume(s->smoother, x, TRUE);
+        pa_smoother_resume(s->smoother, x, true);
     }
 
     /* Please note that we have no idea if playback actually started
      * if prebuf is non-zero! */
 }
 
+static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata);
+
 void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_context *c = userdata;
     pa_stream *s;
     uint32_t channel;
     const char *dn;
-    pa_bool_t suspended;
+    bool suspended;
     uint32_t di;
     pa_usec_t usec = 0;
     uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
@@ -499,8 +579,14 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
 
     s->suspended = suspended;
 
-    check_smoother_status(s, TRUE, FALSE, FALSE);
-    request_auto_timing_update(s, TRUE);
+    if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+        request_auto_timing_update(s, true);
+    }
+
+    check_smoother_status(s, true, false, false);
+    request_auto_timing_update(s, true);
 
     if (s->moved_callback)
         s->moved_callback(s, s->moved_userdata);
@@ -574,7 +660,7 @@ void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t
     s->buffer_attr.prebuf = prebuf;
     s->buffer_attr.minreq = minreq;
 
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     if (s->buffer_attr_callback)
         s->buffer_attr_callback(s, s->buffer_attr_userdata);
@@ -587,7 +673,7 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t ta
     pa_context *c = userdata;
     pa_stream *s;
     uint32_t channel;
-    pa_bool_t suspended;
+    bool suspended;
 
     pa_assert(pd);
     pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED);
@@ -617,8 +703,14 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t ta
 
     s->suspended = suspended;
 
-    check_smoother_status(s, TRUE, FALSE, FALSE);
-    request_auto_timing_update(s, TRUE);
+    if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+        request_auto_timing_update(s, true);
+    }
+
+    check_smoother_status(s, true, false, false);
+    request_auto_timing_update(s, true);
 
     if (s->suspended_callback)
         s->suspended_callback(s, s->suspended_userdata);
@@ -657,8 +749,8 @@ void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     if (s->state != PA_STREAM_READY)
         goto finish;
 
-    check_smoother_status(s, TRUE, TRUE, FALSE);
-    request_auto_timing_update(s, TRUE);
+    check_smoother_status(s, true, true, false);
+    request_auto_timing_update(s, true);
 
     if (s->started_callback)
         s->started_callback(s, s->started_userdata);
@@ -703,6 +795,13 @@ void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     if (s->state != PA_STREAM_READY)
         goto finish;
 
+    if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) {
+        /* Let client know what the running time was when the stream had to be killed  */
+        pa_usec_t stream_time;
+        if (pa_stream_get_time(s, &stream_time) == 0)
+            pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) stream_time);
+    }
+
     if (s->event_callback)
         s->event_callback(s, event, pl, s->event_userdata);
 
@@ -741,7 +840,9 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tag
 
     s->requested_bytes += bytes;
 
-    /* pa_log("got request for %lli, now at %lli", (long long) bytes, (long long) s->requested_bytes); */
+#ifdef STREAM_DEBUG
+    pa_log_debug("got request for %lli, now at %lli", (long long) bytes, (long long) s->requested_bytes);
+#endif
 
     if (s->requested_bytes > 0 && s->write_callback)
         s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
@@ -750,10 +851,16 @@ finish:
     pa_context_unref(c);
 }
 
+int64_t pa_stream_get_underflow_index(pa_stream *p) {
+    pa_assert(p);
+    return p->latest_underrun_at_index;
+}
+
 void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_stream *s;
     pa_context *c = userdata;
     uint32_t channel;
+    int64_t offset = -1;
 
     pa_assert(pd);
     pa_assert(command == PA_COMMAND_OVERFLOW || command == PA_COMMAND_UNDERFLOW);
@@ -763,8 +870,19 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
 
     pa_context_ref(c);
 
-    if (pa_tagstruct_getu32(t, &channel) < 0 ||
-        !pa_tagstruct_eof(t)) {
+    if (pa_tagstruct_getu32(t, &channel) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->version >= 23 && command == PA_COMMAND_UNDERFLOW) {
+        if (pa_tagstruct_gets64(t, &offset) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
         pa_context_fail(c, PA_ERR_PROTOCOL);
         goto finish;
     }
@@ -775,10 +893,13 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
     if (s->state != PA_STREAM_READY)
         goto finish;
 
+    if (offset != -1)
+        s->latest_underrun_at_index = offset;
+
     if (s->buffer_attr.prebuf > 0)
-        check_smoother_status(s, TRUE, FALSE, TRUE);
+        check_smoother_status(s, true, false, true);
 
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     if (command == PA_COMMAND_OVERFLOW) {
         if (s->overflow_callback)
@@ -788,15 +909,17 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
             s->underflow_callback(s, s->underflow_userdata);
     }
 
- finish:
+finish:
     pa_context_unref(c);
 }
 
-static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
+static void invalidate_indexes(pa_stream *s, bool r, bool w) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
-/*     pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); */
+#ifdef STREAM_DEBUG
+    pa_log_debug("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag);
+#endif
 
     if (s->state != PA_STREAM_READY)
         return;
@@ -805,21 +928,25 @@ static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) {
         s->write_index_not_before = s->context->ctag;
 
         if (s->timing_info_valid)
-            s->timing_info.write_index_corrupt = TRUE;
+            s->timing_info.write_index_corrupt = true;
 
-/*         pa_log("write_index invalidated"); */
+#ifdef STREAM_DEBUG
+        pa_log_debug("write_index invalidated");
+#endif
     }
 
     if (r) {
         s->read_index_not_before = s->context->ctag;
 
         if (s->timing_info_valid)
-            s->timing_info.read_index_corrupt = TRUE;
+            s->timing_info.read_index_corrupt = true;
 
-/*         pa_log("read_index invalidated"); */
+#ifdef STREAM_DEBUG
+        pa_log_debug("read_index invalidated");
+#endif
     }
 
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 }
 
 static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
@@ -829,7 +956,7 @@ static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, co
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
 
     pa_stream_ref(s);
-    request_auto_timing_update(s, FALSE);
+    request_auto_timing_update(s, false);
     pa_stream_unref(s);
 }
 
@@ -848,16 +975,34 @@ static void create_stream_complete(pa_stream *s) {
         pa_assert(!s->auto_timing_update_event);
         s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
 
-        request_auto_timing_update(s, TRUE);
+        request_auto_timing_update(s, true);
     }
 
-    check_smoother_status(s, TRUE, FALSE, FALSE);
+    check_smoother_status(s, true, false, false);
 }
 
-static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_sample_spec *ss) {
+static void patch_buffer_attr(pa_stream *s, pa_buffer_attr *attr, pa_stream_flags_t *flags) {
+    const char *e;
+
     pa_assert(s);
     pa_assert(attr);
-    pa_assert(ss);
+
+    if ((e = getenv("PULSE_LATENCY_MSEC"))) {
+        uint32_t ms;
+
+        if (pa_atou(e, &ms) < 0 || ms <= 0)
+            pa_log_debug("Failed to parse $PULSE_LATENCY_MSEC: %s", e);
+        else {
+            attr->maxlength = (uint32_t) -1;
+            attr->tlength = pa_usec_to_bytes(ms * PA_USEC_PER_MSEC, &s->sample_spec);
+            attr->minreq = (uint32_t) -1;
+            attr->prebuf = (uint32_t) -1;
+            attr->fragsize = attr->tlength;
+        }
+
+        if (flags)
+            *flags |= PA_STREAM_ADJUST_LATENCY;
+    }
 
     if (s->context->version >= 13)
         return;
@@ -872,7 +1017,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s
         attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */
 
     if (attr->tlength == (uint32_t) -1)
-        attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
+        attr->tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &s->sample_spec); /* 250ms of buffering */
 
     if (attr->minreq == (uint32_t) -1)
         attr->minreq = (attr->tlength)/5; /* Ask for more data when there are only 200ms left in the playback buffer */
@@ -896,7 +1041,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     pa_stream_ref(s);
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
+        if (pa_context_handle_error(s->context, command, t, false) < 0)
             goto finish;
 
         pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -905,7 +1050,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
 
     if (pa_tagstruct_getu32(t, &s->channel) < 0 ||
         s->channel == PA_INVALID_INDEX ||
-        ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 ||  s->stream_index == PA_INVALID_INDEX)) ||
+        ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) ||
         ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &requested_bytes) < 0)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
@@ -935,7 +1080,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
         pa_sample_spec ss;
         pa_channel_map cm;
         const char *dn = NULL;
-        pa_bool_t suspended;
+        bool suspended;
 
         if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
             pa_tagstruct_get_channel_map(t, &cm) < 0 ||
@@ -950,9 +1095,10 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
             ss.channels != cm.channels ||
             !pa_channel_map_valid(&cm) ||
             !pa_sample_spec_valid(&ss) ||
-            (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
-            (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
-            (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
+            (s->n_formats == 0 && (
+                (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
+                (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
+                (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) {
             pa_context_fail(s->context, PA_ERR_PROTOCOL);
             goto finish;
         }
@@ -979,6 +1125,24 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
             s->timing_info.configured_sink_usec = usec;
     }
 
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
+
+        pa_format_info *f = pa_format_info_new();
+        pa_tagstruct_get_format_info(t, f);
+
+        if (pa_format_info_valid(f))
+            s->format = f;
+        else {
+            pa_format_info_free(f);
+            if (s->n_formats > 0) {
+                /* We used the extended API, so we should have got back a proper format */
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
@@ -988,17 +1152,18 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
         pa_assert(!s->record_memblockq);
 
         s->record_memblockq = pa_memblockq_new(
+                "client side record memblockq",
                 0,
                 s->buffer_attr.maxlength,
                 0,
-                pa_frame_size(&s->sample_spec),
+                &s->sample_spec,
                 1,
                 0,
                 0,
                 NULL);
     }
 
-    s->channel_valid = TRUE;
+    s->channel_valid = true;
     pa_hashmap_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, PA_UINT32_TO_PTR(s->channel), s);
 
     create_stream_complete(s);
@@ -1018,7 +1183,9 @@ static int create_stream(
 
     pa_tagstruct *t;
     uint32_t tag;
-    pa_bool_t volume_set = FALSE;
+    bool volume_set = !!volume;
+    pa_cvolume cv;
+    uint32_t i;
 
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1045,34 +1212,35 @@ static int create_stream(
                                               PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
                                               PA_STREAM_START_UNMUTED|
                                               PA_STREAM_FAIL_ON_SUSPEND|
-                                              PA_STREAM_RELATIVE_VOLUME)), PA_ERR_INVALID);
+                                              PA_STREAM_RELATIVE_VOLUME|
+                                              PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID);
 
     PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY(s->context, s->context->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    /* Althought some of the other flags are not supported on older
+    /* Although some of the other flags are not supported on older
      * version, we don't check for them here, because it doesn't hurt
      * when they are passed but actually not supported. This makes
      * client development easier */
 
-    PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID);
     PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
-    PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, !volume || s->n_formats || (pa_sample_spec_valid(&s->sample_spec) && volume->channels == s->sample_spec.channels), PA_ERR_INVALID);
     PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
     PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID);
 
     pa_stream_ref(s);
 
     s->direction = direction;
-    s->flags = flags;
-    s->corked = !!(flags & PA_STREAM_START_CORKED);
 
     if (sync_stream)
         s->syncid = sync_stream->syncid;
 
     if (attr)
         s->buffer_attr = *attr;
-    automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec);
+    patch_buffer_attr(s, &s->buffer_attr, &flags);
+
+    s->flags = flags;
+    s->corked = !!(flags & PA_STREAM_START_CORKED);
 
     if (flags & PA_STREAM_INTERPOLATE_TIMING) {
         pa_usec_t x;
@@ -1084,10 +1252,10 @@ static int create_stream(
                 SMOOTHER_ADJUST_TIME,
                 SMOOTHER_HISTORY_TIME,
                 !(flags & PA_STREAM_NOT_MONOTONIC),
-                TRUE,
+                true,
                 SMOOTHER_MIN_HISTORY,
                 x,
-                TRUE);
+                true);
     }
 
     if (!dev)
@@ -1111,9 +1279,18 @@ static int create_stream(
             PA_TAG_BOOLEAN, s->corked,
             PA_TAG_INVALID);
 
-    if (s->direction == PA_STREAM_PLAYBACK) {
-        pa_cvolume cv;
+    if (!volume) {
+        if (pa_sample_spec_valid(&s->sample_spec))
+            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+        else {
+            /* This is not really relevant, since no volume was set, and
+             * the real number of channels is embedded in the format_info
+             * structure */
+            volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
+        }
+    }
 
+    if (s->direction == PA_STREAM_PLAYBACK) {
         pa_tagstruct_put(
                 t,
                 PA_TAG_U32, s->buffer_attr.tlength,
@@ -1122,11 +1299,6 @@ static int create_stream(
                 PA_TAG_U32, s->syncid,
                 PA_TAG_INVALID);
 
-        volume_set = !!volume;
-
-        if (!volume)
-            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
-
         pa_tagstruct_put_cvolume(t, volume);
     } else
         pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
@@ -1178,11 +1350,27 @@ static int create_stream(
         pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
     }
 
-    if (s->context->version >= 17) {
+    if (s->context->version >= 17 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
 
-        if (s->direction == PA_STREAM_PLAYBACK)
-            pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+    if (s->context->version >= 18 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
 
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
+
+        pa_tagstruct_putu8(t, s->n_formats);
+        for (i = 0; i < s->n_formats; i++)
+            pa_tagstruct_put_format_info(t, s->req_formats[i]);
+    }
+
+    if (s->context->version >= 22 && s->direction == PA_STREAM_RECORD) {
+        pa_tagstruct_put_cvolume(t, volume);
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+        pa_tagstruct_put_boolean(t, volume_set);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
     }
 
     pa_pstream_send_tagstruct(s->context->pstream, t);
@@ -1364,7 +1552,9 @@ int pa_stream_write(
      * that's OK, the server side applies the same error */
     s->requested_bytes -= (seek == PA_SEEK_RELATIVE ? offset : 0) + (int64_t) length;
 
-    /* pa_log("wrote %lli, now at %lli", (long long) length, (long long) s->requested_bytes); */
+#ifdef STREAM_DEBUG
+    pa_log_debug("wrote %lli, now at %lli", (long long) length, (long long) s->requested_bytes);
+#endif
 
     if (s->direction == PA_STREAM_PLAYBACK) {
 
@@ -1372,31 +1562,31 @@ int pa_stream_write(
         if (s->write_index_corrections[s->current_write_index_correction].valid) {
 
             if (seek == PA_SEEK_ABSOLUTE) {
-                s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE;
-                s->write_index_corrections[s->current_write_index_correction].absolute = TRUE;
+                s->write_index_corrections[s->current_write_index_correction].corrupt = false;
+                s->write_index_corrections[s->current_write_index_correction].absolute = true;
                 s->write_index_corrections[s->current_write_index_correction].value = offset + (int64_t) length;
             } else if (seek == PA_SEEK_RELATIVE) {
                 if (!s->write_index_corrections[s->current_write_index_correction].corrupt)
                     s->write_index_corrections[s->current_write_index_correction].value += offset + (int64_t) length;
             } else
-                s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
+                s->write_index_corrections[s->current_write_index_correction].corrupt = true;
         }
 
         /* Update the write index in the already available latency data */
         if (s->timing_info_valid) {
 
             if (seek == PA_SEEK_ABSOLUTE) {
-                s->timing_info.write_index_corrupt = FALSE;
+                s->timing_info.write_index_corrupt = false;
                 s->timing_info.write_index = offset + (int64_t) length;
             } else if (seek == PA_SEEK_RELATIVE) {
                 if (!s->timing_info.write_index_corrupt)
                     s->timing_info.write_index += offset + (int64_t) length;
             } else
-                s->timing_info.write_index_corrupt = TRUE;
+                s->timing_info.write_index_corrupt = true;
         }
 
         if (!s->timing_info_valid || s->timing_info.write_index_corrupt)
-            request_auto_timing_update(s, TRUE);
+            request_auto_timing_update(s, true);
     }
 
     return 0;
@@ -1415,9 +1605,17 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
     if (!s->peek_memchunk.memblock) {
 
         if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
+            /* record_memblockq is empty. */
             *data = NULL;
             *length = 0;
             return 0;
+
+        } else if (!s->peek_memchunk.memblock) {
+            /* record_memblockq isn't empty, but it doesn't have any data at
+             * the current read index. */
+            *data = NULL;
+            *length = s->peek_memchunk.length;
+            return 0;
         }
 
         s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
@@ -1436,7 +1634,7 @@ int pa_stream_drop(pa_stream *s) {
     PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
     PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY(s->context, s->peek_memchunk.length > 0, PA_ERR_BADSTATE);
 
     pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);
 
@@ -1444,9 +1642,13 @@ int pa_stream_drop(pa_stream *s) {
     if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
         s->timing_info.read_index += (int64_t) s->peek_memchunk.length;
 
-    pa_assert(s->peek_data);
-    pa_memblock_release(s->peek_memchunk.memblock);
-    pa_memblock_unref(s->peek_memchunk.memblock);
+    if (s->peek_memchunk.memblock) {
+        pa_assert(s->peek_data);
+        s->peek_data = NULL;
+        pa_memblock_release(s->peek_memchunk.memblock);
+        pa_memblock_unref(s->peek_memchunk.memblock);
+    }
+
     pa_memchunk_reset(&s->peek_memchunk);
 
     return 0;
@@ -1489,7 +1691,7 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
     /* Ask for a timing update before we cork/uncork to get the best
      * accuracy for the transport latency suitable for the
      * check_smoother_status() call in the started callback */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
 
@@ -1498,14 +1700,14 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
-    /* This might cause the read index to conitnue again, hence
+    /* This might cause the read index to continue again, hence
      * let's request a timing update */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     return o;
 }
 
-static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) {
+static pa_usec_t calc_time(pa_stream *s, bool ignore_transport) {
     pa_usec_t usec;
 
     pa_assert(s);
@@ -1570,7 +1772,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
     pa_operation *o = userdata;
     struct timeval local, remote, now;
     pa_timing_info *i;
-    pa_bool_t playing = FALSE;
+    bool playing = false;
     uint64_t underrun_for = 0, playing_for = 0;
 
     pa_assert(pd);
@@ -1582,12 +1784,12 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
 
     i = &o->stream->timing_info;
 
-    o->stream->timing_info_valid = FALSE;
-    i->write_index_corrupt = TRUE;
-    i->read_index_corrupt = TRUE;
+    o->stream->timing_info_valid = false;
+    i->write_index_corrupt = true;
+    i->read_index_corrupt = true;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
             goto finish;
 
     } else {
@@ -1613,21 +1815,20 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
                 goto finish;
             }
 
-
         if (!pa_tagstruct_eof(t)) {
             pa_context_fail(o->context, PA_ERR_PROTOCOL);
             goto finish;
         }
-        o->stream->timing_info_valid = TRUE;
-        i->write_index_corrupt = FALSE;
-        i->read_index_corrupt = FALSE;
+        o->stream->timing_info_valid = true;
+        i->write_index_corrupt = false;
+        i->read_index_corrupt = false;
 
         i->playing = (int) playing;
         i->since_underrun = (int64_t) (playing ? playing_for : underrun_for);
 
         pa_gettimeofday(&now);
 
-        /* Calculcate timestamps */
+        /* Calculate timestamps */
         if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) {
             /* local and remote seem to have synchronized clocks */
 
@@ -1636,22 +1837,22 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
             else
                 i->transport_usec = pa_timeval_diff(&now, &remote);
 
-            i->synchronized_clocks = TRUE;
+            i->synchronized_clocks = true;
             i->timestamp = remote;
         } else {
             /* clocks are not synchronized, let's estimate latency then */
             i->transport_usec = pa_timeval_diff(&now, &local)/2;
-            i->synchronized_clocks = FALSE;
+            i->synchronized_clocks = false;
             i->timestamp = local;
             pa_timeval_add(&i->timestamp, i->transport_usec);
         }
 
         /* Invalidate read and write indexes if necessary */
         if (tag < o->stream->read_index_not_before)
-            i->read_index_corrupt = TRUE;
+            i->read_index_corrupt = true;
 
         if (tag < o->stream->write_index_not_before)
-            i->write_index_corrupt = TRUE;
+            i->write_index_corrupt = true;
 
         if (o->stream->direction == PA_STREAM_PLAYBACK) {
             /* Write index correction */
@@ -1676,11 +1877,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
                 /* Now fix the write index */
                 if (o->stream->write_index_corrections[j].corrupt) {
                     /* A corrupting seek was made */
-                    i->write_index_corrupt = TRUE;
+                    i->write_index_corrupt = true;
                 } else if (o->stream->write_index_corrections[j].absolute) {
                     /* An absolute seek was made */
                     i->write_index = o->stream->write_index_corrections[j].value;
-                    i->write_index_corrupt = FALSE;
+                    i->write_index_corrupt = false;
                 } else if (!i->write_index_corrupt) {
                     /* A relative seek was made */
                     i->write_index += o->stream->write_index_corrections[j].value;
@@ -1693,7 +1894,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
                     continue;
 
                 if (o->stream->write_index_corrections[n].tag <= tag)
-                    o->stream->write_index_corrections[n].valid = FALSE;
+                    o->stream->write_index_corrections[n].valid = false;
             }
         }
 
@@ -1704,8 +1905,8 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
                 i->read_index -= (int64_t) pa_memblockq_get_length(o->stream->record_memblockq);
         }
 
-        /* Update smoother */
-        if (o->stream->smoother) {
+        /* Update smoother if we're not corked */
+        if (o->stream->smoother && !o->stream->corked) {
             pa_usec_t u, x;
 
             u = x = pa_rtclock_now() - i->transport_usec;
@@ -1730,14 +1931,14 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
             /* Update the smoother */
             if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) ||
                 (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt))
-                pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
+                pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, true));
 
             if (i->playing)
-                pa_smoother_resume(o->stream->smoother, x, TRUE);
+                pa_smoother_resume(o->stream->smoother, x, true);
         }
     }
 
-    o->stream->auto_timing_update_requested = FALSE;
+    o->stream->auto_timing_update_requested = false;
 
     if (o->stream->latency_update_callback)
         o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata);
@@ -1791,9 +1992,9 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t
 
         s->current_write_index_correction = cidx;
 
-        s->write_index_corrections[cidx].valid = TRUE;
-        s->write_index_corrections[cidx].absolute = FALSE;
-        s->write_index_corrections[cidx].corrupt = FALSE;
+        s->write_index_corrections[cidx].valid = true;
+        s->write_index_corrections[cidx].absolute = false;
+        s->write_index_corrections[cidx].corrupt = false;
         s->write_index_corrections[cidx].tag = tag;
         s->write_index_corrections[cidx].value = 0;
     }
@@ -1811,7 +2012,7 @@ void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_stream_ref(s);
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(s->context, command, t, FALSE) < 0)
+        if (pa_context_handle_error(s->context, command, t, false) < 0)
             goto finish;
 
         pa_stream_set_state(s, PA_STREAM_FAILED);
@@ -2019,7 +2220,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
             goto finish;
 
         success = 0;
@@ -2053,7 +2254,7 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
     /* Ask for a timing update before we cork/uncork to get the best
      * accuracy for the transport latency suitable for the
      * check_smoother_status() call in the started callback */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     s->corked = b;
 
@@ -2068,11 +2269,11 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
-    check_smoother_status(s, FALSE, FALSE, FALSE);
+    check_smoother_status(s, false, false, false);
 
     /* This might cause the indexes to hang/start again, hence let's
      * request a timing update, after the cork/uncork, too */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     return o;
 }
@@ -2111,7 +2312,7 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use
     /* Ask for a timing update *before* the flush, so that the
      * transport usec is as up to date as possible when we get the
      * underflow message and update the smoother status*/
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     if (!(o = stream_send_simple_command(s, (uint32_t) (s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM), cb, userdata)))
         return NULL;
@@ -2119,19 +2320,24 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use
     if (s->direction == PA_STREAM_PLAYBACK) {
 
         if (s->write_index_corrections[s->current_write_index_correction].valid)
-            s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE;
+            s->write_index_corrections[s->current_write_index_correction].corrupt = true;
 
         if (s->buffer_attr.prebuf > 0)
-            check_smoother_status(s, FALSE, FALSE, TRUE);
+            check_smoother_status(s, false, false, true);
 
         /* This will change the write index, but leave the
          * read index untouched. */
-        invalidate_indexes(s, FALSE, TRUE);
+        invalidate_indexes(s, false, true);
 
     } else
         /* For record streams this has no influence on the write
          * index, but the read index might jump. */
-        invalidate_indexes(s, TRUE, FALSE);
+        invalidate_indexes(s, true, false);
+
+    /* Note that we do not update requested_bytes here. This is
+     * because we cannot really know how data actually was dropped
+     * from the write index due to this. This 'error' will be applied
+     * by both client and server and hence we should be fine. */
 
     return o;
 }
@@ -2150,14 +2356,14 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
     /* Ask for a timing update before we cork/uncork to get the best
      * accuracy for the transport latency suitable for the
      * check_smoother_status() call in the started callback */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     if (!(o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata)))
         return NULL;
 
     /* This might cause the read index to hang again, hence
      * let's request a timing update */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     return o;
 }
@@ -2176,14 +2382,14 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u
     /* Ask for a timing update before we cork/uncork to get the best
      * accuracy for the transport latency suitable for the
      * check_smoother_status() call in the started callback */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     if (!(o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata)))
         return NULL;
 
     /* This might cause the read index to start moving again, hence
      * let's request a timing update */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     return o;
 }
@@ -2239,7 +2445,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
     if (s->smoother)
         usec = pa_smoother_get(s->smoother, pa_rtclock_now());
     else
-        usec = calc_time(s, FALSE);
+        usec = calc_time(s, false);
 
     /* Make sure the time runs monotonically */
     if (!(s->flags & PA_STREAM_NOT_MONOTONIC)) {
@@ -2340,6 +2546,16 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
     return &s->channel_map;
 }
 
+const pa_format_info* pa_stream_get_format_info(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    /* We don't have the format till routing is done */
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+
+    return s->format;
+}
 const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -2363,7 +2579,7 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command,
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
             goto finish;
 
         success = 0;
@@ -2414,11 +2630,11 @@ finish:
     pa_operation_unref(o);
 }
 
-
 pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr, pa_stream_success_cb_t cb, void *userdata) {
     pa_operation *o;
     pa_tagstruct *t;
     uint32_t tag;
+    pa_buffer_attr copy;
 
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -2432,7 +2648,7 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr
     /* Ask for a timing update before we cork/uncork to get the best
      * accuracy for the transport latency suitable for the
      * check_smoother_status() call in the started callback */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
 
@@ -2442,6 +2658,10 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr
             &tag);
     pa_tagstruct_putu32(t, s->channel);
 
+    copy = *attr;
+    patch_buffer_attr(s, &copy, NULL);
+    attr = &copy;
+
     pa_tagstruct_putu32(t, attr->maxlength);
 
     if (s->direction == PA_STREAM_PLAYBACK)
@@ -2463,9 +2683,9 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
-    /* This might cause changes in the read/write indexex, hence let's
+    /* This might cause changes in the read/write index, hence let's
      * request a timing update */
-    request_auto_timing_update(s, TRUE);
+    request_auto_timing_update(s, true);
 
     return o;
 }
@@ -2531,7 +2751,7 @@ static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t comman
         goto finish;
 
     if (command != PA_COMMAND_REPLY) {
-        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+        if (pa_context_handle_error(o->context, command, t, false) < 0)
             goto finish;
 
         success = 0;
@@ -2556,7 +2776,6 @@ finish:
     pa_operation_unref(o);
 }
 
-
 pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata) {
     pa_operation *o;
     pa_tagstruct *t;