[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.15-test7-27-g6ba3333

Lennart Poettering gitmailer-noreply at 0pointer.de
Sat Apr 4 18:06:28 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  ca39fa2c6f9036a4c90804625842f6af38fb8008 (commit)

- Log -----------------------------------------------------------------
6ba3333 Merge branch 'master' of ssh://rootserver/home/lennart/git/public/pulseaudio
923c5bc make it easy to disable interpolation in the interpolation test tool
14e11e4 Fix a couple of races in native protocol
7dafa87 don't try to outsmart the transport
ce73e71 introduce pa_{sink|source}_get_latency_within_thread()
d035f4a Modify smoothing code to make cubic interpolation optional and allow 'quick fixups' on resuming
1c26d7e plot the difference between system and sound card time
373b5ef properly account for seeks in the requested_bytes counter
-----------------------------------------------------------------------

Summary of changes:
 src/modules/alsa/alsa-sink.c                    |   15 ++-
 src/modules/alsa/alsa-source.c                  |   12 ++-
 src/modules/bluetooth/module-bluetooth-device.c |   11 ++-
 src/modules/module-combine.c                    |   11 ++-
 src/modules/module-esound-sink.c                |   11 ++-
 src/modules/module-ladspa-sink.c                |    2 +-
 src/modules/module-raop-sink.c                  |   11 ++-
 src/modules/module-tunnel.c                     |   13 ++-
 src/modules/rtp/module-rtp-recv.c               |   14 ++-
 src/pulse/context.c                             |    4 +-
 src/pulse/internal.h                            |    2 +-
 src/pulse/stream.c                              |   50 +++----
 src/pulsecore/memblockq.c                       |   18 ++-
 src/pulsecore/memblockq.h                       |    2 +-
 src/pulsecore/protocol-native.c                 |  168 +++++++++++++++--------
 src/pulsecore/sink-input.c                      |    4 +-
 src/pulsecore/sink.c                            |   26 ++++
 src/pulsecore/sink.h                            |    2 +
 src/pulsecore/source-output.c                   |    2 +-
 src/pulsecore/source.c                          |   26 ++++
 src/pulsecore/source.h                          |    1 +
 src/pulsecore/time-smoother.c                   |   41 +++++-
 src/pulsecore/time-smoother.h                   |   14 ++-
 src/tests/interpol-test.c                       |   27 ++++-
 src/tests/memblockq-test.c                      |   18 ++--
 src/tests/smoother-test.c                       |    8 +-
 26 files changed, 367 insertions(+), 146 deletions(-)

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

commit 373b5efe51238b0ad34cb9a9d8fc61b973afdad8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Apr 1 23:05:09 2009 +0200

    properly account for seeks in the requested_bytes counter

diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index e619acd..44052c9 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -235,7 +235,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
         if (amount > 0) {
             unsigned c;
 
-            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE);
+            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
 
             pa_log_debug("Resetting plugin");
 
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 33e23af..209770f 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -238,7 +238,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
     else
         delta = j;
 
-    pa_memblockq_seek(s->memblockq, delta * (int64_t) s->rtp_context.frame_size, PA_SEEK_RELATIVE);
+    pa_memblockq_seek(s->memblockq, delta * (int64_t) s->rtp_context.frame_size, PA_SEEK_RELATIVE, TRUE);
 
     pa_rtclock_get(&now);
 
@@ -246,7 +246,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
 
     if (pa_memblockq_push(s->memblockq, &chunk) < 0) {
         pa_log_warn("Queue overrun");
-        pa_memblockq_seek(s->memblockq, (int64_t) chunk.length, PA_SEEK_RELATIVE);
+        pa_memblockq_seek(s->memblockq, (int64_t) chunk.length, PA_SEEK_RELATIVE, TRUE);
     }
 
 /*     pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq)); */
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 28d1719..991a886 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -364,10 +364,10 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
     if ((s = pa_dynarray_get(c->record_streams, channel))) {
 
         if (chunk->memblock) {
-            pa_memblockq_seek(s->record_memblockq, offset, seek);
+            pa_memblockq_seek(s->record_memblockq, offset, seek, TRUE);
             pa_memblockq_push_align(s->record_memblockq, chunk);
         } else
-            pa_memblockq_seek(s->record_memblockq, offset+chunk->length, seek);
+            pa_memblockq_seek(s->record_memblockq, offset+chunk->length, seek, TRUE);
 
         if (s->read_callback) {
             size_t l;
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index cf362d9..43d1877 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -140,7 +140,7 @@ struct pa_stream {
     uint32_t syncid;
     uint32_t stream_index;
 
-    uint32_t requested_bytes;
+    int64_t requested_bytes;
     pa_buffer_attr buffer_attr;
 
     uint32_t device_index;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 16342ca..c4a54af 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -729,7 +729,7 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tag
     s->requested_bytes += bytes;
 
     if (s->requested_bytes > 0 && s->write_callback)
-        s->write_callback(s, s->requested_bytes, s->write_userdata);
+        s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
 
 finish:
     pa_context_unref(c);
@@ -826,7 +826,7 @@ static void create_stream_complete(pa_stream *s) {
     pa_stream_set_state(s, PA_STREAM_READY);
 
     if (s->requested_bytes > 0 && s->write_callback)
-        s->write_callback(s, s->requested_bytes, s->write_userdata);
+        s->write_callback(s, (size_t) s->requested_bytes, s->write_userdata);
 
     if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
         struct timeval tv;
@@ -874,6 +874,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s
 
 void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_stream *s = userdata;
+    uint32_t requested_bytes;
 
     pa_assert(pd);
     pa_assert(s);
@@ -893,11 +894,13 @@ 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_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) {
+        ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &requested_bytes) < 0)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
     }
 
+    s->requested_bytes = (int64_t) requested_bytes;
+
     if (s->context->version >= 9) {
         if (s->direction == PA_STREAM_PLAYBACK) {
             if (pa_tagstruct_getu32(t, &s->buffer_attr.maxlength) < 0 ||
@@ -1258,12 +1261,9 @@ int pa_stream_write(
     if (free_cb && pa_pstream_get_shm(s->context->pstream))
         free_cb((void*) data);
 
-    if (length < s->requested_bytes)
-        s->requested_bytes -= (uint32_t) length;
-    else
-        s->requested_bytes = 0;
-
-    /* FIXME!!! ^^^ will break when offset is != 0 and mode is not RELATIVE*/
+    /* This is obviously wrong since we ignore the seeking index . But
+     * that's OK, the server side applies the same error */
+    s->requested_bytes -= (seek == PA_SEEK_RELATIVE ? offset : 0) + (int64_t) length;
 
     if (s->direction == PA_STREAM_PLAYBACK) {
 
@@ -1359,7 +1359,7 @@ size_t pa_stream_writable_size(pa_stream *s) {
     PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (size_t) -1);
     PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_RECORD, PA_ERR_BADSTATE, (size_t) -1);
 
-    return s->requested_bytes;
+    return s->requested_bytes > 0 ? (size_t) s->requested_bytes : 0;
 }
 
 size_t pa_stream_readable_size(pa_stream *s) {
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index e6e7b73..d12d13a 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -601,7 +601,7 @@ size_t pa_memblockq_missing(pa_memblockq *bq) {
     return l >= bq->minreq ? l : 0;
 }
 
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, pa_bool_t account) {
     int64_t old, delta;
     pa_assert(bq);
 
@@ -628,12 +628,14 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
 
     delta = bq->write_index - old;
 
-    if (delta >= (int64_t) bq->requested) {
-        delta -= (int64_t) bq->requested;
-        bq->requested = 0;
-    } else if (delta >= 0) {
-        bq->requested -= (size_t) delta;
-        delta = 0;
+    if (account) {
+        if (delta >= (int64_t) bq->requested) {
+            delta -= (int64_t) bq->requested;
+            bq->requested = 0;
+        } else if (delta >= 0) {
+            bq->requested -= (size_t) delta;
+            delta = 0;
+        }
     }
 
     bq->missing -= delta;
@@ -895,7 +897,7 @@ int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
 
             pa_memblock_unref(chunk.memblock);
         } else
-            pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE);
+            pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE, TRUE);
 
         pa_memblockq_drop(bq, chunk.length);
     }
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index e315b83..146d261 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -85,7 +85,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
 int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
 
 /* Manipulate the write pointer */
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, pa_bool_t account);
 
 /* Return a copy of the next memory chunk in the queue. It is not
  * removed from the queue. There are two reasons this function might
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index e11d7a6..30d68f3 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -738,24 +738,21 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
     switch (code) {
         case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
             pa_tagstruct *t;
-            uint32_t l = 0;
+            int l = 0;
 
             for (;;) {
-                if ((l = (uint32_t) pa_atomic_load(&s->missing)) <= 0)
-                    break;
+                if ((l = pa_atomic_load(&s->missing)) <= 0)
+                    return 0;
 
-                if (pa_atomic_cmpxchg(&s->missing, (int) l, 0))
+                if (pa_atomic_cmpxchg(&s->missing, l, 0))
                     break;
             }
 
-            if (l <= 0)
-                break;
-
             t = pa_tagstruct_new(NULL, 0);
             pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
             pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
             pa_tagstruct_putu32(t, s->index);
-            pa_tagstruct_putu32(t, l);
+            pa_tagstruct_putu32(t, (uint32_t) l);
             pa_pstream_send_tagstruct(s->connection->pstream, t);
 
 /*             pa_log("Requesting %lu bytes", (unsigned long) l); */
@@ -1097,7 +1094,8 @@ static playback_stream* playback_stream_new(
 
 /* Called from IO context */
 static void playback_stream_request_bytes(playback_stream *s) {
-    size_t m, previous_missing, minreq;
+    size_t m, minreq;
+    int previous_missing;
 
     playback_stream_assert_ref(s);
 
@@ -1108,11 +1106,11 @@ 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);
+    previous_missing = pa_atomic_add(&s->missing, (int) m);
     minreq = pa_memblockq_get_minreq(s->memblockq);
 
     if (pa_memblockq_prebuf_active(s->memblockq) ||
-        (previous_missing < minreq && previous_missing+m >= minreq))
+        (previous_missing < (int) minreq && previous_missing + (int) m >= (int) minreq))
         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
 }
 
@@ -1297,7 +1295,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
             int64_t windex;
 
             windex = pa_memblockq_get_write_index(s->memblockq);
-            pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+
+            /* The client side is incapable of accounting correctly
+             * for seeks of a type != PA_SEEK_RELATIVE. We need to be
+             * able to deal with that. */
+
+            pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
 
             handle_seek(s, windex);
             return 0;
@@ -1315,7 +1318,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
             if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
                 pa_log_warn("Failed to push data into queue");
                 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
-                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
+                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
             }
 
             handle_seek(s, windex);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 0ed16dd..1fdb3fa 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -631,7 +631,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
              * data, so let's just hand out silence */
             pa_atomic_store(&i->thread_info.drained, 1);
 
-            pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE);
+            pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE, TRUE);
             i->thread_info.playing_for = 0;
             if (i->thread_info.underrun_for != (uint64_t) -1)
                 i->thread_info.underrun_for += ilength;
@@ -776,7 +776,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
 
             if (amount > 0)
                 /* Ok, now update the write pointer */
-                pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+                pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE, TRUE);
 
             if (i->thread_info.rewrite_flush)
                 pa_memblockq_silence(i->thread_info.render_memblockq);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 27f24cd..1c37be9 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -434,7 +434,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
 
     if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
         pa_log_debug("Delay queue overflow!");
-        pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
+        pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
     }
 
     limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c
index 127fb19..ec3f542 100644
--- a/src/tests/memblockq-test.c
+++ b/src/tests/memblockq-test.c
@@ -105,45 +105,45 @@ int main(int argc, char *argv[]) {
     ret = pa_memblockq_push(bq, &chunk4);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, -6, 0);
+    pa_memblockq_seek(bq, -6, 0, TRUE);
     ret = pa_memblockq_push(bq, &chunk3);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, -2, 0);
+    pa_memblockq_seek(bq, -2, 0, TRUE);
     ret = pa_memblockq_push(bq, &chunk1);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, -10, 0);
+    pa_memblockq_seek(bq, -10, 0, TRUE);
     ret = pa_memblockq_push(bq, &chunk4);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, 10, 0);
+    pa_memblockq_seek(bq, 10, 0, TRUE);
 
     ret = pa_memblockq_push(bq, &chunk1);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, -6, 0);
+    pa_memblockq_seek(bq, -6, 0, TRUE);
     ret = pa_memblockq_push(bq, &chunk2);
     assert(ret == 0);
 
     /* Test splitting */
-    pa_memblockq_seek(bq, -12, 0);
+    pa_memblockq_seek(bq, -12, 0, TRUE);
     ret = pa_memblockq_push(bq, &chunk1);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, 20, 0);
+    pa_memblockq_seek(bq, 20, 0, TRUE);
 
     /* Test merging */
     ret = pa_memblockq_push(bq, &chunk3);
     assert(ret == 0);
-    pa_memblockq_seek(bq, -2, 0);
+    pa_memblockq_seek(bq, -2, 0, TRUE);
 
     chunk3.index += 2;
     chunk3.length -= 2;
     ret = pa_memblockq_push(bq, &chunk3);
     assert(ret == 0);
 
-    pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE);
+    pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE, TRUE);
 
     dump(bq);
 

commit 1c26d7e174ba54c062c0ae50d33bf4e819bb15ba
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Apr 1 23:05:43 2009 +0200

    plot the difference between system and sound card time

diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index dd24e82..c2c3efe 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -162,11 +162,12 @@ int main(int argc, char *argv[]) {
             pa_bool_t cork_now;
 
             rtc = pa_timeval_diff(&now, &start);
-            printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k,
+            printf("%i\t%llu\t%llu\t%llu\t%llu\t%lli\t%u\t%u\n", k,
                    (unsigned long long) rtc,
                    (unsigned long long) t,
                    (unsigned long long) (rtc-old_rtc),
                    (unsigned long long) (t-old_t),
+                   (signed long long) rtc - (signed long long) t,
                    changed,
                    playing);
 

commit d035f4a3f3e73fd67d69a03c03adc9a08f30ef21
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Apr 5 02:26:02 2009 +0200

    Modify smoothing code to make cubic interpolation optional and allow 'quick fixups' on resuming
    
    The primary reason for this change is to allow time graphs that do not
    go through the origin and hence smoothing starting from the origin is
    not desired. This change will allow passing time data into the smoother
    while paused and then abruptly use that data without smoothing using the
    'quick fixup' flag when resuming.
    
    Primary use case is allowing recording time graphs where the data
    recorded originates from a time before the stream was created. The
    resulting graft will be shifted and should not be smoothened to go
    through the origin.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 0dc0e2b..942b59e 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1286,7 +1286,7 @@ static void thread_func(void *userdata) {
                     pa_log_info("Starting playback.");
                     snd_pcm_start(u->pcm_handle);
 
-                    pa_smoother_resume(u->smoother, pa_rtclock_usec());
+                    pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
                 }
 
                 update_smoother(u);
@@ -1495,7 +1495,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     snd_pcm_uframes_t period_frames, tsched_frames;
     size_t frame_size;
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
-    pa_usec_t usec;
     pa_sink_new_data data;
 
     pa_assert(m);
@@ -1559,10 +1558,14 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
-    u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5);
-    usec = pa_rtclock_usec();
-    pa_smoother_set_time_offset(u->smoother, usec);
-    pa_smoother_pause(u->smoother, usec);
+    u->smoother = pa_smoother_new(
+            DEFAULT_TSCHED_BUFFER_USEC*2,
+            DEFAULT_TSCHED_BUFFER_USEC*2,
+            TRUE,
+            TRUE,
+            5,
+            pa_rtclock_usec(),
+            TRUE);
 
     if (reserve_init(u, pa_modargs_get_value(
                              ma, "device_id",
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 348cd08..a5d2e29 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -776,7 +776,7 @@ static int unsuspend(struct userdata *u) {
     /* FIXME: We need to reload the volume somehow */
 
     snd_pcm_start(u->pcm_handle);
-    pa_smoother_resume(u->smoother, pa_rtclock_usec());
+    pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
 
     pa_log_info("Resumed successfully...");
 
@@ -1416,8 +1416,14 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
     u->alsa_rtpoll_item = NULL;
 
-    u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC*2, DEFAULT_TSCHED_WATERMARK_USEC*2, TRUE, 5);
-    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+    u->smoother = pa_smoother_new(
+            DEFAULT_TSCHED_WATERMARK_USEC*2,
+            DEFAULT_TSCHED_WATERMARK_USEC*2,
+            TRUE,
+            TRUE,
+            5,
+            pa_rtclock_usec(),
+            FALSE);
 
     if (reserve_init(u, pa_modargs_get_value(
                              ma, "device_id",
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 96b95b4..4613172 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -869,7 +869,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
                         if (start_stream_fd(u) < 0)
                             failed = TRUE;
 
-                    pa_smoother_resume(u->read_smoother, pa_rtclock_usec());
+                    pa_smoother_resume(u->read_smoother, pa_rtclock_usec(), TRUE);
                     break;
 
                 case PA_SOURCE_UNLINKED:
@@ -1965,7 +1965,14 @@ int pa__init(pa_module* m) {
     u->core = m->core;
     u->service_fd = -1;
     u->stream_fd = -1;
-    u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->read_smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            TRUE,
+            TRUE,
+            10,
+            0,
+            FALSE);
     u->sample_spec = m->core->default_sample_spec;
     u->modargs = ma;
 
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index b7e18bc..a1ef8da 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -664,7 +664,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED)
                 pa_smoother_pause(u->thread_info.smoother, pa_rtclock_usec());
             else
-                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec());
+                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_usec(), TRUE);
 
             break;
 
@@ -1043,7 +1043,14 @@ int pa__init(pa_module*m) {
     pa_atomic_store(&u->thread_info.running, FALSE);
     u->thread_info.in_null_mode = FALSE;
     u->thread_info.counter = 0;
-    u->thread_info.smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->thread_info.smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            TRUE,
+            TRUE,
+            10,
+            0,
+            FALSE);
 
     if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) {
         pa_log("Failed to parse adjust_time value");
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 5c47f44..a1a783a 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -150,7 +150,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                 case PA_SINK_RUNNING:
 
                     if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
-                        pa_smoother_resume(u->smoother, pa_rtclock_usec());
+                        pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
 
                     break;
 
@@ -545,7 +545,14 @@ int pa__init(pa_module*m) {
     u->module = m;
     m->userdata = u;
     u->fd = -1;
-    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            TRUE,
+            TRUE,
+            10,
+            0,
+            FALSE);
     pa_memchunk_reset(&u->memchunk);
     u->offset = 0;
 
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index d8ddf18..4d68b1b 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -192,7 +192,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                 case PA_SINK_RUNNING:
 
                     if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                        pa_smoother_resume(u->smoother, pa_rtclock_usec());
+                        pa_smoother_resume(u->smoother, pa_rtclock_usec(), TRUE);
 
                         /* The connection can be closed when idle, so check to
                            see if we need to reestablish it */
@@ -540,7 +540,14 @@ int pa__init(pa_module*m) {
     u->module = m;
     m->userdata = u;
     u->fd = -1;
-    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            TRUE,
+            TRUE,
+            10,
+            0,
+            FALSE);
     pa_memchunk_reset(&u->raw_memchunk);
     pa_memchunk_reset(&u->encoded_memchunk);
     u->offset = 0;
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 1d658ba..5ea58aa 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -405,7 +405,7 @@ static void check_smoother_status(struct userdata *u, pa_bool_t past)  {
     if (u->remote_suspended || u->remote_corked)
         pa_smoother_pause(u->smoother, x);
     else
-        pa_smoother_resume(u->smoother, x);
+        pa_smoother_resume(u->smoother, x, TRUE);
 }
 
 /* Called from IO thread context */
@@ -1815,7 +1815,14 @@ int pa__init(pa_module*m) {
     u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
     u->source = NULL;
 #endif
-    u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+    u->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC,
+            PA_USEC_PER_SEC*2,
+            TRUE,
+            TRUE,
+            10,
+            pa_rtclock_usec(),
+            FALSE);
     u->ctag = 1;
     u->device_index = u->channel = PA_INVALID_INDEX;
     u->time_event = NULL;
@@ -1933,8 +1940,6 @@ int pa__init(pa_module*m) {
     u->fragsize = (uint32_t) -1;
 #endif
 
-    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
-
     if (!(u->thread = pa_thread_new(thread_func, u))) {
         pa_log("Failed to create thread.");
         goto fail;
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 209770f..445941c 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -430,8 +430,14 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
     s->sdp_info = *sdp_info;
     s->rtpoll_item = NULL;
     s->intended_latency = LATENCY_USEC;
-    s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10);
-    pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now));
+    s->smoother = pa_smoother_new(
+            PA_USEC_PER_SEC*5,
+            PA_USEC_PER_SEC*2,
+            TRUE,
+            TRUE,
+            10,
+            pa_timeval_load(&now),
+            FALSE);
     s->last_rate_update = pa_timeval_load(&now);
     pa_atomic_store(&s->timestamp, (int) now.tv_sec);
 
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index c4a54af..ff3644f 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -393,7 +393,8 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t
     if (s->suspended || s->corked || force_stop)
         pa_smoother_pause(s->smoother, x);
     else if (force_start || s->buffer_attr.prebuf == 0)
-        pa_smoother_resume(s->smoother, x);
+        pa_smoother_resume(s->smoother, x, TRUE);
+
 
     /* Please note that we have no idea if playback actually started
      * if prebuf is non-zero! */
@@ -1064,14 +1065,17 @@ static int create_stream(
     if (flags & PA_STREAM_INTERPOLATE_TIMING) {
         pa_usec_t x;
 
-        if (s->smoother)
-            pa_smoother_free(s->smoother);
-
-        s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONIC), SMOOTHER_MIN_HISTORY);
-
         x = pa_rtclock_usec();
-        pa_smoother_set_time_offset(s->smoother, x);
-        pa_smoother_pause(s->smoother, x);
+
+        pa_assert(!s->smoother);
+        s->smoother = pa_smoother_new(
+                SMOOTHER_ADJUST_TIME,
+                SMOOTHER_HISTORY_TIME,
+                !(flags & PA_STREAM_NOT_MONOTONIC),
+                TRUE,
+                SMOOTHER_MIN_HISTORY,
+                x,
+                TRUE);
     }
 
     if (!dev)
@@ -1623,7 +1627,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command,
                 pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE));
 
             if (i->playing)
-                pa_smoother_resume(o->stream->smoother, x);
+                pa_smoother_resume(o->stream->smoother, x, TRUE);
         }
     }
 
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 6562194..55ac868 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -78,17 +78,26 @@ struct pa_smoother {
 
     /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
     double a, b, c;
-    pa_bool_t abc_valid;
+    pa_bool_t abc_valid:1;
 
     pa_bool_t monotonic:1;
     pa_bool_t paused:1;
+    pa_bool_t smoothing:1; /* If FALSE we skip the polonyomial interpolation step */
 
     pa_usec_t pause_time;
 
     unsigned min_history;
 };
 
-pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
+pa_smoother* pa_smoother_new(
+        pa_usec_t adjust_time,
+        pa_usec_t history_time,
+        pa_bool_t monotonic,
+        pa_bool_t smoothing,
+        unsigned min_history,
+        pa_usec_t time_offset,
+        pa_bool_t paused) {
+
     pa_smoother *s;
 
     pa_assert(adjust_time > 0);
@@ -116,9 +125,13 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b
     s->abc_valid = FALSE;
 
     s->paused = FALSE;
+    s->smoothing = smoothing;
 
     s->min_history = min_history;
 
+    s->paused = paused;
+    s->time_offset = s->pause_time = time_offset;
+
     return s;
 }
 
@@ -278,7 +291,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
     pa_assert(s);
     pa_assert(y);
 
-    if (x >= s->px) {
+    if (!s->smoothing || x >= s->px) {
         int64_t t;
 
         /* The requested point is right of the point where we wanted
@@ -348,7 +361,6 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
          * we can adjust our position smoothly from this one */
         estimate(s, x, &ney, &nde);
         s->ex = x; s->ey = ney; s->de = nde;
-
         s->ry = y;
     }
 
@@ -359,8 +371,13 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
     s->dp = avg_gradient(s, x);
 
     /* And calculate when we want to be on track again */
-    s->px = s->ex + s->adjust_time;
-    s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+    if (s->smoothing) {
+        s->px = s->ex + s->adjust_time;
+        s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+    } else {
+        s->px = s->ex;
+        s->py = s->ry;
+    }
 
     s->abc_valid = FALSE;
 
@@ -420,7 +437,7 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
     s->pause_time = x;
 }
 
-void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t fix_now) {
     pa_assert(s);
 
     if (!s->paused)
@@ -433,6 +450,16 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
 
     s->paused = FALSE;
     s->time_offset += x - s->pause_time;
+
+    if (fix_now)
+        pa_smoother_fix_now(s);
+}
+
+void pa_smoother_fix_now(pa_smoother *s) {
+    pa_assert(s);
+
+    s->px = s->ex;
+    s->py = s->ry;
 }
 
 pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
index 2051e64..5244a7e 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -27,7 +27,15 @@
 
 typedef struct pa_smoother pa_smoother;
 
-pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
+pa_smoother* pa_smoother_new(
+        pa_usec_t x_adjust_time,
+        pa_usec_t x_history_time,
+        pa_bool_t monotonic,
+        pa_bool_t smoothing,
+        unsigned min_history,
+        pa_usec_t x_offset,
+        pa_bool_t paused);
+
 void pa_smoother_free(pa_smoother* s);
 
 /* Adds a new value to our dataset. x = local/system time, y = remote time */
@@ -42,8 +50,10 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
 void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
 
 void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
-void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t abrupt);
 
 void pa_smoother_reset(pa_smoother *s);
 
+void pa_smoother_fix_now(pa_smoother *s);
+
 #endif
diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c
index 798dfed..2cc9f58 100644
--- a/src/tests/smoother-test.c
+++ b/src/tests/smoother-test.c
@@ -45,10 +45,12 @@ int main(int argc, char*argv[]) {
 
     srand(0);
 
+    pa_log_set_level(PA_LOG_DEBUG);
+
     for (m = 0, u = 0; u < PA_ELEMENTSOF(msec); u+= 2) {
 
         msec[u] = m+1 + (rand() % 100) - 50;
-        msec[u+1] = m + (rand() % 2000) - 1000;
+        msec[u+1] = m + (rand() % 2000) - 1000   + 5000;
 
         m += rand() % 100;
 
@@ -59,7 +61,7 @@ int main(int argc, char*argv[]) {
             msec[u+1] = 0;
     }
 
-    s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE, 6);
+    s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, FALSE, TRUE, 6, 0, TRUE);
 
     for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) {
 
@@ -67,6 +69,8 @@ int main(int argc, char*argv[]) {
             pa_smoother_put(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, (pa_usec_t) msec[u+1] * PA_USEC_PER_MSEC);
             printf("%i\t\t%i\n", msec[u],  msec[u+1]);
             u += 2;
+
+            pa_smoother_resume(s, (pa_usec_t) msec[u] * PA_USEC_PER_MSEC, TRUE);
         }
 
         printf("%llu\t%llu\n", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC));

commit ce73e715c9c822beb797c023700c92bcc08464e7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Apr 5 02:46:38 2009 +0200

    introduce pa_{sink|source}_get_latency_within_thread()

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 73ad247..a0f0ea7 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -958,6 +958,32 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
     return usec;
 }
 
+/* Called from IO thread */
+pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
+    pa_usec_t usec = 0;
+    pa_msgobject *o;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (s->thread_info.state == PA_SINK_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SINK_LATENCY))
+        return 0;
+
+    o = PA_MSGOBJECT(s);
+
+    /* We probably should make this a proper vtable callback instead of going through process_msg() */
+
+    if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+        return -1;
+
+    return usec;
+}
+
 /* Called from main thread */
 void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
     pa_sink_input *i;
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 7d1e11e..634bf3e 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -295,4 +295,6 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
 
 void pa_sink_invalidate_requested_latency(pa_sink *s);
 
+pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
+
 #endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 1c3377b..252e23a 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -617,6 +617,32 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
     return usec;
 }
 
+/* Called from IO thread */
+pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
+    pa_usec_t usec = 0;
+    pa_msgobject *o;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    /* The returned value is supposed to be in the time domain of the sound card! */
+
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return 0;
+
+    if (!(s->flags & PA_SOURCE_LATENCY))
+        return 0;
+
+    o = PA_MSGOBJECT(s);
+
+    /* We probably should make this a proper vtable callback instead of going through process_msg() */
+
+    if (o->process_msg(o, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+        return -1;
+
+    return usec;
+}
+
 /* Called from main thread */
 void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
     pa_cvolume old_virtual_volume;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 210f534..652783e 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -267,5 +267,6 @@ void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_laten
 /*** To be called exclusively by source output drivers, from IO context */
 
 void pa_source_invalidate_requested_latency(pa_source *s);
+pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
 
 #endif

commit 7dafa87b68c59aeb93c7244b33d9dc9f6e614970
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Apr 5 02:50:32 2009 +0200

    don't try to outsmart the transport

diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index ff3644f..339a89e 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -380,14 +380,6 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t
             x -= s->timing_info.transport_usec;
         else
             x += s->timing_info.transport_usec;
-
-        if (s->direction == PA_STREAM_PLAYBACK)
-            /* it takes a while until the pause/resume is actually
-             * audible */
-            x += s->timing_info.sink_usec;
-        else
-            /* Data froma  while back will be dropped */
-            x -= s->timing_info.source_usec;
     }
 
     if (s->suspended || s->corked || force_stop)

commit 14e11e4533ddd60413af2d33325d2b603262029b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Apr 5 02:59:02 2009 +0200

    Fix a couple of races in native protocol
    
    Also make sure we account for recording memblock that are currently 'on
    the fly' between the main and the IO thread.
    
    Also makes a couple of timing calls that were done in different calls
    in a single inter-thread call. That way there is a better guarantee that
    they match up.

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 30d68f3..59e5d80 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -86,7 +86,15 @@ typedef struct record_stream {
     pa_bool_t early_requests:1;
 
     pa_buffer_attr buffer_attr;
-    pa_usec_t source_latency;
+
+    pa_atomic_t on_the_fly;
+    pa_usec_t configured_source_latency;
+    size_t drop_initial;
+
+    /* Only updated after SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY */
+    size_t on_the_fly_snapshot;
+    pa_usec_t current_monitor_latency;
+    pa_usec_t current_source_latency;
 } record_stream;
 
 PA_DECLARE_CLASS(record_stream);
@@ -119,12 +127,14 @@ typedef struct playback_stream {
     uint32_t syncid;
 
     pa_atomic_t missing;
-    pa_usec_t sink_latency;
+    pa_usec_t configured_sink_latency;
     pa_buffer_attr buffer_attr;
 
     /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
     int64_t read_index, write_index;
     size_t render_memblockq_length;
+    pa_usec_t current_sink_latency;
+    uint64_t playing_for, underrun_for;
 } playback_stream;
 
 PA_DECLARE_CLASS(playback_stream);
@@ -182,6 +192,10 @@ struct pa_native_protocol {
 };
 
 enum {
+    SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+enum {
     SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
     SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
     SINK_INPUT_MESSAGE_FLUSH,
@@ -230,6 +244,7 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
 static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
 static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -474,6 +489,10 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
 
         case RECORD_STREAM_MESSAGE_POST_DATA:
 
+            /* We try to keep up to date with how many bytes are
+             * currently on the fly */
+            pa_atomic_sub(&s->on_the_fly, chunk->length);
+
             if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
 /*                 pa_log_warn("Failed to push data into output queue."); */
                 return -1;
@@ -537,29 +556,29 @@ static void fix_record_buffer_attr_pre(record_stream *s) {
         /* Ok, the user didn't ask us to adjust the latency, hence we
          * don't */
 
-        source_usec = 0;
+        source_usec = (pa_usec_t) -1;
     }
 
-    if (source_usec > 0)
-        s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
+    if (source_usec != (pa_usec_t) -1)
+        s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
     else
-        s->source_latency = 0;
+        s->configured_source_latency = 0;
 
     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;
+        fragsize_usec = s->configured_source_latency;
 
     } else if (s->adjust_latency) {
 
         /* Now subtract what we actually got */
 
-        if (fragsize_usec >= s->source_latency*2)
-            fragsize_usec -= s->source_latency;
+        if (fragsize_usec >= s->configured_source_latency*2)
+            fragsize_usec -= s->configured_source_latency;
         else
-            fragsize_usec = s->source_latency;
+            fragsize_usec = s->configured_source_latency;
     }
 
     if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
@@ -645,7 +664,9 @@ static record_stream* record_stream_new(
     s->buffer_attr = *attr;
     s->adjust_latency = adjust_latency;
     s->early_requests = early_requests;
+    pa_atomic_store(&s->on_the_fly, 0);
 
+    s->source_output->parent.process_msg = source_output_process_msg;
     s->source_output->push = source_output_push_cb;
     s->source_output->kill = source_output_kill_cb;
     s->source_output->get_latency = source_output_get_latency_cb;
@@ -675,9 +696,9 @@ 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->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) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
                 (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
-                (double) s->source_latency / PA_USEC_PER_MSEC);
+                (double) s->configured_source_latency / PA_USEC_PER_MSEC);
 
     pa_source_output_put(s->source_output);
     return s;
@@ -817,7 +838,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
             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_tagstruct_put_usec(t, s->configured_sink_latency);
             pa_pstream_send_tagstruct(s->connection->pstream, t);
 
             break;
@@ -915,14 +936,14 @@ static void fix_playback_buffer_attr(playback_stream *s) {
         pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
     }
 
-    s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+    s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
 
     if (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;
+        minreq_usec = s->configured_sink_latency;
 
     } else if (s->adjust_latency) {
 
@@ -930,14 +951,14 @@ static void fix_playback_buffer_attr(playback_stream *s) {
          * let's subtract from what we asked for for the remaining
          * buffer space */
 
-        if (tlength_usec >= s->sink_latency)
-            tlength_usec -= s->sink_latency;
+        if (tlength_usec >= s->configured_sink_latency)
+            tlength_usec -= s->configured_sink_latency;
     }
 
     /* FIXME: This is actually larger than necessary, since not all of
      * the sink latency is actually rewritable. */
-    if (tlength_usec < s->sink_latency + 2*minreq_usec)
-        tlength_usec = s->sink_latency + 2*minreq_usec;
+    if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
+        tlength_usec = s->configured_sink_latency + 2*minreq_usec;
 
     if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
         pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
@@ -1083,10 +1104,10 @@ 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(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, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
                 (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
                 (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
-                (double) s->sink_latency / PA_USEC_PER_MSEC);
+                (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
 
     pa_sink_input_put(s->sink_input);
     return s;
@@ -1387,10 +1408,14 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
         }
 
         case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
-
+            /* Atomically get a snapshot of all timing parameters... */
             s->read_index = pa_memblockq_get_read_index(s->memblockq);
             s->write_index = pa_memblockq_get_write_index(s->memblockq);
             s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+            s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink);
+            s->underrun_for = s->sink_input->thread_info.underrun_for;
+            s->playing_for = s->sink_input->thread_info.playing_for;
+
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_SET_STATE: {
@@ -1603,7 +1628,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
         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_tagstruct_put_usec(t, s->configured_sink_latency);
     }
 
     pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1612,6 +1637,27 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
 /*** source_output callbacks ***/
 
 /* Called from thread context */
+static int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_source_output *o = PA_SOURCE_OUTPUT(_o);
+    record_stream *s;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    switch (code) {
+        case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
+            /* Atomically get a snapshot of all timing parameters... */
+            s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of) : 0;
+            s->current_source_latency = pa_source_get_latency_within_thread(o->source);
+            s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
+            return 0;
+    }
+
+    return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
+}
+
+/* Called from thread context */
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
     record_stream *s;
 
@@ -1620,6 +1666,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
     record_stream_assert_ref(s);
     pa_assert(chunk);
 
+    pa_atomic_add(&s->on_the_fly, chunk->length);
     pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
 }
 
@@ -1715,7 +1762,7 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
     if (s->connection->version >= 13) {
         pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
         pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
-        pa_tagstruct_put_usec(t, s->source_latency);
+        pa_tagstruct_put_usec(t, s->configured_source_latency);
     }
 
     pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1938,7 +1985,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     }
 
     if (c->version >= 13)
-        pa_tagstruct_put_usec(reply, s->sink_latency);
+        pa_tagstruct_put_usec(reply, s->configured_sink_latency);
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
@@ -2185,7 +2232,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     }
 
     if (c->version >= 13)
-        pa_tagstruct_put_usec(reply, s->source_latency);
+        pa_tagstruct_put_usec(reply, s->configured_source_latency);
 
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
@@ -2472,7 +2519,6 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
     playback_stream *s;
     struct timeval tv, now;
     uint32_t idx;
-    pa_usec_t latency;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2488,25 +2534,27 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
     s = pa_idxset_get_by_index(c->output_streams, idx);
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
     CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
-    CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
-
-    reply = reply_new(tag);
 
-    latency = pa_sink_get_latency(s->sink_input->sink);
-    latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
-
-    pa_tagstruct_put_usec(reply, latency);
+    /* Get an atomic snapshot of all timing parameters */
+    pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
 
+    reply = reply_new(tag);
+    pa_tagstruct_put_usec(reply,
+                          s->current_sink_latency +
+                          pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec));
     pa_tagstruct_put_usec(reply, 0);
-    pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
+    pa_tagstruct_put_boolean(reply,
+                             s->playing_for > 0 &&
+                             pa_sink_get_state(s->sink_input->sink) == PA_SINK_RUNNING &&
+                             pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, s->write_index);
     pa_tagstruct_puts64(reply, s->read_index);
 
     if (c->version >= 13) {
-        pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
-        pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+        pa_tagstruct_putu64(reply, s->underrun_for);
+        pa_tagstruct_putu64(reply, s->playing_for);
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
@@ -2533,10 +2581,17 @@ static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint3
     s = pa_idxset_get_by_index(c->record_streams, idx);
     CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 
+    /* Get an atomic snapshot of all timing parameters */
+    pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+
     reply = reply_new(tag);
-    pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
-    pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
-    pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
+    pa_tagstruct_put_usec(reply, s->current_monitor_latency);
+    pa_tagstruct_put_usec(reply,
+                          s->current_source_latency +
+                          pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
+    pa_tagstruct_put_boolean(reply,
+                             pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING &&
+                             pa_source_output_get_state(s->source_output) == PA_SOURCE_OUTPUT_RUNNING);
     pa_tagstruct_put_timeval(reply, &tv);
     pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
     pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -3514,7 +3569,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
         pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
 
         if (c->version >= 13)
-            pa_tagstruct_put_usec(reply, s->sink_latency);
+            pa_tagstruct_put_usec(reply, s->configured_sink_latency);
 
     } else {
         record_stream *s;
@@ -3550,7 +3605,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
         pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
 
         if (c->version >= 13)
-            pa_tagstruct_put_usec(reply, s->source_latency);
+            pa_tagstruct_put_usec(reply, s->configured_source_latency);
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);

commit 923c5bc5bfba15399649ebc50807c27765102e6e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Apr 5 03:04:22 2009 +0200

    make it easy to disable interpolation in the interpolation test tool

diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index c2c3efe..c103a49 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -36,6 +36,8 @@
 
 #include <pulsecore/thread.h>
 
+#define INTERPOLATE
+
 static pa_context *context = NULL;
 static pa_stream *stream = NULL;
 static pa_mainloop_api *mainloop_api = NULL;
@@ -58,6 +60,15 @@ static void stream_read_cb(pa_stream *p, size_t nbytes, void *userdata) {
     }
 }
 
+static void stream_latency_cb(pa_stream *p, void *userdata) {
+#ifndef INTERPOLATE
+    pa_operation *o;
+
+    o = pa_stream_update_timing_info(p, NULL, NULL);
+    pa_operation_unref(o);
+#endif
+}
+
 /* This is called whenever the context status changes */
 static void context_state_callback(pa_context *c, void *userdata) {
     assert(c);
@@ -69,6 +80,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
             break;
 
         case PA_CONTEXT_READY: {
+            pa_stream_flags_t flags = PA_STREAM_AUTO_TIMING_UPDATE;
 
             static const pa_sample_spec ss = {
                 .format = PA_SAMPLE_S16LE,
@@ -76,19 +88,25 @@ static void context_state_callback(pa_context *c, void *userdata) {
                 .channels = 2
             };
 
+#ifdef INTERPOLATE
+            flags |= PA_STREAM_INTERPOLATE_TIMING;
+#endif
+
             fprintf(stderr, "Connection established.\n");
 
             stream = pa_stream_new(c, "interpol-test", &ss, NULL);
             assert(stream);
 
             if (playback) {
-                pa_assert_se(pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) == 0);
+                pa_assert_se(pa_stream_connect_playback(stream, NULL, NULL, flags, NULL, NULL) == 0);
                 pa_stream_set_write_callback(stream, stream_write_cb, NULL);
             } else {
-                pa_assert_se(pa_stream_connect_record(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE) == 0);
+                pa_assert_se(pa_stream_connect_record(stream, NULL, NULL, flags) == 0);
                 pa_stream_set_read_callback(stream, stream_read_cb, NULL);
             }
 
+            pa_stream_set_latency_update_callback(stream, stream_latency_cb, NULL);
+
             break;
         }
 
@@ -109,6 +127,8 @@ int main(int argc, char *argv[]) {
     pa_usec_t old_t = 0, old_rtc = 0;
     pa_bool_t corked = FALSE;
 
+    pa_log_set_level(PA_LOG_DEBUG);
+
     playback = argc <= 1 || !pa_streq(argv[1], "-r");
 
     /* Set up a new main loop */

commit 6ba3333030fa44fe43e3795be8af7473113f6109
Merge: 923c5bc ca39fa2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Apr 5 03:05:51 2009 +0200

    Merge branch 'master' of ssh://rootserver/home/lennart/git/public/pulseaudio


-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list