From b8a729a00f170241599e670ee3f3f65457b4320f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Apr 2006 01:29:33 +0000 Subject: * 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 --- src/polyp/def.h | 85 ++++++++++++++++++++++++++++++++++----------------- src/polyp/internal.h | 6 ++-- src/polyp/simple.c | 4 +-- src/polyp/stream.c | 86 ++++++++++++++++++++++++++-------------------------- src/polyp/stream.h | 46 ++++++++++++++++++++-------- 5 files changed, 138 insertions(+), 89 deletions(-) (limited to 'src/polyp') diff --git a/src/polyp/def.h b/src/polyp/def.h index 93d0996b..659b943b 100644 --- a/src/polyp/def.h +++ b/src/polyp/def.h @@ -80,22 +80,44 @@ typedef enum pa_stream_direction { /** 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_TIMING = 2, /**< Interpolate the latency for * this stream. When enabled, - * you can use - * pa_stream_interpolated_xxx() - * for synchronization. Using - * these functions instead of - * pa_stream_get_latency() has - * the advantage of not - * requiring a whole roundtrip - * for responses. Consider using - * this option when frequently - * requesting latency - * 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_get_latency() and pa_stream_get_time() + * will try to estimate the + * current record/playback time + * based on the local time that + * passed since the last timing + * info update. In addition + * timing update requests are + * issued periodically + * automatically. Using this + * option has the advantage of + * not requiring a whole + * roundtrip when the current + * playback/recording time is + * 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; /** 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 */ #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 * 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 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 + * 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: * 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.*/ -typedef struct pa_latency_info { - struct timeval timestamp; /**< The time when this latency info was current */ + * the latency of the owning sink. The two latency estimations + * described here are implemented in pa_stream_get_latency().*/ +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 * remote machine have synchronized * 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 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 * playback buffer in bytes. Think twice before * using this for seeking purposes: it @@ -213,9 +244,7 @@ typedef struct pa_latency_info { * 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; +} pa_timing_info; /** A structure for the spawn api. This may be used to integrate auto * spawned daemons into your application. For more information see @@ -236,12 +265,12 @@ typedef struct pa_spawn_api { * passed to the new process. */ } pa_spawn_api; -/** Seek type \since 0.8*/ +/** Seek type for pa_stream_write(). \since 0.8*/ typedef enum pa_seek_mode { 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_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_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_mode_t; PA_C_DECL_END diff --git a/src/polyp/internal.h b/src/polyp/internal.h index 8f1603b3..3b85b3d1 100644 --- a/src/polyp/internal.h +++ b/src/polyp/internal.h @@ -118,8 +118,8 @@ struct pa_stream { int corked; /* Store latest latency info */ - pa_latency_info latency_info; - int latency_info_valid; + pa_timing_info timing_info; + int timing_info_valid; /* Use to make sure that time advances monotonically */ 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_trash_ipol(pa_stream *s); - pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag); #define PA_CHECK_VALIDITY(context, expression, error) do { \ diff --git a/src/polyp/simple.c b/src/polyp/simple.c index 2593d5fa..f48c0b17 100644 --- a/src/polyp/simple.c +++ b/src/polyp/simple.c @@ -345,7 +345,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) { 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; assert(s); @@ -377,7 +377,7 @@ pa_usec_t pa_simple_get_playback_latency(pa_simple *p, int *rerror) { } 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) *rerror = pa_context_errno(p->context); return (pa_usec_t) -1; diff --git a/src/polyp/stream.c b/src/polyp/stream.c index d3599582..de27954c 100644 --- a/src/polyp/stream.c +++ b/src/polyp/stream.c @@ -89,7 +89,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec * s->record_memblockq = NULL; s->previous_time = 0; - s->latency_info_valid = 0; + s->timing_info_valid = 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) { 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); 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) */ pa_stream_ref(s); - if (s->flags & PA_STREAM_INTERPOLATE_LATENCY) { + if (s->flags & PA_STREAM_INTERPOLATE_TIMING) { struct timeval tv; pa_gettimeofday(&tv); @@ -406,7 +406,7 @@ static int create_stream( assert(s->ref >= 1); 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, !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); @@ -557,16 +557,16 @@ int pa_stream_write( } /* 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) { - s->latency_info.write_index_corrupt = 0; - s->latency_info.write_index = offset + length; + s->timing_info.write_index_corrupt = 0; + s->timing_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; + if (!s->timing_info.write_index_corrupt) + s->timing_info.write_index += offset + length; } else - s->latency_info.write_index_corrupt = 1; + s->timing_info.write_index_corrupt = 1; } 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); } -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; struct timeval local, remote, now; - pa_latency_info *i; + pa_timing_info *i; assert(pd); assert(o); assert(o->stream); assert(o->context); - i = &o->stream->latency_info; - o->stream->latency_info_valid = 0; + i = &o->stream->timing_info; + o->stream->timing_info_valid = 0; i->write_index_corrupt = 0; 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_usec_valid = 0; @@ -762,7 +762,7 @@ static void stream_get_latency_info_callback(pa_pdispatch *pd, uint32_t command, if (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: @@ -771,7 +771,7 @@ finish: 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; pa_operation *o; 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_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 */ 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->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) { /* Refresh the interpolated data just befor pausing */ 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_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); 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))) { 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); } @@ -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))) { 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); } @@ -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))) { 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); } @@ -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->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; 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->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 * to us, we assume that the real output time is actually * a little ahead */ - usec += s->latency_info.transport_usec; + 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->latency_info.sink_usec >= usec) + if (s->timing_info.sink_usec >= usec) usec = 0; else - usec -= s->latency_info.sink_usec; + 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->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 */ - usec += s->latency_info.transport_usec; + usec += s->timing_info.transport_usec; /* 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 * time by the playback device buffer */ - if (s->latency_info.sink_usec >= usec) + if (s->timing_info.sink_usec >= usec) usec = 0; 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 = usec; } } /* 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 * 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->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); + 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); if ((r = pa_stream_get_time(s, &t)) < 0) return r; if (s->direction == PA_STREAM_PLAYBACK) - cindex = s->latency_info.write_index; + cindex = s->timing_info.write_index; else - cindex = s->latency_info.read_index; + cindex = s->timing_info.read_index; if (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; } -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->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, 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) { diff --git a/src/polyp/stream.h b/src/polyp/stream.h index 99284ba3..ce041986 100644 --- a/src/polyp/stream.h +++ b/src/polyp/stream.h @@ -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 */ pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); -/** Update the latency info of a stream */ -pa_operation* pa_stream_update_latency_info(pa_stream *p, pa_stream_success_cb_t cb, void *userdata); +/** Request a timing info structure update for a stream. Use + * 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 */ 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); /** Return the current playback/recording time. This is based on the - * counter accessible with pa_stream_get_counter(). This function - * requires a pa_latency_info structure as argument, which should be - * acquired using pa_stream_get_latency(). \since 0.6 */ + * data in the timing info structure returned by + * pa_stream_get_timing_info(). This function will usually only return + * 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); -/** Return the total stream latency. Thus function requires a - * pa_latency_info structure as argument, which should be aquired - * using pa_stream_get_latency(). In case the stream is a monitoring - * stream the result can be negative, i.e. the captured samples are - * not yet played. In this case *negative is set to 1. \since 0.6 */ +/** Return the total stream latency. This function is based on + * pa_stream_get_time(). In case the stream is a monitoring stream the + * result can be negative, i.e. the captured samples are 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); -/** Return the latest latency data. \since 0.8 */ -const pa_latency_info* pa_stream_get_latency_info(pa_stream *s); +/** Return the latest raw timing data structure. The returned pointer + * 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 */ const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); -- cgit