[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.15-test5-91-g061344f

Lennart Poettering gitmailer-noreply at 0pointer.de
Mon Mar 30 10:14:38 PDT 2009


This is an automated email from the git hooks/post-receive script. It was
generated because of a push to the "PulseAudio Sound Server" repository.

The master branch has been updated
      from  87d63b19a3f35650536c3595cf89145d61a052ca (commit)

- Log -----------------------------------------------------------------
061344f Merge commit 'origin/master'
aa68036 rework tunneling code
65b787d notify clients about tlength changes
491aafd typo fix
b349dae add pa_memblockq_apply_attr()/pa_memblockq_get_attr()
7c37c37 document more often the context certain functions are called in
7f5481e simplify latency config functions a bit and make them callable in more contexts
d04f3e1 Trigger move callback a little bit earlier so that no IO thread is running
e3f1510 don't enabled tsched on software ALSA devices
-----------------------------------------------------------------------

Summary of changes:
 PROTOCOL                        |   13 ++
 src/modules/alsa/alsa-sink.c    |    5 +
 src/modules/alsa/alsa-source.c  |    5 +
 src/modules/alsa/alsa-util.c    |   15 ++-
 src/modules/alsa/alsa-util.h    |    2 +
 src/modules/module-tunnel.c     |  187 ++++++++++++-----
 src/pulse/context.c             |    4 +-
 src/pulse/def.h                 |    2 +-
 src/pulse/internal.h            |    3 +
 src/pulse/stream.c              |   92 ++++++++-
 src/pulse/stream.h              |    9 +-
 src/pulsecore/memblockq.c       |   20 ++
 src/pulsecore/memblockq.h       |    5 +-
 src/pulsecore/native-common.h   |    6 +-
 src/pulsecore/protocol-native.c |  441 ++++++++++++++++++++------------------
 src/pulsecore/sink-input.c      |   64 +++---
 src/pulsecore/sink-input.h      |    2 +-
 src/pulsecore/source-output.c   |   64 +++---
 src/pulsecore/source-output.h   |    2 +-
 19 files changed, 596 insertions(+), 345 deletions(-)

-----------------------------------------------------------------------

commit e3f15104cf0386a0e0a782037e8c0323629be749
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:00:23 2009 +0200

    don't enabled tsched on software ALSA devices

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 7c09553..0296f64 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1630,6 +1630,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
         u->use_tsched = use_tsched = FALSE;
     }
 
+    if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) {
+        pa_log_info("Device is not a hardware device, disabling timer-based scheduling.");
+        u->use_tsched = use_tsched = FALSE;
+    }
+
     if (u->use_mmap)
         pa_log_info("Successfully enabled mmap() mode.");
 
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index dfd1870..ef365a2 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1482,6 +1482,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         u->use_tsched = use_tsched = FALSE;
     }
 
+    if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) {
+        pa_log_info("Device is not a hardware device, disabling timer-based scheduling.");
+        u->use_tsched = use_tsched = FALSE;
+    }
+
     if (u->use_mmap)
         pa_log_info("Successfully enabled mmap() mode.");
 
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 3f26aeb..2d0ca10 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1716,10 +1716,11 @@ char *pa_alsa_get_driver_name(int card) {
 
 char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm) {
     int card;
-
     snd_pcm_info_t* info;
     snd_pcm_info_alloca(&info);
 
+    pa_assert(pcm);
+
     if (snd_pcm_info(pcm, info) < 0)
         return NULL;
 
@@ -1749,3 +1750,15 @@ char *pa_alsa_get_reserve_name(const char *device) {
 
     return pa_sprintf_malloc("Audio%i", i);
 }
+
+pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
+    snd_pcm_info_t* info;
+    snd_pcm_info_alloca(&info);
+
+    pa_assert(pcm);
+
+    if (snd_pcm_info(pcm, info) < 0)
+        return FALSE;
+
+    return snd_pcm_info_get_card(info) >= 0;
+}
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index fe0f71e..68496d5 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -139,4 +139,6 @@ char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
 
 char *pa_alsa_get_reserve_name(const char *device);
 
+pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm);
+
 #endif

commit d04f3e1aef569bae3226ae94a42ad95ca2fa01c7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:21:34 2009 +0200

    Trigger move callback a little bit earlier so that no IO thread is running

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index ff49e69..2cdb37e 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -203,7 +203,7 @@ 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 void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_moving_cb(pa_sink_input *i);
 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);
@@ -215,7 +215,7 @@ 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_moved_cb(pa_source_output *o);
+static void source_output_moving_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);
 
@@ -636,7 +636,7 @@ static record_stream* record_stream_new(
     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->send_event = source_output_send_event_cb;
     s->source_output->userdata = s;
@@ -1049,7 +1049,7 @@ 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->moved = sink_input_moved_cb;
+    s->sink_input->moving = sink_input_moving_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
     s->sink_input->send_event = sink_input_send_event_cb;
     s->sink_input->userdata = s;
@@ -1539,7 +1539,7 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
 }
 
 /* Called from main context */
-static void sink_input_moved_cb(pa_sink_input *i) {
+static void sink_input_moving_cb(pa_sink_input *i) {
     playback_stream *s;
     pa_tagstruct *t;
     uint32_t maxlength, tlength, prebuf, minreq;
@@ -1661,7 +1661,7 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
 }
 
 /* Called from main context */
-static void source_output_moved_cb(pa_source_output *o) {
+static void source_output_moving_cb(pa_source_output *o) {
     record_stream *s;
     pa_tagstruct *t;
     uint32_t maxlength, fragsize;
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index ae2c6f5..15a7880 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -117,7 +117,7 @@ static void reset_callbacks(pa_sink_input *i) {
     i->attach = NULL;
     i->detach = NULL;
     i->suspend = NULL;
-    i->moved = NULL;
+    i->moving = NULL;
     i->kill = NULL;
     i->get_latency = NULL;
     i->state_change = NULL;
@@ -1169,6 +1169,9 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     } else
         new_resampler = NULL;
 
+    if (i->moving)
+        i->moving(i);
+
     i->sink = dest;
     i->save_sink = save;
     pa_idxset_put(dest->inputs, i, NULL);
@@ -1195,7 +1198,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
                 0,
                 &i->sink->silence);
     }
-
     pa_sink_update_status(dest);
 
     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
@@ -1216,9 +1218,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
 
     /* Notify everyone */
-    if (i->moved)
-        i->moved(i);
-
     pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 0bcb9d5..4e29be6 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -150,7 +150,7 @@ struct pa_sink_input {
 
     /* If non-NULL called whenever the the sink this input is attached
      * to changes. Called from main context */
-    void (*moved) (pa_sink_input *i);   /* may be NULL */
+    void (*moving) (pa_sink_input *i);   /* may be NULL */
 
     /* Supposed to unlink and destroy this stream. Called from main
      * context. */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 373d563..25aea7d 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -87,7 +87,7 @@ static void reset_callbacks(pa_source_output *o) {
     o->attach = NULL;
     o->detach = NULL;
     o->suspend = NULL;
-    o->moved = NULL;
+    o->moving = NULL;
     o->kill = NULL;
     o->get_latency = NULL;
     o->state_change = NULL;
@@ -749,6 +749,9 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
     } else
         new_resampler = NULL;
 
+    if (o->moving)
+        o->moving(o);
+
     o->source = dest;
     o->save_source = save;
     pa_idxset_put(o->source->outputs, o, NULL);
@@ -776,14 +779,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
     }
 
     pa_source_update_status(dest);
+
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
 
     pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
 
     /* Notify everyone */
-    if (o->moved)
-        o->moved(o);
-
     pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
     pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
 
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 018ec88..8d57ded 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -122,7 +122,7 @@ struct pa_source_output {
 
     /* If non-NULL called whenever the the source this output is attached
      * to changes. Called from main context */
-    void (*moved) (pa_source_output *o);   /* may be NULL */
+    void (*moving) (pa_source_output *o);   /* may be NULL */
 
     /* Supposed to unlink and destroy this stream. Called from main
      * context. */

commit 7f5481ede0e67343fc914a57ea6c68f36fced104
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:23:48 2009 +0200

    simplify latency config functions a bit and make them callable in more contexts

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 15a7880..da42242 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -819,26 +819,12 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes  /* in the
 }
 
 /* Called from thread context */
-static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
-    pa_sink_assert_ref(s);
-
-    if (usec == (pa_usec_t) -1)
-        return usec;
-
-    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
-        usec = s->thread_info.max_latency;
-
-    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
-        usec = s->thread_info.min_latency;
-
-    return usec;
-}
-
-/* Called from thread context */
 pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
     pa_sink_input_assert_ref(i);
 
-    usec = fixup_latency(i->sink, usec);
+    if (usec != (pa_usec_t) -1)
+        usec =  PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
     i->thread_info.requested_sink_latency = usec;
     pa_sink_invalidate_requested_latency(i->sink);
 
@@ -847,33 +833,42 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
 
 /* Called from main context */
 pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
+    pa_usec_t min_latency, max_latency;
+
     pa_sink_input_assert_ref(i);
 
-    if (PA_SINK_INPUT_IS_LINKED(i->state))
+    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
-    else
-        /* If this sink input is not realized yet, we have to touch
-         * the thread info data directly */
+        return usec;
+    }
+
+    /* If this sink input is not realized yet or we are being moved,
+     * we have to touch the thread info data directly */
+
+    pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
 
-        i->thread_info.requested_sink_latency = usec;
+    if (usec != (pa_usec_t) -1)
+        usec =  PA_CLAMP(usec, min_latency, max_latency);
+
+    i->thread_info.requested_sink_latency = usec;
 
     return usec;
 }
 
 /* Called from main context */
 pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
-    pa_usec_t usec = 0;
-
     pa_sink_input_assert_ref(i);
 
-    if (PA_SINK_INPUT_IS_LINKED(i->state))
+    if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
+        pa_usec_t usec = 0;
         pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
-    else
-        /* If this sink input is not realized yet, we have to touch
-         * the thread info data directly */
-        usec = i->thread_info.requested_sink_latency;
+        return usec;
+    }
 
-    return usec;
+    /* If this sink input is not realized yet or we are being moved,
+     * we have to touch the thread info data directly */
+
+    return i->thread_info.requested_sink_latency;
 }
 
 /* Called from main context */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 25aea7d..550b657 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -516,26 +516,12 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes  /* i
 }
 
 /* Called from thread context */
-static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
-    pa_source_assert_ref(s);
-
-    if (usec == (pa_usec_t) -1)
-        return usec;
-
-    if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
-        usec = s->thread_info.max_latency;
-
-    if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
-        usec = s->thread_info.min_latency;
-
-    return usec;
-}
-
-/* Called from thread context */
 pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
     pa_source_output_assert_ref(o);
 
-    usec = fixup_latency(o->source, usec);
+    if (usec != (pa_usec_t) -1)
+        usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+
     o->thread_info.requested_source_latency = usec;
     pa_source_invalidate_requested_latency(o->source);
 
@@ -544,33 +530,42 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
 
 /* Called from main context */
 pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
+    pa_usec_t min_latency, max_latency;
+
     pa_source_output_assert_ref(o);
 
-    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
         pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
-    else
-        /* If this source output is not realized yet, we have to touch
-         * the thread info data directly */
+        return usec;
+    }
+
+    /* If this source output is not realized yet or is being moved, we
+     * have to touch the thread info data directly */
+
+    pa_source_get_latency_range(o->source, &min_latency, &max_latency);
 
-        o->thread_info.requested_source_latency = usec;
+    if (usec != (pa_usec_t) -1)
+        usec = PA_CLAMP(usec, min_latency, max_latency);
+
+    o->thread_info.requested_source_latency = usec;
 
     return usec;
 }
 
 /* Called from main context */
 pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
-    pa_usec_t usec = 0;
-
     pa_source_output_assert_ref(o);
 
-    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+    if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
+        pa_usec_t usec = 0;
         pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
-    else
-        /* If this source output is not realized yet, we have to touch
-         * the thread info data directly */
-        usec = o->thread_info.requested_source_latency;
+        return usec;
+    }
 
-    return usec;
+    /* If this source output is not realized yet or is being moved, we
+     * have to touch the thread info data directly */
+
+    return o->thread_info.requested_source_latency;
 }
 
 /* Called from main context */

commit 7c37c37fff565d64cba5acd9a92f9c9854ca2601
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:26:23 2009 +0200

    document more often the context certain functions are called in

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 2cdb37e..b11265c 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -360,6 +360,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 
 /* structure management */
 
+/* Called from main context */
 static void upload_stream_unlink(upload_stream *s) {
     pa_assert(s);
 
@@ -371,6 +372,7 @@ static void upload_stream_unlink(upload_stream *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);
@@ -388,6 +390,7 @@ static void upload_stream_free(pa_object *o) {
     pa_xfree(s);
 }
 
+/* Called from main context */
 static upload_stream* upload_stream_new(
         pa_native_connection *c,
         const pa_sample_spec *ss,
@@ -420,6 +423,7 @@ static upload_stream* upload_stream_new(
     return s;
 }
 
+/* Called from main context */
 static void record_stream_unlink(record_stream *s) {
     pa_assert(s);
 
@@ -437,6 +441,7 @@ static void record_stream_unlink(record_stream *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);
@@ -447,6 +452,7 @@ static void record_stream_free(pa_object *o) {
     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);
@@ -581,6 +587,7 @@ static void fix_record_buffer_attr_post(
     *fragsize = (uint32_t) s->fragment_size;
 }
 
+/* Called from main context */
 static record_stream* record_stream_new(
         pa_native_connection *c,
         pa_source *source,
@@ -669,6 +676,7 @@ static record_stream* record_stream_new(
     return s;
 }
 
+/* Called from main context */
 static void record_stream_send_killed(record_stream *r) {
     pa_tagstruct *t;
     record_stream_assert_ref(r);
@@ -680,6 +688,7 @@ static void record_stream_send_killed(record_stream *r) {
     pa_pstream_send_tagstruct(r->connection->pstream, t);
 }
 
+/* Called from main context */
 static void playback_stream_unlink(playback_stream *s) {
     pa_assert(s);
 
@@ -700,6 +709,7 @@ static void playback_stream_unlink(playback_stream *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);
@@ -710,6 +720,7 @@ static void playback_stream_free(pa_object* o) {
     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);
@@ -953,6 +964,7 @@ static void fix_playback_buffer_attr_post(
     s->minreq = *minreq;
 }
 
+/* Called from main context */
 static playback_stream* playback_stream_new(
         pa_native_connection *c,
         pa_sink *sink,
@@ -1089,7 +1101,7 @@ static playback_stream* playback_stream_new(
     return s;
 }
 
-/* Called from thread context */
+/* Called from IO context */
 static void playback_stream_request_bytes(playback_stream *s) {
     size_t m, previous_missing;
 
@@ -1110,6 +1122,7 @@ static void playback_stream_request_bytes(playback_stream *s) {
 }
 
 
+/* Called from main context */
 static void playback_stream_send_killed(playback_stream *p) {
     pa_tagstruct *t;
     playback_stream_assert_ref(p);
@@ -1121,6 +1134,7 @@ static void playback_stream_send_killed(playback_stream *p) {
     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);
@@ -1142,6 +1156,7 @@ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdat
     return 0;
 }
 
+/* Called from main context */
 static void native_connection_unlink(pa_native_connection *c) {
     record_stream *r;
     output_stream *o;
@@ -1181,6 +1196,7 @@ static void native_connection_unlink(pa_native_connection *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);
 
@@ -1198,6 +1214,7 @@ static void native_connection_free(pa_object *o) {
     pa_xfree(c);
 }
 
+/* Called from main context */
 static void native_connection_send_memblock(pa_native_connection *c) {
     uint32_t start;
     record_stream *r;
@@ -1232,6 +1249,7 @@ static void native_connection_send_memblock(pa_native_connection *c) {
 
 /*** sink input callbacks ***/
 
+/* Called from thread context */
 static void handle_seek(playback_stream *s, int64_t indexw) {
     playback_stream_assert_ref(s);
 
@@ -1447,6 +1465,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     return 0;
 }
 
+/* Called from thread context */
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     playback_stream *s;
 
@@ -1461,6 +1480,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t 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;
 
@@ -1471,6 +1491,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t 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;
     size_t tlength;

commit b349dae6e146233175c38f46d4bd964b06a2ccd2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:27:07 2009 +0200

    add pa_memblockq_apply_attr()/pa_memblockq_get_attr()

diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index e2be42b..e6e7b73 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -851,6 +851,26 @@ void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
     bq->maxrewind = (maxrewind/bq->base)*bq->base;
 }
 
+void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) {
+    pa_assert(bq);
+    pa_assert(a);
+
+    pa_memblockq_set_maxlength(bq, a->maxlength);
+    pa_memblockq_set_tlength(bq, a->tlength);
+    pa_memblockq_set_prebuf(bq, a->prebuf);
+    pa_memblockq_set_minreq(bq, a->minreq);
+}
+
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) {
+    pa_assert(bq);
+    pa_assert(a);
+
+    a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq);
+    a->tlength = (uint32_t) pa_memblockq_get_tlength(bq);
+    a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq);
+    a->minreq = (uint32_t) pa_memblockq_get_minreq(bq);
+}
+
 int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
 
     pa_assert(bq);
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 0a74aa3..e315b83 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -158,6 +158,10 @@ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
 void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */
 void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
 
+/* Apply the data from pa_buffer_attr */
+void pa_memblockq_apply_attr(pa_memblockq *memblockq, const pa_buffer_attr *a);
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a);
+
 /* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
 void pa_memblockq_willneed(pa_memblockq *bq);
 
@@ -175,5 +179,4 @@ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
 /* Return how many items are currently stored in the queue */
 unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
 
-
 #endif

commit 491aafd8dcfaafced045a4ff494a1841819458d4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:27:33 2009 +0200

    typo fix

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 9418e96..9928f9b 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -624,7 +624,7 @@ typedef struct pa_timing_info {
     /**< The configured latency for the sink. \since 0.9.11 */
 
     pa_usec_t configured_source_usec;
-    /**< The configured latency for * the source. \since 0.9.11 */
+    /**< The configured latency for the source. \since 0.9.11 */
 
     int64_t since_underrun;
     /**< Bytes that were handed to the sink since the last underrun

commit 65b787d00010ee59d823ec081c82116f2400ecf0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 18:46:12 2009 +0200

    notify clients about tlength changes

diff --git a/PROTOCOL b/PROTOCOL
index 8c5937b..88166f1 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -168,3 +168,16 @@ PA_COMMAND_GET_MODULE_INFO_LIST
 
   remove bool auto_unload
   add proplist at the end
+
+new messages:
+
+  PA_COMMAND_GET_CARD_INFO
+  PA_COMMAND_GET_CARD_INFO_LIST
+  PA_COMMAND_SET_CARD_PROFILE
+
+  PA_COMMAND_CLIENT_EVENT
+  PA_COMMAND_PLAYBACK_STREAM_EVENT
+  PA_COMMAND_RECORD_STREAM_EVENT
+
+  PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED
+  PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 00dffc2..7c86a43 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -99,7 +99,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_EXTENSION] = pa_command_extension,
     [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,
     [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event,
-    [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event
+    [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event,
+    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
+    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr
 };
 static void context_free(pa_context *c);
 
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 9646d8a..c08bd26 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -185,6 +185,8 @@ struct pa_stream {
     void *started_userdata;
     pa_stream_event_cb_t event_callback;
     void *event_userdata;
+    pa_stream_notify_cb_t buffer_attr_callback;
+    void *buffer_attr_userdata;
 };
 
 typedef void (*pa_operation_cb_t)(void);
@@ -213,6 +215,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
 void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 
 pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
 void pa_operation_done(pa_operation *o);
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 48e3b08..bb53b19 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -72,6 +72,8 @@ static void reset_callbacks(pa_stream *s) {
     s->started_userdata = NULL;
     s->event_callback = NULL;
     s->event_userdata = NULL;
+    s->buffer_attr_callback = NULL;
+    s->buffer_attr_userdata = NULL;
 }
 
 pa_stream *pa_stream_new_with_proplist(
@@ -396,7 +398,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     const char *dn;
     pa_bool_t suspended;
     uint32_t di;
-    pa_usec_t usec;
+    pa_usec_t usec = 0;
     uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
 
     pa_assert(pd);
@@ -486,6 +488,80 @@ finish:
     pa_context_unref(c);
 }
 
+void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    pa_usec_t usec = 0;
+    uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED || command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (pa_tagstruct_getu32(t, &channel) < 0) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (command == PA_COMMAND_RECORD_STREAM_MOVED) {
+        if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &fragsize) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    } else {
+        if (pa_tagstruct_getu32(t, &maxlength) < 0 ||
+            pa_tagstruct_getu32(t, &tlength) < 0 ||
+            pa_tagstruct_getu32(t, &prebuf) < 0 ||
+            pa_tagstruct_getu32(t, &minreq) < 0 ||
+            pa_tagstruct_get_usec(t, &usec) < 0) {
+            pa_context_fail(c, PA_ERR_PROTOCOL);
+            goto finish;
+        }
+    }
+
+    if (!pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED ? c->playback_streams : c->record_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (s->direction == PA_STREAM_RECORD)
+        s->timing_info.configured_source_usec = usec;
+    else
+        s->timing_info.configured_sink_usec = usec;
+
+    s->buffer_attr.maxlength = maxlength;
+    s->buffer_attr.fragsize = fragsize;
+    s->buffer_attr.tlength = tlength;
+    s->buffer_attr.prebuf = prebuf;
+    s->buffer_attr.minreq = minreq;
+
+    request_auto_timing_update(s, TRUE);
+
+    if (s->buffer_attr_callback)
+        s->buffer_attr_callback(s, s->buffer_attr_userdata);
+
+finish:
+    pa_context_unref(c);
+}
+
 void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_context *c = userdata;
     pa_stream *s;
@@ -1798,6 +1874,20 @@ void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *u
     s->event_userdata = userdata;
 }
 
+void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->buffer_attr_callback = cb;
+    s->buffer_attr_userdata = userdata;
+}
+
 void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_operation *o = userdata;
     int success = 1;
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index e80bc65..8e99a75 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -512,6 +512,13 @@ void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, vo
  * control event is received.\since 0.9.15 */
 void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata);
 
+/** Set the callback function that is called whenver the buffer
+ * attributes on the server side change. Please note that the buffer
+ * attributes can change when moving a stream to a different
+ * sink/source too, hence if you use this callback you should use
+ * pa_stream_set_moved_callback() as well. \since 0.9.15 */
+void pa_stream_set_buffer_attr_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+
 /** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
 pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
 
@@ -530,7 +537,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
  * temporarily. Available for playback streams only. */
 pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
 
-/** Rename the stream.*/
+/** Rename the stream. */
 pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
 
 /** Return the current playback/recording time. This is based on the
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 6951e10..d4d7f3e 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -152,7 +152,7 @@ enum {
     /* Supported since protocol v14 (0.9.12) */
     PA_COMMAND_EXTENSION,
 
-    /* Supported since protocol v15 (0.9.15*/
+    /* Supported since protocol v15 (0.9.15) */
     PA_COMMAND_GET_CARD_INFO,
     PA_COMMAND_GET_CARD_INFO_LIST,
     PA_COMMAND_SET_CARD_PROFILE,
@@ -161,6 +161,10 @@ enum {
     PA_COMMAND_PLAYBACK_STREAM_EVENT,
     PA_COMMAND_RECORD_STREAM_EVENT,
 
+    /* SERVER->CLIENT */
+    PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
+    PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index b11265c..3e75cab 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -81,7 +81,11 @@ typedef struct record_stream {
 
     pa_source_output *source_output;
     pa_memblockq *memblockq;
-    size_t fragment_size;
+
+    pa_bool_t adjust_latency:1;
+    pa_bool_t early_requests:1;
+
+    pa_buffer_attr buffer_attr;
     pa_usec_t source_latency;
 } record_stream;
 
@@ -105,14 +109,18 @@ typedef struct playback_stream {
 
     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;
-    size_t minreq;
     pa_usec_t sink_latency;
+    pa_buffer_attr buffer_attr;
 
     /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
     int64_t read_index, write_index;
@@ -180,7 +188,8 @@ enum {
     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 {
@@ -188,7 +197,8 @@ enum {
     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 {
@@ -478,35 +488,34 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
     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);
-    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);
 
-    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
@@ -514,7 +523,7 @@ static void fix_record_buffer_attr_pre(
 
         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
@@ -536,14 +545,14 @@ static void fix_record_buffer_attr_pre(
     else
         s->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 */
 
         fragsize_usec = s->source_latency;
 
-    } else if (adjust_latency) {
+    } else if (s->adjust_latency) {
 
         /* Now subtract what we actually got */
 
@@ -556,35 +565,31 @@ static void fix_record_buffer_attr_pre(
     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);
-    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);
 
-    s->fragment_size = (*fragsize/base)*base;
-    if (s->fragment_size <= 0)
-        s->fragment_size = base;
+    s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
+    if (s->buffer_attr.fragsize <= 0)
+        s->buffer_attr.fragsize = base;
 
-    if (s->fragment_size > *maxlength)
-        s->fragment_size = *maxlength;
-
-    *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 */
@@ -594,8 +599,7 @@ static record_stream* record_stream_new(
         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,
@@ -610,7 +614,6 @@ static record_stream* record_stream_new(
 
     pa_assert(c);
     pa_assert(ss);
-    pa_assert(maxlength);
     pa_assert(p);
     pa_assert(ret);
 
@@ -639,6 +642,9 @@ static record_stream* record_stream_new(
     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;
 
     s->source_output->push = source_output_push_cb;
     s->source_output->kill = source_output_kill_cb;
@@ -648,11 +654,11 @@ static record_stream* record_stream_new(
     s->source_output->send_event = source_output_send_event_cb;
     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,
-            *maxlength,
+            s->buffer_attr.maxlength,
             0,
             base = pa_frame_size(&source_output->sample_spec),
             1,
@@ -660,7 +666,8 @@ static record_stream* record_stream_new(
             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;
@@ -668,8 +675,8 @@ 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",
-                ((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) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->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->source_latency / PA_USEC_PER_MSEC);
 
     pa_source_output_put(s->source_output);
@@ -799,67 +806,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_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->sink_latency);
+            pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+            break;
+        }
     }
 
     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) {
-
+/* Called from main context */
+static void fix_playback_buffer_attr(playback_stream *s) {
     size_t frame_size;
     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);
 
-    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);
 
-    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;
-
         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
@@ -901,14 +920,14 @@ static void fix_playback_buffer_attr_pre(
 
     s->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 */
 
         minreq_usec = s->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
@@ -925,43 +944,22 @@ static void fix_playback_buffer_attr_pre(
 
     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))
-        *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) {
+    if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
+        s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
 
-    pa_assert(s);
-    pa_assert(maxlength);
-    pa_assert(tlength);
-    pa_assert(prebuf);
-    pa_assert(minreq);
-
-    *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);
-
-    s->minreq = *minreq;
+    if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > s->buffer_attr.tlength)
+        s->buffer_attr.prebuf = s->buffer_attr.tlength;
 }
 
 /* Called from main context */
@@ -970,10 +968,7 @@ static playback_stream* playback_stream_new(
         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,
@@ -994,10 +989,6 @@ static playback_stream* playback_stream_new(
 
     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(ret);
@@ -1054,6 +1045,9 @@ static playback_stream* playback_stream_new(
     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;
@@ -1068,21 +1062,21 @@ static playback_stream* playback_stream_new(
 
     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,
-            *maxlength,
-            *tlength,
+            s->buffer_attr.maxlength,
+            s->buffer_attr.tlength,
             pa_frame_size(&sink_input->sample_spec),
-            *prebuf,
-            *minreq,
+            s->buffer_attr.prebuf,
+            s->buffer_attr.minreq,
             0,
             &silence);
-
     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);
 
@@ -1092,9 +1086,9 @@ 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",
-                ((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) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->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->sink_latency / PA_USEC_PER_MSEC);
 
     pa_sink_input_put(s->sink_input);
@@ -1103,7 +1097,7 @@ static playback_stream* playback_stream_new(
 
 /* Called from IO context */
 static void playback_stream_request_bytes(playback_stream *s) {
-    size_t m, previous_missing;
+    size_t m, previous_missing, minreq;
 
     playback_stream_assert_ref(s);
 
@@ -1115,9 +1109,10 @@ static void playback_stream_request_bytes(playback_stream *s) {
 /*     pa_log("request_bytes(%lu)", (unsigned long) m); */
 
     previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m);
+    minreq = pa_memblockq_get_minreq(s->memblockq);
 
     if (pa_memblockq_prebuf_active(s->memblockq) ||
-        (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+        (previous_missing < minreq && previous_missing+m >= minreq))
         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
 }
 
@@ -1234,8 +1229,8 @@ static void native_connection_send_memblock(pa_native_connection *c) {
         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);
 
@@ -1417,6 +1412,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
              * 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);
@@ -1502,8 +1503,10 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
 
     tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
 
-    if (pa_memblockq_get_tlength(s->memblockq) < tlength)
+    if (pa_memblockq_get_tlength(s->memblockq) < tlength) {
         pa_memblockq_set_tlength(s->memblockq, 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 */
@@ -1563,23 +1566,14 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
 static void sink_input_moving_cb(pa_sink_input *i) {
     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);
 
-    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);
-
-    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;
@@ -1593,10 +1587,10 @@ static void sink_input_moving_cb(pa_sink_input *i) {
     pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
 
     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_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->sink_latency);
     }
 
@@ -1685,18 +1679,15 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
 static void source_output_moving_cb(pa_source_output *o) {
     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);
 
-    fragsize = (uint32_t) s->fragment_size;
-    maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
-
-    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;
@@ -1710,8 +1701,8 @@ static void source_output_moving_cb(pa_source_output *o) {
     pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
 
     if (s->connection->version >= 13) {
-        pa_tagstruct_putu32(t, maxlength);
-        pa_tagstruct_putu32(t, fragsize);
+        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
         pa_tagstruct_put_usec(t, s->source_latency);
     }
 
@@ -1744,7 +1735,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;
-    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;
@@ -1773,6 +1765,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
     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(
@@ -1781,11 +1774,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_U32, &maxlength,
+                PA_TAG_U32, &attr.maxlength,
                 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) {
@@ -1896,7 +1889,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
      * 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, &ret);
+    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);
 
     CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -1912,10 +1905,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 */
 
-        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) {
@@ -1999,7 +1992,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;
-    uint32_t maxlength, fragment_size;
+    pa_buffer_attr attr;
     uint32_t source_index;
     const char *name = NULL, *source_name;
     pa_sample_spec ss;
@@ -2029,14 +2022,16 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     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 ||
-        pa_tagstruct_getu32(t, &maxlength) < 0 ||
+        pa_tagstruct_getu32(t, &attr.maxlength) < 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;
     }
@@ -2146,7 +2141,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
         (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_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, &ret);
+    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);
 
     CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -2159,8 +2154,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 */
 
-        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) {
@@ -3456,12 +3451,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;
-    uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+    pa_buffer_attr a;
     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;
@@ -3479,10 +3476,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
 
         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) ||
@@ -3491,18 +3488,18 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
             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);
-        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)
             pa_tagstruct_put_usec(reply, s->sink_latency);
@@ -3517,8 +3514,8 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
 
         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) ||
@@ -3527,13 +3524,18 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
             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);
-        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)
             pa_tagstruct_put_usec(reply, s->source_latency);

commit aa6803653e513214fa1ab1d03ace5c91c5357a2b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 19:13:07 2009 +0200

    rework tunneling code

diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index c148884..7f303f1 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -145,6 +145,7 @@ static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t t
 static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 #ifdef TUNNEL_SINK
@@ -159,7 +160,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
     [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
     [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
-    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved
+    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
+    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_RECORD_STREAM_EVENT] = command_stream_or_client_event,
+    [PA_COMMAND_CLIENT_EVENT] = command_stream_or_client_event
 };
 
 struct userdata {
@@ -196,8 +200,8 @@ struct userdata {
     pa_bool_t remote_corked:1;
     pa_bool_t remote_suspended:1;
 
-    pa_usec_t transport_usec;
-    pa_bool_t transport_usec_valid;
+    pa_usec_t transport_usec; /* maintained in the main thread */
+    pa_usec_t thread_transport_usec; /* maintained in the IO thread */
 
     uint32_t ignore_latency_before;
 
@@ -222,6 +226,11 @@ struct userdata {
 static void request_latency(struct userdata *u);
 
 /* Called from main context */
+static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_log_debug("Got stream or client event.");
+}
+
+/* Called from main context */
 static void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
 
@@ -261,11 +270,14 @@ static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
         pa_tagstruct_get_boolean(t, &suspended) < 0 ||
         !pa_tagstruct_eof(t)) {
-        pa_log("Invalid packet");
+
+        pa_log("Invalid packet.");
         pa_module_unload_request(u->module, TRUE);
         return;
     }
 
+    pa_log_debug("Server reports device suspend.");
+
 #ifdef TUNNEL_SINK
     pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
 #else
@@ -278,13 +290,33 @@ static void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag
 /* Called from main context */
 static void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
+    uint32_t channel, di;
+    const char *dn;
+    pa_bool_t suspended;
 
     pa_assert(pd);
     pa_assert(t);
     pa_assert(u);
     pa_assert(u->pdispatch == pd);
 
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_getu32(t, &di) < 0 ||
+        pa_tagstruct_gets(t, &dn) < 0 ||
+        pa_tagstruct_get_boolean(t, &suspended) < 0) {
+
+        pa_log_error("Invalid packet.");
+        pa_module_unload_request(u->module, TRUE);
+        return;
+    }
+
     pa_log_debug("Server reports a stream move.");
+
+#ifdef TUNNEL_SINK
+    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
+#else
+    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(!!suspended), 0, NULL);
+#endif
+
     request_latency(u);
 }
 
@@ -306,21 +338,21 @@ static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 #endif
 
 /* Called from IO thread context */
-static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
+static void check_smoother_status(struct userdata *u, pa_bool_t past)  {
     pa_usec_t x;
-    pa_assert(u);
 
-    if (u->remote_corked == cork)
-        return;
+    pa_assert(u);
 
-    u->remote_corked = cork;
     x = pa_rtclock_usec();
 
-    /* Correct by the time this needs to travel to the other side.
-     * This is a valid thread-safe access, because the main thread is
-     * waiting for us */
-    if (u->transport_usec_valid)
-        x += u->transport_usec;
+    /* Correct by the time the requested issued needs to travel to the
+     * other side.  This is a valid thread-safe access, because the
+     * main thread is waiting for us */
+
+    if (past)
+        x -= u->thread_transport_usec;
+    else
+        x += u->thread_transport_usec;
 
     if (u->remote_suspended || u->remote_corked)
         pa_smoother_pause(u->smoother, x);
@@ -328,6 +360,17 @@ static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
         pa_smoother_resume(u->smoother, x);
 }
 
+/* Called from IO thread context */
+static void stream_cork_within_thread(struct userdata *u, pa_bool_t cork) {
+    pa_assert(u);
+
+    if (u->remote_corked == cork)
+        return;
+
+    u->remote_corked = cork;
+    check_smoother_status(u, FALSE);
+}
+
 /* Called from main context */
 static void stream_cork(struct userdata *u, pa_bool_t cork) {
     pa_tagstruct *t;
@@ -352,26 +395,13 @@ static void stream_cork(struct userdata *u, pa_bool_t cork) {
 
 /* Called from IO thread context */
 static void stream_suspend_within_thread(struct userdata *u, pa_bool_t suspend) {
-    pa_usec_t x;
     pa_assert(u);
 
     if (u->remote_suspended == suspend)
         return;
 
     u->remote_suspended = suspend;
-
-    x = pa_rtclock_usec();
-
-    /* Correct by the time this needed to travel from the other side.
-     * This is a valid thread-safe access, because the main thread is
-     * waiting for us */
-    if (u->transport_usec_valid)
-        x -= u->transport_usec;
-
-    if (u->remote_suspended || u->remote_corked)
-        pa_smoother_pause(u->smoother, x);
-    else
-        pa_smoother_resume(u->smoother, x);
+    check_smoother_status(u, TRUE);
 }
 
 #ifdef TUNNEL_SINK
@@ -446,13 +476,16 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
             y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
 
-            if (y > (pa_usec_t) offset || offset < 0)
+            if (y > (pa_usec_t) offset)
                 y -= (pa_usec_t) offset;
             else
                 y = 0;
 
             pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
 
+            /* We can access this freely here, since the main thread is waiting for us */
+            u->thread_transport_usec = u->transport_usec;
+
             return 0;
         }
 
@@ -546,14 +579,13 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
             pa_usec_t y;
 
             y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
-
-            if (offset >= 0 || y > (pa_usec_t) -offset)
-                y += (pa_usec_t) offset;
-            else
-                y = 0;
+            y += (pa_usec_t) offset;
 
             pa_smoother_put(u->smoother, pa_rtclock_usec(), y);
 
+            /* We can access this freely here, since the main thread is waiting for us */
+            u->thread_transport_usec = u->transport_usec;
+
             return 0;
         }
     }
@@ -606,7 +638,7 @@ static void thread_func(void *userdata) {
 
 #ifdef TUNNEL_SINK
         if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
-            if (u->sink->thread_info.rewind_requested)
+            if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
                 pa_sink_process_rewind(u->sink, 0);
 #endif
 
@@ -662,7 +694,7 @@ fail:
 /* Called from main context */
 static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
-    pa_usec_t sink_usec, source_usec, transport_usec = 0;
+    pa_usec_t sink_usec, source_usec;
     pa_bool_t playing;
     int64_t write_index, read_index;
     struct timeval local, remote, now;
@@ -709,7 +741,6 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
     }
 
     if (tag < u->ignore_latency_before) {
-        request_latency(u);
         return;
     }
 
@@ -725,7 +756,6 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
 #endif
     } else
         u->transport_usec = pa_timeval_diff(&now, &local)/2;
-    u->transport_usec_valid = TRUE;
 
     /* First, take the device's delay */
 #ifdef TUNNEL_SINK
@@ -745,9 +775,9 @@ static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint
     /* Our measurements are already out of date, hence correct by the     *
      * transport latency */
 #ifdef TUNNEL_SINK
-    delay -= (int64_t) transport_usec;
+    delay -= (int64_t) u->transport_usec;
 #else
-    delay += (int64_t) transport_usec;
+    delay += (int64_t) u->transport_usec;
 #endif
 
     /* Now correct by what we have have read/written since we requested the update */
@@ -786,8 +816,7 @@ static void request_latency(struct userdata *u) {
     pa_tagstruct_putu32(t, tag = u->ctag++);
     pa_tagstruct_putu32(t, u->channel);
 
-    pa_gettimeofday(&now);
-    pa_tagstruct_put_timeval(t, &now);
+    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
 
     pa_pstream_send_tagstruct(u->pstream, t);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
@@ -861,6 +890,7 @@ static void update_description(struct userdata *u) {
 static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
     pa_sample_spec ss;
+    pa_channel_map cm;
     const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
     uint32_t cookie;
 
@@ -882,7 +912,9 @@ static void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
         pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
         pa_tagstruct_gets(t, &default_sink_name) < 0 ||
         pa_tagstruct_gets(t, &default_source_name) < 0 ||
-        pa_tagstruct_getu32(t, &cookie) < 0) {
+        pa_tagstruct_getu32(t, &cookie) < 0 ||
+        (u->version >= 15 &&
+         pa_tagstruct_get_channel_map(t, &cm) < 0)) {
 
         pa_log("Parse failure");
         goto fail;
@@ -963,6 +995,20 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t
         }
     }
 
+    if (u->version >= 15) {
+        pa_volume_t base_volume;
+        uint32_t state, n_volume_steps, card;
+
+        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
+            pa_tagstruct_getu32(t, &state) < 0 ||
+            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
+            pa_tagstruct_getu32(t, &card) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
@@ -1059,12 +1105,11 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag
         pa_cvolume_equal(&volume, &u->sink->virtual_volume))
         return;
 
-    memcpy(&u->sink->virtual_volume, &volume, sizeof(pa_cvolume));
+    pa_sink_volume_changed(u->sink, &volume);
 
     if (u->version >= 11)
-        u->sink->muted = !!mute;
+        pa_sink_mute_changed(u->sink, mute);
 
-    pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
     return;
 
 fail:
@@ -1126,6 +1171,20 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa
         }
     }
 
+    if (u->version >= 15) {
+        pa_volume_t base_volume;
+        uint32_t state, n_volume_steps, card;
+
+        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
+            pa_tagstruct_getu32(t, &state) < 0 ||
+            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
+            pa_tagstruct_getu32(t, &card) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
@@ -1314,11 +1373,11 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t
         if (pa_tagstruct_get_usec(t, &usec) < 0)
             goto parse_error;
 
-#ifdef TUNNEL_SINK
-        pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0);
-#else
-        pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0);
-#endif
+/* #ifdef TUNNEL_SINK */
+/*         pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); */
+/* #else */
+/*         pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); */
+/* #endif */
     }
 
     if (!pa_tagstruct_eof(t))
@@ -1391,11 +1450,17 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
 
 #ifdef TUNNEL_SINK
+    pa_proplist_setf(u->sink->proplist, "tunnel.remote_version", "%u", u->version);
+    pa_sink_update_proplist(u->sink, 0, NULL);
+
     pa_snprintf(name, sizeof(name), "%s for %s@%s",
                 u->sink_name,
                 pa_get_user_name(un, sizeof(un)),
                 pa_get_host_name(hn, sizeof(hn)));
 #else
+    pa_proplist_setf(u->source->proplist, "tunnel.remote_version", "%u", u->version);
+    pa_source_update_proplist(u->source, 0, NULL);
+
     pa_snprintf(name, sizeof(name), "%s for %s@%s",
                 u->source_name,
                 pa_get_user_name(un, sizeof(un)),
@@ -1409,9 +1474,9 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
     if (u->version >= 13) {
         pa_proplist *pl;
         pl = pa_proplist_new();
-        pa_init_proplist(pl);
         pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
         pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
+        pa_init_proplist(pl);
         pa_tagstruct_put_proplist(reply, pl);
         pa_proplist_free(pl);
     } else
@@ -1503,6 +1568,14 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
         pa_tagstruct_put_boolean(reply, TRUE); /* early rquests */
     }
 
+    if (u->version >= 15) {
+#ifdef TUNNEL_SINK
+        pa_tagstruct_put_boolean(reply, FALSE); /* muted_set */
+#endif
+        pa_tagstruct_put_boolean(reply, FALSE); /* don't inhibit auto suspend */
+        pa_tagstruct_put_boolean(reply, FALSE); /* fail on suspend */
+    }
+
     pa_pstream_send_tagstruct(u->pstream, reply);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
 
@@ -1559,7 +1632,6 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
 
     u->counter_delta += (int64_t) chunk->length;
 }
-
 #endif
 
 /* Called from main context */
@@ -1700,8 +1772,7 @@ int pa__init(pa_module*m) {
     u->device_index = u->channel = PA_INVALID_INDEX;
     u->time_event = NULL;
     u->ignore_latency_before = 0;
-    u->transport_usec = 0;
-    u->transport_usec_valid = FALSE;
+    u->transport_usec = u->thread_transport_usec = 0;
     u->remote_suspended = u->remote_corked = FALSE;
     u->counter = u->counter_delta = 0;
 
@@ -1747,7 +1818,7 @@ int pa__init(pa_module*m) {
     if (u->sink_name)
         pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
 
-    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL);
+    u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL);
     pa_sink_new_data_done(&data);
 
     if (!u->sink) {
@@ -1763,7 +1834,7 @@ int pa__init(pa_module*m) {
 
     u->sink->refresh_volume = u->sink->refresh_muted = FALSE;
 
-    pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0);
+/*     pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); */
 
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
@@ -1797,7 +1868,7 @@ int pa__init(pa_module*m) {
     u->source->set_state = source_set_state;
     u->source->userdata = u;
 
-    pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0);
+/*     pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); */
 
     pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
     pa_source_set_rtpoll(u->source, u->rtpoll);

commit 061344fbd78f4a643f4e2a12b24c5d1ae1622e04
Merge: aa68036 87d63b1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Mar 30 19:14:09 2009 +0200

    Merge commit 'origin/master'


-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list