* update docs for reworked latency API

* rename pa_latency_info to pa_timing_info, since that describes better what it is. Most people will only use pa_stream_get_time() anyway


git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@651 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2006-04-07 01:29:33 +00:00
parent c0592bb27c
commit b8a729a00f
6 changed files with 142 additions and 94 deletions

View file

@ -80,22 +80,44 @@ typedef enum pa_stream_direction {
/** Some special flags for stream connections. \since 0.6 */ /** Some special flags for stream connections. \since 0.6 */
typedef enum pa_stream_flags { 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_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_TIMING = 2, /**< Interpolate the latency for
* this stream. When enabled, * this stream. When enabled,
* you can use * pa_stream_get_latency() and pa_stream_get_time()
* pa_stream_interpolated_xxx() * will try to estimate the
* for synchronization. Using * current record/playback time
* these functions instead of * based on the local time that
* pa_stream_get_latency() has * passed since the last timing
* the advantage of not * info update. In addition
* requiring a whole roundtrip * timing update requests are
* for responses. Consider using * issued periodically
* this option when frequently * automatically. Using this
* requesting latency * option has the advantage of
* information. This is * not requiring a whole
* especially useful on long latency * roundtrip when the current
* network connections. */ * playback/recording time is
PA_STREAM_NOT_MONOTONOUS = 4, /**< Don't force the time to run monotonically */ * needed. Consider using this
* option when requesting
* latency information
* frequently. This is
* especially useful on long
* latency network
* connections. */
PA_STREAM_NOT_MONOTONOUS = 4, /**< Don't force the time to
* increase monotonically. If
* this option is enabled,
* pa_stream_get_time() will not
* necessarily return always
* monotonically increasing time
* values on each call. This may
* confuse applications which
* cannot deal with time going
* 'backwards', but has the
* advantage that bad transport
* latency estimations that
* caused the time to to jump
* ahead can be corrected
* quickly, without the need to
* wait. */
} pa_stream_flags_t; } pa_stream_flags_t;
/** Playback and record buffer metrics */ /** Playback and record buffer metrics */
@ -167,21 +189,23 @@ typedef enum pa_subscription_event_type {
/** Return one if an event type t matches an event mask bitfield */ /** Return one if an event type t matches an event mask bitfield */
#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)))) #define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
/** A structure for latency info. See pa_stream_get_latency(). The /** A structure for all kinds of timing information of a stream. See
* pa_stream_update_timing_info() and pa_stream_get_timing_info(). The
* total output latency a sample that is written with * total output latency a sample that is written with
* pa_stream_write() takes to be played may be estimated by * pa_stream_write() takes to be played may be estimated by
* sink_usec+buffer_usec+transport_usec. The output buffer to which * sink_usec+buffer_usec+transport_usec. The output buffer to which
* buffer_usec relates may be manipulated freely (with * buffer_usec relates may be manipulated freely (with
* pa_stream_write()'s seek 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 * the buffers sink_usec and source_usec relate to are first-in
* first-out buffer which cannot be flushed or manipulated in any * 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 * way. The total input latency a sample that is recorded takes to be
* delivered to the application is: * delivered to the application is:
* source_usec+buffer_usec+transport_usec-sink_usec. (Take care of * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of
* sign issues!) When connected to a monitor source sink_usec contains * sign issues!) When connected to a monitor source sink_usec contains
* the latency of the owning sink.*/ * the latency of the owning sink. The two latency estimations
typedef struct pa_latency_info { * described here are implemented in pa_stream_get_latency().*/
struct timeval timestamp; /**< The time when this latency info was current */ typedef struct pa_timing_info {
struct timeval timestamp; /**< The time when this timing info structure was current */
int synchronized_clocks; /**< Non-zero if the local and the int synchronized_clocks; /**< Non-zero if the local and the
* remote machine have synchronized * remote machine have synchronized
* clocks. If synchronized clocks are * clocks. If synchronized clocks are
@ -198,7 +222,14 @@ typedef struct pa_latency_info {
int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ 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 */ int write_index_corrupt; /**< Non-zero if write_index is not
* up-to-date because a local write
* command that corrupted it has been
* issued in the time since this latency
* info was current . Only write
* commands with SEEK_RELATIVE_ON_READ
* and SEEK_RELATIVE_END can corrupt
* write_index. */
int64_t write_index; /**< Current write index into the int64_t write_index; /**< Current write index into the
* playback buffer in bytes. Think twice before * playback buffer in bytes. Think twice before
* using this for seeking purposes: it * using this for seeking purposes: it
@ -213,9 +244,7 @@ typedef struct pa_latency_info {
* want to use it. Consider using * want to use it. Consider using
* PA_SEEK_RELATIVE_ON_READ * PA_SEEK_RELATIVE_ON_READ
* instead. \since 0.8 */ * instead. \since 0.8 */
} pa_timing_info;
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 /** A structure for the spawn api. This may be used to integrate auto
* spawned daemons into your application. For more information see * spawned daemons into your application. For more information see
@ -236,12 +265,12 @@ typedef struct pa_spawn_api {
* passed to the new process. */ * passed to the new process. */
} pa_spawn_api; } pa_spawn_api;
/** Seek type \since 0.8*/ /** Seek type for pa_stream_write(). \since 0.8*/
typedef enum pa_seek_mode { typedef enum pa_seek_mode {
PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */ PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */
PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */ PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */
PA_SEEK_RELATIVE_ON_READ = 2, /**< Seek relatively to the read index */ PA_SEEK_RELATIVE_ON_READ = 2, /**< Seek relatively to the read index. */
PA_SEEK_RELATIVE_END = 3, /**< Seek relatively to the current end of the buffer queue */ PA_SEEK_RELATIVE_END = 3, /**< Seek relatively to the current end of the buffer queue. */
} pa_seek_mode_t; } pa_seek_mode_t;
PA_C_DECL_END PA_C_DECL_END

View file

@ -118,8 +118,8 @@ struct pa_stream {
int corked; int corked;
/* Store latest latency info */ /* Store latest latency info */
pa_latency_info latency_info; pa_timing_info timing_info;
int latency_info_valid; int timing_info_valid;
/* Use to make sure that time advances monotonically */ /* Use to make sure that time advances monotonically */
pa_usec_t previous_time; pa_usec_t previous_time;
@ -182,8 +182,6 @@ pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, vo
void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
void pa_stream_trash_ipol(pa_stream *s);
pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag); pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag);
#define PA_CHECK_VALIDITY(context, expression, error) do { \ #define PA_CHECK_VALIDITY(context, expression, error) do { \

View file

@ -345,7 +345,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
return p->dead ? -1 : 0; return p->dead ? -1 : 0;
} }
static void latency_complete(pa_stream *s, int success, void *userdata) { static void timing_complete(pa_stream *s, int success, void *userdata) {
pa_simple *p = userdata; pa_simple *p = userdata;
assert(s); assert(s);
@ -377,7 +377,7 @@ pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) {
} }
p->latency = 0; p->latency = 0;
if (!(o = pa_stream_update_latency_info(p->stream, latency_complete, p))) { if (!(o = pa_stream_update_timing_info(p->stream, timing_complete, p))) {
if (rerror) if (rerror)
*rerror = pa_context_errno(p->context); *rerror = pa_context_errno(p->context);
return (pa_usec_t) -1; return (pa_usec_t) -1;

View file

@ -89,7 +89,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
s->record_memblockq = NULL; s->record_memblockq = NULL;
s->previous_time = 0; s->previous_time = 0;
s->latency_info_valid = 0; s->timing_info_valid = 0;
s->corked = 0; s->corked = 0;
@ -311,7 +311,7 @@ static void ipol_callback(pa_mainloop_api *m, pa_time_event *e, PA_GCC_UNUSED co
if (s->state == PA_STREAM_READY && !s->ipol_requested) { if (s->state == PA_STREAM_READY && !s->ipol_requested) {
pa_operation *o; pa_operation *o;
if ((o = pa_stream_update_latency_info(s, NULL, NULL))) { if ((o = pa_stream_update_timing_info(s, NULL, NULL))) {
pa_operation_unref(o); pa_operation_unref(o);
s->ipol_requested = 1; s->ipol_requested = 1;
} }
@ -371,7 +371,7 @@ 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) */ /* We add an extra ref as long as we're connected (i.e. in the dynarray) */
pa_stream_ref(s); pa_stream_ref(s);
if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) { if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
struct timeval tv; struct timeval tv;
pa_gettimeofday(&tv); pa_gettimeofday(&tv);
@ -406,7 +406,7 @@ static int create_stream(
assert(s->ref >= 1); assert(s->ref >= 1);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE); PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|PA_STREAM_INTERPOLATE_LATENCY)), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|PA_STREAM_INTERPOLATE_TIMING)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || flags == 0, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || flags == 0, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID); PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
@ -557,16 +557,16 @@ int pa_stream_write(
} }
/* Update the write index in the already available latency data */ /* Update the write index in the already available latency data */
if (s->latency_info_valid) { if (s->timing_info_valid) {
if (seek == PA_SEEK_ABSOLUTE) { if (seek == PA_SEEK_ABSOLUTE) {
s->latency_info.write_index_corrupt = 0; s->timing_info.write_index_corrupt = 0;
s->latency_info.write_index = offset + length; s->timing_info.write_index = offset + length;
} else if (seek == PA_SEEK_RELATIVE) { } else if (seek == PA_SEEK_RELATIVE) {
if (!s->latency_info.write_index_corrupt) if (!s->timing_info.write_index_corrupt)
s->latency_info.write_index += offset + length; s->timing_info.write_index += offset + length;
} else } else
s->latency_info.write_index_corrupt = 1; s->timing_info.write_index_corrupt = 1;
} }
return 0; return 0;
@ -654,18 +654,18 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us
return pa_operation_ref(o); return pa_operation_ref(o);
} }
static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata; pa_operation *o = userdata;
struct timeval local, remote, now; struct timeval local, remote, now;
pa_latency_info *i; pa_timing_info *i;
assert(pd); assert(pd);
assert(o); assert(o);
assert(o->stream); assert(o->stream);
assert(o->context); assert(o->context);
i = &o->stream->latency_info; i = &o->stream->timing_info;
o->stream->latency_info_valid = 0; o->stream->timing_info_valid = 0;
i->write_index_corrupt = 0; i->write_index_corrupt = 0;
if (command != PA_COMMAND_REPLY) { if (command != PA_COMMAND_REPLY) {
@ -741,7 +741,7 @@ static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command,
} }
} }
o->stream->latency_info_valid = 1; o->stream->timing_info_valid = 1;
o->stream->ipol_timestamp = now; o->stream->ipol_timestamp = now;
o->stream->ipol_usec_valid = 0; o->stream->ipol_usec_valid = 0;
@ -762,7 +762,7 @@ static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command,
if (o->callback) { if (o->callback) {
pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback;
cb(o->stream, o->stream->latency_info_valid, o->userdata); cb(o->stream, o->stream->timing_info_valid, o->userdata);
} }
finish: finish:
@ -771,7 +771,7 @@ finish:
pa_operation_unref(o); pa_operation_unref(o);
} }
pa_operation* pa_stream_update_latency_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) { pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t cb, void *userdata) {
uint32_t tag; uint32_t tag;
pa_operation *o; pa_operation *o;
pa_tagstruct *t; pa_tagstruct *t;
@ -800,7 +800,7 @@ pa_operation* pa_stream_update_latency_info(pa_stream *s, pa_stream_success_cb_t
pa_tagstruct_put_timeval(t, pa_gettimeofday(&now)); pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
pa_pstream_send_tagstruct(s->context->pstream, t); pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_info_callback, o); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_timing_info_callback, o);
/* Fill in initial correction data */ /* Fill in initial correction data */
o->stream->idx_latency_correction = cidx; o->stream->idx_latency_correction = cidx;
@ -945,7 +945,7 @@ 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->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->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) { if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
if (!s->corked && b) { if (!s->corked && b) {
/* Refresh the interpolated data just befor pausing */ /* Refresh the interpolated data just befor pausing */
pa_stream_get_time(s, NULL); pa_stream_get_time(s, NULL);
@ -967,7 +967,7 @@ 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_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, o);
if ((lo = pa_stream_update_latency_info(s, NULL, NULL))) if ((lo = pa_stream_update_timing_info(s, NULL, NULL)))
pa_operation_unref(lo); pa_operation_unref(lo);
return pa_operation_ref(o); return pa_operation_ref(o);
@ -1001,7 +1001,7 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use
if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) { 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; pa_operation *lo;
if ((lo = pa_stream_update_latency_info(s, NULL, NULL))) if ((lo = pa_stream_update_timing_info(s, NULL, NULL)))
pa_operation_unref(lo); pa_operation_unref(lo);
} }
@ -1016,7 +1016,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) { if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) {
pa_operation *lo; pa_operation *lo;
if ((lo = pa_stream_update_latency_info(s, NULL, NULL))) if ((lo = pa_stream_update_timing_info(s, NULL, NULL)))
pa_operation_unref(lo); pa_operation_unref(lo);
} }
@ -1031,7 +1031,7 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u
if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) { if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) {
pa_operation *lo; pa_operation *lo;
if ((lo = pa_stream_update_latency_info(s, NULL, NULL))) if ((lo = pa_stream_update_timing_info(s, NULL, NULL)))
pa_operation_unref(lo); pa_operation_unref(lo);
} }
@ -1072,56 +1072,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->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->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->timing_info_valid, PA_ERR_NODATA);
if (s->flags & PA_STREAM_INTERPOLATE_LATENCY && s->ipol_usec_valid ) if (s->flags & PA_STREAM_INTERPOLATE_TIMING && s->ipol_usec_valid )
usec = s->ipol_usec; usec = s->ipol_usec;
else { else {
if (s->direction == PA_STREAM_PLAYBACK) { if (s->direction == PA_STREAM_PLAYBACK) {
/* The last byte that was written into the output device /* The last byte that was written into the output device
* had this time value associated */ * 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); 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 /* Because the latency info took a little time to come
* to us, we assume that the real output time is actually * to us, we assume that the real output time is actually
* a little ahead */ * a little ahead */
usec += s->latency_info.transport_usec; usec += s->timing_info.transport_usec;
/* However, the output device usually maintains a buffer /* However, the output device usually maintains a buffer
too, hence the real sample currently played is a little too, hence the real sample currently played is a little
back */ back */
if (s->latency_info.sink_usec >= usec) if (s->timing_info.sink_usec >= usec)
usec = 0; usec = 0;
else else
usec -= s->latency_info.sink_usec; usec -= s->timing_info.sink_usec;
} else if (s->direction == PA_STREAM_RECORD) { } else if (s->direction == PA_STREAM_RECORD) {
/* The last byte written into the server side queue had /* The last byte written into the server side queue had
* this time value associated */ * 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); 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 */ /* Add transport latency */
usec += s->latency_info.transport_usec; usec += s->timing_info.transport_usec;
/* Add latency of data in device buffer */ /* Add latency of data in device buffer */
usec += s->latency_info.source_usec; usec += s->timing_info.source_usec;
/* If this is a monitor source, we need to correct the /* If this is a monitor source, we need to correct the
* time by the playback device buffer */ * time by the playback device buffer */
if (s->latency_info.sink_usec >= usec) if (s->timing_info.sink_usec >= usec)
usec = 0; usec = 0;
else else
usec -= s->latency_info.sink_usec; usec -= s->timing_info.sink_usec;
} }
if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) { if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
s->ipol_usec_valid = 1; s->ipol_usec_valid = 1;
s->ipol_usec = usec; s->ipol_usec = usec;
} }
} }
/* Interpolate if requested */ /* Interpolate if requested */
if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) { if (s->flags & PA_STREAM_INTERPOLATE_TIMING) {
/* We just add the time that passed since the latency info was /* We just add the time that passed since the latency info was
* current */ * current */
@ -1176,16 +1176,16 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); 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->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->timing_info_valid, PA_ERR_NODATA);
PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->latency_info.write_index_corrupt, PA_ERR_NODATA); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.write_index_corrupt, PA_ERR_NODATA);
if ((r = pa_stream_get_time(s, &t)) < 0) if ((r = pa_stream_get_time(s, &t)) < 0)
return r; return r;
if (s->direction == PA_STREAM_PLAYBACK) if (s->direction == PA_STREAM_PLAYBACK)
cindex = s->latency_info.write_index; cindex = s->timing_info.write_index;
else else
cindex = s->latency_info.read_index; cindex = s->timing_info.read_index;
if (cindex < 0) if (cindex < 0)
cindex = 0; cindex = 0;
@ -1200,15 +1200,15 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative) {
return 0; return 0;
} }
const pa_latency_info* pa_stream_get_latency_info(pa_stream *s) { const pa_timing_info* pa_stream_get_timing_info(pa_stream *s) {
assert(s); assert(s);
assert(s->ref >= 1); 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->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->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->latency_info_valid, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->timing_info_valid, PA_ERR_BADSTATE);
return &s->latency_info; return &s->timing_info;
} }
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) { const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s) {

View file

@ -130,8 +130,11 @@ size_t pa_stream_readable_size(pa_stream *p);
/** Drain a playback stream. Use this for notification when the buffer is empty */ /** Drain a playback stream. Use this for notification when the buffer is empty */
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Update the latency info of a stream */ /** Request a timing info structure update for a stream. Use
pa_operation* pa_stream_update_latency_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata); * pa_stream_get_timing_info() to get access to the raw timing data,
* or pa_stream_get_time() or pa_stream_get_latency() to get cleaned
* up values. */
pa_operation* pa_stream_update_timing_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata);
/** Set the callback function that is called whenever the state of the stream changes */ /** Set the callback function that is called whenever the state of the stream changes */
void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata); void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata);
@ -171,20 +174,39 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata); pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
/** Return the current playback/recording time. This is based on the /** Return the current playback/recording time. This is based on the
* counter accessible with pa_stream_get_counter(). This function * data in the timing info structure returned by
* requires a pa_latency_info structure as argument, which should be * pa_stream_get_timing_info(). This function will usually only return
* acquired using pa_stream_get_latency(). \since 0.6 */ * new data if a timing info update has been recieved. Only if timing
* interpolation has been requested (PA_STREAM_INTERPOLATE_TIMING)
* the data from the last timing update is used for an estimation of
* the current playback/recording time based on the local time that
* passed since the timing info structure has been acquired. The time
* value returned by this function is guaranteed to increase
* monotonically. (that means: the returned value is always greater or
* equal to the value returned on the last call) This behaviour can
* be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
* desirable to deal better with bad estimations of transport
* latencies, but may have strange effects if the application is not
* able to deal with time going 'backwards'. \since 0.6 */
int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec); int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec);
/** Return the total stream latency. Thus function requires a /** Return the total stream latency. This function is based on
* pa_latency_info structure as argument, which should be aquired * pa_stream_get_time(). In case the stream is a monitoring stream the
* using pa_stream_get_latency(). In case the stream is a monitoring * result can be negative, i.e. the captured samples are not yet
* stream the result can be negative, i.e. the captured samples are * played. In this case *negative is set to 1. \since 0.6 */
* not yet played. In this case *negative is set to 1. \since 0.6 */
int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative); int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative);
/** Return the latest latency data. \since 0.8 */ /** Return the latest raw timing data structure. The returned pointer
const pa_latency_info* pa_stream_get_latency_info(pa_stream *s); * points to an internal read-only instance of the timing
* structure. The user should make a copy of this structure if he
* wants to modify it. An in-place update to this data structure may
* be requested using pa_stream_update_timing_info(). If no
* pa_stream_update_timing_info() call was issued before, this
* function will fail with PA_ERR_NODATA. Please note that the
* write_index member field (and only this field) is updated on each
* pa_stream_write() call, not just when a timing update has been
* recieved. \since 0.8 */
const pa_timing_info* pa_stream_get_timing_info(pa_stream *s);
/** Return a pointer to the stream's sample specification. \since 0.6 */ /** Return a pointer to the stream's sample specification. \since 0.6 */
const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);

View file

@ -340,15 +340,15 @@ static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig,
} }
/* Show the current latency */ /* Show the current latency */
static void stream_update_latency_callback(pa_stream *s, int success, void *userdata) { static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
pa_usec_t total; pa_usec_t total;
int negative = 0; int negative = 0;
const pa_latency_info *i; const pa_timing_info *i;
assert(s); assert(s);
if (!success || if (!success ||
!(i = pa_stream_get_latency_info(s)) || !(i = pa_stream_get_timing_info(s)) ||
pa_stream_get_latency(s, &total, &negative) < 0) { pa_stream_get_latency(s, &total, &negative) < 0) {
fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context))); fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
quit(1); quit(1);
@ -366,8 +366,7 @@ static void stream_update_latency_callback(pa_stream *s, int success, void *user
/* Someone requested that the latency is shown */ /* Someone requested that the latency is shown */
static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) { static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
fprintf(stderr, "Got SIGUSR1, requesting latency.\n"); pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
pa_operation_unref(pa_stream_update_latency_info(stream, stream_update_latency_callback, NULL));
} }