/** Some special flags for stream connections. \since 0.6 */
typedef enum pa_stream_flags {
PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */
- PA_STREAM_INTERPOLATE_LATENCY = 2 /**< Interpolate the latency for
+ PA_STREAM_INTERPOLATE_LATENCY = 2, /**< Interpolate the latency for
* this stream. When enabled,
* you can use
* pa_stream_interpolated_xxx()
* information. This is
* especially useful on long latency
* network connections. */
+ PA_STREAM_NOT_MONOTONOUS = 4, /**< Don't force the time to run monotonically */
} pa_stream_flags_t;
/** Playback and record buffer metrics */
PA_ERR_INVALIDSERVER, /**< Invalid server */
PA_ERR_MODINITFAILED, /**< Module initialization failed */
PA_ERR_BADSTATE, /**< Bad state */
- PA_ERR_NODATA, /**< No data */
+ PA_ERR_NODATA, /**< No data */
PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
* 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
- * pa_stream_write()'s delta argument, pa_stream_flush() and friends),
+ * pa_stream_write()'s seek argument, pa_stream_flush() and friends),
* the buffers sink_usec/source_usec relates to is a first-in
* first-out buffer which cannot be flushed or manipulated in any
* way. The total input latency a sample that is recorded takes to be
* sign issues!) When connected to a monitor source sink_usec contains
* the latency of the owning sink.*/
typedef struct pa_latency_info {
- 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 */
- int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
- uint32_t queue_length; /**< Queue size in bytes. For both playback and record streams. */
+ struct timeval timestamp; /**< The time when this latency info was current */
int synchronized_clocks; /**< Non-zero if the local and the
* remote machine have synchronized
* clocks. If synchronized clocks are
* detects synchronized clocks is very
* limited und unreliable itself. \since
* 0.5 */
- struct timeval timestamp; /**< The time when this latency info was current */
- uint64_t counter; /**< The byte counter current when the latency info was requested. \since 0.6 */
- int64_t write_index; /**< Current absolute write index in the buffer. \since 0.8 */
- int64_t read_index; /**< Current absolute read index in the buffer. \since 0.8 */
+
+ 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 */
+
+ int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */
+
+ int write_index_corrupt; /**< Non-Zero if the write_index is not up to date because a local write command corrupted it */
+ 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 */
+ int64_t read_index; /**< Current read 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_ON_READ
+ * instead. \since 0.8 */
+
+ uint32_t buffer_length; /* Current buffer length. This is usually identical to write_index-read_index. */
} pa_latency_info;
/** A structure for the spawn api. This may be used to integrate auto
PA_SEEK_RELATIVE_END = 3, /**< Seek relatively to the current end of the buffer queue */
} pa_seek_mode_t;
-
PA_C_DECL_END
#endif
#include "internal.h"
#define LATENCY_IPOL_INTERVAL_USEC (10000L)
-#define COUNTER_HASHMAP_MAXSIZE (5)
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
pa_stream *s;
+ int i;
assert(c);
s->direction = PA_STREAM_NODIRECTION;
s->name = pa_xstrdup(name);
s->sample_spec = *ss;
+ s->flags = 0;
if (map)
s->channel_map = *map;
s->record_memblockq = NULL;
- s->counter_hashmap = pa_hashmap_new(NULL, NULL);
-
- s->counter = 0;
s->previous_time = 0;
- s->previous_ipol_time = 0;
+ s->latency_info_valid = 0;
s->corked = 0;
- s->interpolate = 0;
- s->ipol_usec = 0;
- memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
+ 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;
+
PA_LLIST_PREPEND(pa_stream, c->streams, s);
/* The context and stream will point at each other. We cannot ref count
return s;
}
-static void hashmap_free_func(void *p, void *userdata) {
- pa_xfree(p);
-}
-
static void stream_free(pa_stream *s) {
assert(s && s->context && !s->channel_valid);
if (s->record_memblockq)
pa_memblockq_free(s->record_memblockq);
- if (s->counter_hashmap)
- pa_hashmap_free(s->counter_hashmap, hashmap_free_func, NULL);
-
pa_xfree(s->name);
pa_xfree(s);
}
if (s->state == PA_STREAM_READY && !s->ipol_requested) {
pa_operation *o;
- o = pa_stream_get_latency_info(s, NULL, NULL);
- if (o)
+
+ if ((o = pa_stream_update_latency_info(s, NULL, NULL))) {
pa_operation_unref(o);
- s->ipol_requested = 1;
+ s->ipol_requested = 1;
+ }
}
pa_gettimeofday(&next);
/* We add an extra ref as long as we're connected (i.e. in the dynarray) */
pa_stream_ref(s);
- if (s->interpolate) {
+ if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) {
struct timeval tv;
pa_gettimeofday(&tv);
pa_stream_ref(s);
s->direction = direction;
+ s->flags = flags;
if (sync_stream)
s->syncid = sync_stream->syncid;
- s->interpolate = !!(flags & PA_STREAM_INTERPOLATE_LATENCY);
- pa_stream_trash_ipol(s);
-
if (attr)
s->buffer_attr = *attr;
else {
else
s->requested_bytes = 0;
- s->counter += length;
+ /* Update latency request correction */
+ if (s->latency_corrections[s->idx_latency_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;
+ } 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;
+ } else
+ s->latency_corrections[s->idx_latency_correction].corrupt = 1;
+ }
+
+ /* Update the write index in the already available latency data */
+ if (s->latency_info_valid) {
+
+ if (seek == PA_SEEK_ABSOLUTE) {
+ s->latency_info.write_index_corrupt = 0;
+ s->latency_info.write_index = offset + length;
+ } else if (seek == PA_SEEK_RELATIVE) {
+ if (!s->latency_info.write_index_corrupt)
+ s->latency_info.write_index += offset + length;
+ } else
+ s->latency_info.write_index_corrupt = 1;
+ }
+
return 0;
}
s->peek_memchunk.index = 0;
s->peek_memchunk.memblock = NULL;
- s->counter += s->peek_memchunk.length;
return 0;
}
static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
- pa_latency_info i, *p = NULL;
struct timeval local, remote, now;
+ pa_latency_info *i;
assert(pd);
assert(o);
assert(o->stream);
assert(o->context);
- i.counter = *(uint64_t*)pa_hashmap_get(o->stream->counter_hashmap, (void*)(unsigned long)tag);
- pa_xfree(pa_hashmap_remove(o->stream->counter_hashmap, (void*)(unsigned long)tag));
+ i = &o->stream->latency_info;
+ o->stream->latency_info_valid = 0;
+ i->write_index_corrupt = 0;
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 ||
- pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
- pa_tagstruct_get_boolean(t, &i.playing) < 0 ||
- pa_tagstruct_getu32(t, &i.queue_length) < 0 ||
+ } else if (pa_tagstruct_get_usec(t, &i->buffer_usec) < 0 ||
+ 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 ||
pa_tagstruct_get_timeval(t, &remote) < 0 ||
- pa_tagstruct_gets64(t, &i.write_index) < 0 ||
- pa_tagstruct_gets64(t, &i.read_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->write_index) < 0 ||
+ pa_tagstruct_gets64(t, &i->read_index) < 0 ||
!pa_tagstruct_eof(t)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
+
} else {
pa_gettimeofday(&now);
/* local and remote seem to have synchronized clocks */
if (o->stream->direction == PA_STREAM_PLAYBACK)
- i.transport_usec = pa_timeval_diff(&remote, &local);
+ i->transport_usec = pa_timeval_diff(&remote, &local);
else
- i.transport_usec = pa_timeval_diff(&now, &remote);
+ i->transport_usec = pa_timeval_diff(&now, &remote);
- i.synchronized_clocks = 1;
- i.timestamp = remote;
+ i->synchronized_clocks = 1;
+ i->timestamp = remote;
} else {
/* clocks are not synchronized, let's estimate latency then */
- i.transport_usec = pa_timeval_diff(&now, &local)/2;
- i.synchronized_clocks = 0;
- i.timestamp = local;
- pa_timeval_add(&i.timestamp, i.transport_usec);
+ i->transport_usec = pa_timeval_diff(&now, &local)/2;
+ i->synchronized_clocks = 0;
+ i->timestamp = local;
+ pa_timeval_add(&i->timestamp, i->transport_usec);
}
-
- if (o->stream->interpolate) {
-/* pa_log("new interpol data"); */
- o->stream->ipol_timestamp = i.timestamp;
- o->stream->ipol_usec = pa_stream_get_time(o->stream, &i);
- o->stream->ipol_requested = 0;
+
+ if (o->stream->direction == PA_STREAM_PLAYBACK) {
+ /* Write index correction */
+
+ int n, j;
+ uint32_t ctag = tag;
+
+ /* 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) {
+
+ /* Step over invalid data or out-of-date data */
+ if (!o->stream->latency_corrections[j].valid ||
+ o->stream->latency_corrections[j].tag < ctag)
+ continue;
+
+ /* Make sure that everything is in order */
+ ctag = o->stream->latency_corrections[j].tag+1;
+
+ /* Now fix the write index */
+ if (o->stream->latency_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) {
+ /* An absolute seek was made */
+ i->write_index = o->stream->latency_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;
+ }
+ }
}
+
+ o->stream->latency_info_valid = 1;
+
+ o->stream->ipol_timestamp = now;
+ o->stream->ipol_usec_valid = 0;
+ }
- p = &i;
+ /* 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)
+ continue;
+
+ if (o->stream->latency_corrections[n].tag <= tag)
+ o->stream->latency_corrections[n].valid = 0;
+ }
}
if (o->callback) {
- pa_stream_get_latency_info_cb_t cb = (pa_stream_get_latency_info_cb_t) o->callback;
- cb(o->stream, p, o->userdata);
+ pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
+ cb(o->stream, o->stream->latency_info_valid, o->userdata);
}
-
+
finish:
+
pa_operation_done(o);
pa_operation_unref(o);
}
-pa_operation* pa_stream_get_latency_info(pa_stream *s, pa_stream_get_latency_info_cb_t cb, void *userdata) {
+pa_operation* pa_stream_update_latency_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
uint32_t tag;
pa_operation *o;
pa_tagstruct *t;
struct timeval now;
- uint64_t *counter;
+ int cidx;
assert(s);
assert(s->ref >= 1);
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);
- PA_CHECK_VALIDITY_RETURN_NULL(s->context, pa_hashmap_size(s->counter_hashmap) < COUNTER_HASHMAP_MAXSIZE, PA_ERR_INTERNAL);
+
+ /* Find a place to store the write_index correction data for this entry */
+ cidx = (s->idx_latency_correction + 1) % PA_MAX_LATENCY_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);
o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
-
+
t = pa_tagstruct_command(
s->context,
s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_GET_PLAYBACK_LATENCY : PA_COMMAND_GET_RECORD_LATENCY,
&tag);
pa_tagstruct_putu32(t, s->channel);
-
- pa_gettimeofday(&now);
- pa_tagstruct_put_timeval(t, &now);
+ pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o);
- counter = pa_xmalloc(sizeof(uint64_t));
- *counter = s->counter;
- pa_hashmap_put(s->counter_hashmap, (void*)(unsigned long)tag, counter);
-
+ /* 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;
+
return pa_operation_ref(o);
}
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->interpolate) {
- if (!s->corked && b)
- /* Pausing */
- s->ipol_usec = pa_stream_get_interpolated_time(s);
- else if (s->corked && !b)
+ if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) {
+ 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);
}
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
- lo = pa_stream_get_latency_info(s, NULL, NULL);
- if (lo)
+ if ((lo = pa_stream_update_latency_info(s, NULL, NULL)))
pa_operation_unref(lo);
return pa_operation_ref(o);
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;
- lo = pa_stream_get_latency_info(s, NULL, NULL);
- if (lo)
+
+ if ((lo = pa_stream_update_latency_info(s, NULL, NULL)))
pa_operation_unref(lo);
}
if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) {
pa_operation *lo;
- lo = pa_stream_get_latency_info(s, NULL, NULL);
- if (lo)
+
+ if ((lo = pa_stream_update_latency_info(s, NULL, NULL)))
pa_operation_unref(lo);
}
if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) {
pa_operation *lo;
- lo = pa_stream_get_latency_info(s, NULL, NULL);
- if (lo)
+
+ if ((lo = pa_stream_update_latency_info(s, NULL, NULL)))
pa_operation_unref(lo);
}
return pa_operation_ref(o);
}
-uint64_t pa_stream_get_counter(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
-
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (uint64_t) -1);
-
- return s->counter;
-}
-
-pa_usec_t pa_stream_get_time(pa_stream *s, const pa_latency_info *i) {
- pa_usec_t usec;
+int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
+ pa_usec_t usec = 0;
assert(s);
assert(s->ref >= 1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (pa_usec_t) -1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, (pa_usec_t) -1);
-
- usec = pa_bytes_to_usec(i->counter, &s->sample_spec);
+ 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->latency_info_valid, PA_ERR_NODATA);
- if (i) {
+ if (s->flags & PA_STREAM_INTERPOLATE_LATENCY && s->ipol_usec_valid )
+ usec = s->ipol_usec;
+ else {
if (s->direction == PA_STREAM_PLAYBACK) {
- pa_usec_t latency = i->transport_usec + i->buffer_usec + i->sink_usec;
- if (usec < latency)
+ /* The last byte that was written into the output device
+ * had this time value associated */
+ usec = pa_bytes_to_usec(s->latency_info.read_index < 0 ? 0 : (uint64_t) s->latency_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->latency_info.transport_usec;
+
+ /* However, the output device usually maintains a buffer
+ too, hence the real sample currently played is a little
+ back */
+ if (s->latency_info.sink_usec >= usec)
usec = 0;
else
- usec -= latency;
-
+ usec -= s->latency_info.sink_usec;
+
} else if (s->direction == PA_STREAM_RECORD) {
- usec += i->source_usec + i->buffer_usec + i->transport_usec;
-
- if (usec > i->sink_usec)
- usec -= i->sink_usec;
- else
+ /* The last byte written into the server side queue had
+ * this time value associated */
+ usec = pa_bytes_to_usec(s->latency_info.write_index < 0 ? 0 : (uint64_t) s->latency_info.write_index, &s->sample_spec);
+
+ /* Add transport latency */
+ usec += s->latency_info.transport_usec;
+
+ /* Add latency of data in device buffer */
+ usec += s->latency_info.source_usec;
+
+ /* If this is a monitor source, we need to correct the
+ * time by the playback device buffer */
+ if (s->latency_info.sink_usec >= usec)
usec = 0;
+ else
+ usec -= s->latency_info.sink_usec;
+ }
+
+ if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) {
+ s->ipol_usec_valid = 1;
+ s->ipol_usec = usec;
}
}
- if (usec < s->previous_time)
- usec = s->previous_time;
+ /* Interpolate if requested */
+ if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) {
- s->previous_time = usec;
+ /* We just add the time that passed since the latency info was
+ * current */
+ if (!s->corked) {
+ struct timeval now;
+
+ usec += pa_timeval_diff(pa_gettimeofday(&now), &s->ipol_timestamp);
+ s->ipol_timestamp = now;
+ }
+ }
- return usec;
+ /* Make sure the time runs monotonically */
+ if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
+ if (usec < s->previous_time)
+ usec = s->previous_time;
+ else
+ s->previous_time = usec;
+ }
+
+ if (r_usec)
+ *r_usec = usec;
+
+ return 0;
}
-static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t t, pa_usec_t c, int *negative) {
+static pa_usec_t time_counter_diff(pa_stream *s, pa_usec_t a, pa_usec_t b, int *negative) {
assert(s);
assert(s->ref >= 1);
if (negative)
*negative = 0;
- if (c < t) {
- if (s->direction == PA_STREAM_RECORD) {
- if (negative)
- *negative = 1;
-
- return t-c;
+ if (a >= b)
+ return a-b;
+ else {
+ if (negative && s->direction == PA_STREAM_RECORD) {
+ *negative = 1;
+ return b-a;
} else
return 0;
- } else
- return c-t;
+ }
}
-pa_usec_t pa_stream_get_latency(pa_stream *s, const pa_latency_info *i, int *negative) {
+int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
pa_usec_t t, c;
+ int r;
+ int64_t cindex;
assert(s);
assert(s->ref >= 1);
- assert(i);
+ assert(r_usec);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (pa_usec_t) -1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, (pa_usec_t) -1);
+ 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->latency_info_valid, PA_ERR_NODATA);
+ PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->latency_info.write_index_corrupt, PA_ERR_NODATA);
- t = pa_stream_get_time(s, i);
- c = pa_bytes_to_usec(s->counter, &s->sample_spec);
-
- return time_counter_diff(s, t, c, negative);
-}
+ if ((r = pa_stream_get_time(s, &t)) < 0)
+ return r;
-const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ if (s->direction == PA_STREAM_PLAYBACK)
+ cindex = s->latency_info.write_index;
+ else
+ cindex = s->latency_info.read_index;
- return &s->sample_spec;
-}
+ if (cindex < 0)
+ cindex = 0;
+
+ c = pa_bytes_to_usec(cindex, &s->sample_spec);
-const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
- assert(s);
- assert(s->ref >= 1);
+ if (s->direction == PA_STREAM_PLAYBACK)
+ *r_usec = time_counter_diff(s, c, t, negative);
+ else
+ *r_usec = time_counter_diff(s, t, c, negative);
- return &s->channel_map;
+ return 0;
}
-void pa_stream_trash_ipol(pa_stream *s) {
+const pa_latency_info* pa_stream_get_latency_info(pa_stream *s) {
assert(s);
assert(s->ref >= 1);
- if (!s->interpolate)
- return;
+ 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);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->latency_info_valid, PA_ERR_BADSTATE);
- memset(&s->ipol_timestamp, 0, sizeof(s->ipol_timestamp));
- s->ipol_usec = 0;
+ return &s->latency_info;
}
-pa_usec_t pa_stream_get_interpolated_time(pa_stream *s) {
- pa_usec_t usec;
-
+const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {
assert(s);
assert(s->ref >= 1);
-
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (pa_usec_t) -1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, (pa_usec_t) -1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->interpolate, PA_ERR_BADSTATE, (pa_usec_t) -1);
-
- if (s->corked)
- usec = s->ipol_usec;
- else {
- if (s->ipol_timestamp.tv_sec == 0)
- usec = 0;
- else
- usec = s->ipol_usec + pa_timeval_age(&s->ipol_timestamp);
- }
-
- if (usec < s->previous_ipol_time)
- usec = s->previous_ipol_time;
-
- s->previous_ipol_time = usec;
- return usec;
+ return &s->sample_spec;
}
-pa_usec_t pa_stream_get_interpolated_latency(pa_stream *s, int *negative) {
- pa_usec_t t, c;
-
+const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
assert(s);
assert(s->ref >= 1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE, (pa_usec_t) -1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE, (pa_usec_t) -1);
- PA_CHECK_VALIDITY_RETURN_ANY(s->context, s->interpolate, PA_ERR_BADSTATE, (pa_usec_t) -1);
-
- t = pa_stream_get_interpolated_time(s);
- c = pa_bytes_to_usec(s->counter, &s->sample_spec);
- return time_counter_diff(s, t, c, negative);
+ return &s->channel_map;
}
+
+