From 49b3150434ee97fbcee33948053ffbf33a7b5505 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Apr 2006 17:17:23 +0000 Subject: * rename "latency correction" to "write index correction" * add read index invalidation code * rename "ipol_event" stuff to "auto_timing_update" * remove buffer_usec field from pa_timing_info, since it can be easily calculated from write_index and read_index anyway * add read_index_corrupt field to "pa_timing_info", similar to the already existing write_index_corrupt field * restart automatic timing update event every time a query is issued, not just when the last event elapsed * proper invalidation code for pa_stream_flush() * do tarsnport/sink/source latency correction for playback time only when device is not corked git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@686 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/polyp/def.h | 23 ++-- src/polyp/internal.h | 21 ++-- src/polyp/stream.c | 306 +++++++++++++++++++++++++++++++-------------------- 3 files changed, 216 insertions(+), 134 deletions(-) (limited to 'src/polyp') diff --git a/src/polyp/def.h b/src/polyp/def.h index eadddf08..432bd8ce 100644 --- a/src/polyp/def.h +++ b/src/polyp/def.h @@ -204,13 +204,14 @@ typedef enum pa_subscription_event_type { * pa_stream_update_timing_info() and pa_stream_get_timing_info(). The * total output latency a sample that is written with * pa_stream_write() takes to be played may be estimated by - * sink_usec+buffer_usec+transport_usec. The output buffer to which - * buffer_usec relates may be manipulated freely (with + * sink_usec+buffer_usec+transport_usec. (where buffer_usec is defined + * as pa_bytes_to_usec(write_index-read_index)) The output buffer + * which buffer_usec relates to may be manipulated freely (with * pa_stream_write()'s seek argument, pa_stream_flush() and friends), * the buffers sink_usec and source_usec relate to are first-in - * first-out (FIFO) buffers which cannot be flushed or manipulated in any - * way. The total input latency a sample that is recorded takes to be - * delivered to the application is: + * first-out (FIFO) buffers which cannot be flushed or manipulated in + * any way. The total input latency a sample that is recorded takes to + * be delivered to the application is: * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of * sign issues!) When connected to a monitor source sink_usec contains * the latency of the owning sink. The two latency estimations @@ -226,7 +227,6 @@ typedef struct pa_timing_info { * limited und unreliable itself. \since * 0.5 */ - pa_usec_t buffer_usec; /**< Time in usecs the current buffer takes to play. For both playback and record streams. */ pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */ @@ -240,14 +240,21 @@ typedef struct pa_timing_info { * info was current . Only write * commands with SEEK_RELATIVE_ON_READ * and SEEK_RELATIVE_END can corrupt - * write_index. */ + * write_index. \since 0.8 */ int64_t write_index; /**< Current write index into the * playback buffer in bytes. Think twice before * using this for seeking purposes: it * might be out of date a the time you * want to use it. Consider using * PA_SEEK_RELATIVE instead. \since - * 0.8 */ + * 0.8 */ + + int read_index_corrupt; /**< Non-zero if read_index is not + * up-to-date because a local pause or + * flush request that corrupted it has + * been issued in the time since this + * latency info was current. \since 0.8 */ + int64_t read_index; /**< Current read index into the * playback buffer in bytes. Think twice before * using this for seeking purposes: it diff --git a/src/polyp/internal.h b/src/polyp/internal.h index 3b85b3d1..2bec0294 100644 --- a/src/polyp/internal.h +++ b/src/polyp/internal.h @@ -83,14 +83,14 @@ struct pa_context { pa_client_conf *conf; }; -#define PA_MAX_LATENCY_CORRECTIONS 10 +#define PA_MAX_WRITE_INDEX_CORRECTIONS 10 -typedef struct pa_latency_correction { +typedef struct pa_index_correction { uint32_t tag; int valid; int64_t value; int absolute, corrupt; -} pa_latency_correction; +} pa_index_correction; struct pa_stream { int ref; @@ -124,13 +124,18 @@ struct pa_stream { /* Use to make sure that time advances monotonically */ pa_usec_t previous_time; - /* Latency correction stuff */ - pa_latency_correction latency_corrections[PA_MAX_LATENCY_CORRECTIONS]; - int idx_latency_correction; + /* time updates with tags older than these are invalid */ + uint32_t write_index_not_before; + uint32_t read_index_not_before; + + /* Data about individual timing update correctoins */ + pa_index_correction write_index_corrections[PA_MAX_WRITE_INDEX_CORRECTIONS]; + int current_write_index_correction; /* Latency interpolation stuff */ - pa_time_event *ipol_event; - int ipol_requested; + pa_time_event *auto_timing_update_event; + int auto_timing_update_requested; + pa_usec_t ipol_usec; int ipol_usec_valid; struct timeval ipol_timestamp; diff --git a/src/polyp/stream.c b/src/polyp/stream.c index ca107750..b3f19d09 100644 --- a/src/polyp/stream.c +++ b/src/polyp/stream.c @@ -90,19 +90,22 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->previous_time = 0; s->timing_info_valid = 0; + s->read_index_not_before = 0; + s->write_index_not_before = 0; + for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++) + s->write_index_corrections[i].valid = 0; + s->current_write_index_correction = 0; + s->corked = 0; s->ipol_usec_valid = 0; s->ipol_timestamp.tv_sec = 0; s->ipol_timestamp.tv_usec = 0; - s->ipol_event = NULL; - s->ipol_requested = 0; - - for (i = 0; i < PA_MAX_LATENCY_CORRECTIONS; i++) - s->latency_corrections[i].valid = 0; - s->idx_latency_correction = 0; + s->auto_timing_update_event = NULL; + s->auto_timing_update_requested = 0; + PA_LLIST_PREPEND(pa_stream, c->streams, s); /* The context and stream will point at each other. We cannot ref count @@ -119,9 +122,9 @@ static void stream_free(pa_stream *s) { pa_context_unref(s->context); - if (s->ipol_event) { + if (s->auto_timing_update_event) { assert(s->mainloop); - s->mainloop->time_free(s->ipol_event); + s->mainloop->time_free(s->auto_timing_update_event); } if (s->peek_memchunk.memblock) @@ -296,34 +299,77 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC } } -finish: + finish: pa_context_unref(c); } -static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { +static void request_auto_timing_update(pa_stream *s, int force) { struct timeval next; - pa_stream *s = userdata; - - pa_stream_ref(s); + assert(s); -/* pa_log("requesting new ipol data"); */ + if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE)) + return; - if (s->state == PA_STREAM_READY && !s->ipol_requested) { + if (s->state == PA_STREAM_READY && + (force || !s->auto_timing_update_requested)) { pa_operation *o; +/* pa_log("automatically requesting new timing data"); */ + if ((o = pa_stream_update_timing_info(s, NULL, NULL))) { pa_operation_unref(o); - s->ipol_requested = 1; + s->auto_timing_update_requested = 1; } } - + pa_gettimeofday(&next); pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); - m->time_restart(e, &next); + s->mainloop->time_restart(s->auto_timing_update_event, &next); +} + +static void invalidate_indexes(pa_stream *s, int r, int w) { + assert(s); + + pa_log("invalidate r:%u w:%u tag:%u", r, w, s->context->ctag); - pa_stream_unref(s); + if (s->state != PA_STREAM_READY) + return; + + if (w) { + s->write_index_not_before = s->context->ctag; + + if (s->timing_info_valid) + s->timing_info.write_index_corrupt = 1; + + pa_log("write_index invalidated"); + + } + + if (r) { + s->read_index_not_before = s->context->ctag; + + if (s->timing_info_valid) + s->timing_info.read_index_corrupt = 1; + + pa_log("read_index invalidated"); + } + + if ((s->direction == PA_STREAM_PLAYBACK && r) || + (s->direction == PA_STREAM_RECORD && w)) + s->ipol_usec_valid = 0; + + request_auto_timing_update(s, 1); } +static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { + pa_stream *s = userdata; + + pa_log("time event"); + + pa_stream_ref(s); + request_auto_timing_update(s, 0); + pa_stream_unref(s); +} void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s = userdata; @@ -371,6 +417,8 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED /* We add an extra ref as long as we're connected (i.e. in the dynarray) */ pa_stream_ref(s); + pa_stream_set_state(s, PA_STREAM_READY); + if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { struct timeval tv; @@ -378,12 +426,12 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_gettimeofday(&tv); tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ - assert(!s->ipol_event); - s->ipol_event = s->mainloop->time_new(s->mainloop, &tv, &ipol_callback, s); + assert(!s->auto_timing_update_event); + s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + + request_auto_timing_update(s, 1); } - pa_stream_set_state(s, PA_STREAM_READY); - if (s->requested_bytes > 0 && s->ref > 1 && s->write_callback) s->write_callback(s, s->requested_bytes, s->write_userdata); @@ -548,18 +596,19 @@ int pa_stream_write( s->requested_bytes = 0; if (s->direction == PA_STREAM_PLAYBACK) { + /* Update latency request correction */ - if (s->latency_corrections[s->idx_latency_correction].valid) { + if (s->write_index_corrections[s->current_write_index_correction].valid) { if (seek == PA_SEEK_ABSOLUTE) { - s->latency_corrections[s->idx_latency_correction].corrupt = 0; - s->latency_corrections[s->idx_latency_correction].absolute = 1; - s->latency_corrections[s->idx_latency_correction].value = offset + length; + s->write_index_corrections[s->current_write_index_correction].corrupt = 0; + s->write_index_corrections[s->current_write_index_correction].absolute = 1; + s->write_index_corrections[s->current_write_index_correction].value = offset + length; } else if (seek == PA_SEEK_RELATIVE) { - if (!s->latency_corrections[s->idx_latency_correction].corrupt) - s->latency_corrections[s->idx_latency_correction].value += offset + length; + if (!s->write_index_corrections[s->current_write_index_correction].corrupt) + s->write_index_corrections[s->current_write_index_correction].value += offset + length; } else - s->latency_corrections[s->idx_latency_correction].corrupt = 1; + s->write_index_corrections[s->current_write_index_correction].corrupt = 1; } /* Update the write index in the already available latency data */ @@ -574,6 +623,9 @@ int pa_stream_write( } else s->timing_info.write_index_corrupt = 1; } + + if (!s->timing_info_valid || s->timing_info.write_index_corrupt) + request_auto_timing_update(s, 1); } return 0; @@ -672,15 +724,20 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, assert(o->context); i = &o->stream->timing_info; + + pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); + o->stream->timing_info_valid = 0; i->write_index_corrupt = 0; + i->read_index_corrupt = 0; + + pa_log("timing update %u\n", tag); if (command != PA_COMMAND_REPLY) { if (pa_context_handle_error(o->context, command, t) < 0) goto finish; - } else if (pa_tagstruct_get_usec(t, &i->buffer_usec) < 0 || - pa_tagstruct_get_usec(t, &i->sink_usec) < 0 || + } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 || pa_tagstruct_get_usec(t, &i->source_usec) < 0 || pa_tagstruct_get_boolean(t, &i->playing) < 0 || pa_tagstruct_get_timeval(t, &local) < 0 || @@ -692,8 +749,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; } else { + o->stream->timing_info_valid = 1; + pa_gettimeofday(&now); + /* Calculcate timestamps */ if (pa_timeval_cmp(&local, &remote) <= 0 && pa_timeval_cmp(&remote, &now) <= 0) { /* local and remote seem to have synchronized clocks */ @@ -712,6 +772,13 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, pa_timeval_add(&i->timestamp, i->transport_usec); } + /* Invalidate read and write indexes if necessary */ + if (tag < o->stream->read_index_not_before) + i->read_index_corrupt = 1; + + if (tag < o->stream->write_index_not_before) + i->write_index_corrupt = 1; + if (o->stream->direction == PA_STREAM_PLAYBACK) { /* Write index correction */ @@ -720,53 +787,54 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, /* Go through the saved correction values and add up the total correction.*/ - for (n = 0, j = o->stream->idx_latency_correction; - n < PA_MAX_LATENCY_CORRECTIONS; - n++, j = (j + 1) % PA_MAX_LATENCY_CORRECTIONS) { + for (n = 0, j = o->stream->current_write_index_correction+1; + n < PA_MAX_WRITE_INDEX_CORRECTIONS; + n++, j = (j + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS) { /* Step over invalid data or out-of-date data */ - if (!o->stream->latency_corrections[j].valid || - o->stream->latency_corrections[j].tag < ctag) + if (!o->stream->write_index_corrections[j].valid || + o->stream->write_index_corrections[j].tag < ctag) continue; /* Make sure that everything is in order */ - ctag = o->stream->latency_corrections[j].tag+1; + ctag = o->stream->write_index_corrections[j].tag+1; /* Now fix the write index */ - if (o->stream->latency_corrections[j].corrupt) { + if (o->stream->write_index_corrections[j].corrupt) { /* A corrupting seek was made */ i->write_index = 0; i->write_index_corrupt = 1; - } else if (o->stream->latency_corrections[j].absolute) { + } else if (o->stream->write_index_corrections[j].absolute) { /* An absolute seek was made */ - i->write_index = o->stream->latency_corrections[j].value; + i->write_index = o->stream->write_index_corrections[j].value; i->write_index_corrupt = 0; } else if (!i->write_index_corrupt) { /* A relative seek was made */ - i->write_index += o->stream->latency_corrections[j].value; + i->write_index += o->stream->write_index_corrections[j].value; } } } - - o->stream->timing_info_valid = 1; - + o->stream->ipol_timestamp = now; o->stream->ipol_usec_valid = 0; } + o->stream->auto_timing_update_requested = 0; + pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); + /* Clear old correction entries */ if (o->stream->direction == PA_STREAM_PLAYBACK) { int n; - for (n = 0; n < PA_MAX_LATENCY_CORRECTIONS; n++) { - if (!o->stream->latency_corrections[n].valid) + for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) { + if (!o->stream->write_index_corrections[n].valid) continue; - if (o->stream->latency_corrections[n].tag <= tag) - o->stream->latency_corrections[n].valid = 0; + if (o->stream->write_index_corrections[n].tag <= tag) + o->stream->write_index_corrections[n].valid = 0; } } - + if (o->callback) { pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; cb(o->stream, o->stream->timing_info_valid, o->userdata); @@ -790,16 +858,15 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - + if (s->direction == PA_STREAM_PLAYBACK) { /* Find a place to store the write_index correction data for this entry */ - cidx = (s->idx_latency_correction + 1) % PA_MAX_LATENCY_CORRECTIONS; + cidx = (s->current_write_index_correction + 1) % PA_MAX_WRITE_INDEX_CORRECTIONS; /* Check if we could allocate a correction slot. If not, there are too many outstanding queries */ - PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->latency_corrections[cidx].valid, PA_ERR_INTERNAL); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, !s->write_index_corrections[cidx].valid, PA_ERR_INTERNAL); } - - o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); t = pa_tagstruct_command( s->context, @@ -813,14 +880,16 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t if (s->direction == PA_STREAM_PLAYBACK) { /* Fill in initial correction data */ - o->stream->idx_latency_correction = cidx; - o->stream->latency_corrections[cidx].valid = 1; - o->stream->latency_corrections[cidx].tag = tag; - o->stream->latency_corrections[cidx].absolute = 0; - o->stream->latency_corrections[cidx].value = 0; - o->stream->latency_corrections[cidx].corrupt = 0; + o->stream->current_write_index_correction = cidx; + o->stream->write_index_corrections[cidx].valid = 1; + o->stream->write_index_corrections[cidx].tag = tag; + o->stream->write_index_corrections[cidx].absolute = 0; + o->stream->write_index_corrections[cidx].value = 0; + o->stream->write_index_corrections[cidx].corrupt = 0; } + pa_log("requesting update %u\n", tag); + return pa_operation_ref(o); } @@ -946,7 +1015,6 @@ finish: pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; - pa_operation *lo; pa_tagstruct *t; uint32_t tag; @@ -956,15 +1024,6 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); - if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { - if (!s->corked && b) { - /* Refresh the interpolated data just befor pausing */ - pa_stream_get_time(s, NULL); - } else if (s->corked && !b) - /* Unpausing */ - pa_gettimeofday(&s->ipol_timestamp); - } - s->corked = b; o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); @@ -978,9 +1037,9 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi pa_pstream_send_tagstruct(s->context->pstream, t); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); - if ((lo = pa_stream_update_timing_info(s, NULL, NULL))) - pa_operation_unref(lo); - + if (s->direction == PA_STREAM_PLAYBACK) + invalidate_indexes(s, 1, 0); + return pa_operation_ref(o); } @@ -1006,14 +1065,24 @@ static pa_operation* stream_send_simple_command(pa_stream *s, uint32_t command, pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation *o; - + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) { - pa_operation *lo; - if ((lo = pa_stream_update_timing_info(s, NULL, NULL))) - pa_operation_unref(lo); + if (s->direction == PA_STREAM_PLAYBACK) { + if (s->write_index_corrections[s->current_write_index_correction].valid) + s->write_index_corrections[s->current_write_index_correction].corrupt = 1; + + if (s->timing_info_valid) + s->timing_info.write_index_corrupt = 1; + + if (s->buffer_attr.prebuf > 0) + invalidate_indexes(s, 1, 0); + else + request_auto_timing_update(s, 1); + } else + invalidate_indexes(s, 0, 1); } return o; @@ -1023,14 +1092,11 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us pa_operation *o; PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); - if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) { - pa_operation *lo; + if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) + invalidate_indexes(s, 1, 0); - if ((lo = pa_stream_update_timing_info(s, NULL, NULL))) - pa_operation_unref(lo); - } - return o; } @@ -1038,13 +1104,10 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u pa_operation *o; PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); - if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) { - pa_operation *lo; - - if ((lo = pa_stream_update_timing_info(s, NULL, NULL))) - pa_operation_unref(lo); - } + if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) + invalidate_indexes(s, 1, 0); return o; } @@ -1084,50 +1147,56 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); - if (s->flags & PA_STREAM_INTERPOLATE_TIMING && s->ipol_usec_valid ) + if (s->flags & PA_STREAM_INTERPOLATE_TIMING && s->ipol_usec_valid) usec = s->ipol_usec; else { if (s->direction == PA_STREAM_PLAYBACK) { /* The last byte that was written into the output device * had this time value associated */ usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec); - - /* Because the latency info took a little time to come - * to us, we assume that the real output time is actually - * a little ahead */ - usec += s->timing_info.transport_usec; - - /* However, the output device usually maintains a buffer - too, hence the real sample currently played is a little - back */ - if (s->timing_info.sink_usec >= usec) - usec = 0; - else - usec -= s->timing_info.sink_usec; + + if (!s->corked) { + /* Because the latency info took a little time to come + * to us, we assume that the real output time is actually + * a little ahead */ + usec += s->timing_info.transport_usec; + + /* However, the output device usually maintains a buffer + too, hence the real sample currently played is a little + back */ + if (s->timing_info.sink_usec >= usec) + usec = 0; + else + usec -= s->timing_info.sink_usec; + } } else if (s->direction == PA_STREAM_RECORD) { /* The last byte written into the server side queue had * this time value associated */ usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec); - - /* Add transport latency */ - usec += s->timing_info.transport_usec; - - /* Add latency of data in device buffer */ - usec += s->timing_info.source_usec; - - /* If this is a monitor source, we need to correct the - * time by the playback device buffer */ - if (s->timing_info.sink_usec >= usec) - usec = 0; - else - usec -= s->timing_info.sink_usec; + + if (!s->corked) { + /* Add transport latency */ + usec += s->timing_info.transport_usec; + + /* Add latency of data in device buffer */ + usec += s->timing_info.source_usec; + + /* If this is a monitor source, we need to correct the + * time by the playback device buffer */ + if (s->timing_info.sink_usec >= usec) + usec = 0; + else + usec -= s->timing_info.sink_usec; + } } if (s->flags & PA_STREAM_INTERPOLATE_TIMING) { - s->ipol_usec_valid = 1; s->ipol_usec = usec; + s->ipol_usec_valid = 1; } } @@ -1189,6 +1258,7 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) { PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->timing_info_valid, PA_ERR_NODATA); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.read_index_corrupt, PA_ERR_NODATA); if ((r = pa_stream_get_time(s, &t)) < 0) return r; -- cgit