mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
stream: add pw_stream_get_time_n()
Deprecate pw_stream_get_time() in favour of _get_time_n() that contains the size of the pw_time structure. Make the old one fill in the fields up to the buffered field. Make the new one use the size to decide how much info to fill in. Add a new buffered field in pw_time that contains the buffered data inside the converter/resampler. This leaves the queued field with purely the user provided size in the buffers. Use get_time_n() in places.
This commit is contained in:
parent
421b3b6ece
commit
5a9d2679ca
6 changed files with 81 additions and 35 deletions
|
|
@ -389,7 +389,7 @@ static void on_stream_process(void *data)
|
|||
struct pw_buffer *b;
|
||||
snd_pcm_uframes_t hw_avail, before, want, xfer;
|
||||
|
||||
pw_stream_get_time(pw->stream, &pw->time);
|
||||
pw_stream_get_time_n(pw->stream, &pw->time, sizeof(pw->time));
|
||||
|
||||
if (pw->time.rate.num != 0) {
|
||||
pw->time.delay = pw->time.delay * io->rate * pw->time.rate.num / pw->time.rate.denom;
|
||||
|
|
|
|||
|
|
@ -1356,7 +1356,7 @@ static void stream_process(void *data)
|
|||
if (do_flush)
|
||||
pw_stream_flush(stream->stream, true);
|
||||
|
||||
pw_stream_get_time(stream->stream, &pd.pwt);
|
||||
pw_stream_get_time_n(stream->stream, &pd.pwt, sizeof(pd.pwt));
|
||||
|
||||
pw_loop_invoke(impl->loop,
|
||||
do_process_done, 1, &pd, sizeof(pd), false, stream);
|
||||
|
|
|
|||
|
|
@ -2095,17 +2095,32 @@ int pw_stream_set_active(struct pw_stream *stream, bool active)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct old_time {
|
||||
int64_t now;
|
||||
struct spa_fraction rate;
|
||||
uint64_t ticks;
|
||||
int64_t delay;
|
||||
uint64_t queued;
|
||||
};
|
||||
|
||||
SPA_EXPORT
|
||||
int pw_stream_get_time(struct pw_stream *stream, struct pw_time *time)
|
||||
{
|
||||
return pw_stream_get_time_n(stream, time, sizeof(struct old_time));
|
||||
}
|
||||
|
||||
SPA_EXPORT
|
||||
int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size)
|
||||
{
|
||||
struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this);
|
||||
uintptr_t seq1, seq2;
|
||||
uint32_t rate_queued;
|
||||
uint32_t buffered, quantum;
|
||||
|
||||
do {
|
||||
seq1 = SEQ_READ(impl->seq);
|
||||
*time = impl->time;
|
||||
rate_queued = impl->rate_queued;
|
||||
memcpy(time, &impl->time, SPA_MIN(size, sizeof(struct pw_time)));
|
||||
buffered = impl->rate_queued;
|
||||
quantum = impl->quantum;
|
||||
seq2 = SEQ_READ(impl->seq);
|
||||
} while (!SEQ_READ_SUCCESS(seq1, seq2));
|
||||
|
||||
|
|
@ -2114,19 +2129,19 @@ int pw_stream_get_time(struct pw_stream *stream, struct pw_time *time)
|
|||
else
|
||||
time->queued = (int64_t)(impl->queued.incount - time->queued);
|
||||
|
||||
time->queued += rate_queued;
|
||||
|
||||
time->delay += ((impl->latency.min_quantum + impl->latency.max_quantum) / 2) * impl->quantum;
|
||||
time->delay += ((impl->latency.min_quantum + impl->latency.max_quantum) / 2) * quantum;
|
||||
time->delay += (impl->latency.min_rate + impl->latency.max_rate) / 2;
|
||||
time->delay += ((impl->latency.min_ns + impl->latency.max_ns) / 2) * time->rate.denom / SPA_NSEC_PER_SEC;
|
||||
|
||||
if (size >= sizeof(struct pw_time))
|
||||
time->buffered = buffered;
|
||||
|
||||
pw_log_trace_fp("%p: %"PRIi64" %"PRIi64" %"PRIu64" %d/%d %"PRIu64" %"
|
||||
PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, stream,
|
||||
time->now, time->delay, time->ticks,
|
||||
time->rate.num, time->rate.denom, time->queued,
|
||||
impl->dequeued.outcount, impl->dequeued.incount,
|
||||
impl->queued.outcount, impl->queued.incount);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ struct pw_stream_control {
|
|||
|
||||
/** A time structure.
|
||||
*
|
||||
* Use pw_stream_get_time() to get an updated time snapshot of the stream.
|
||||
* Use pw_stream_get_time_n() to get an updated time snapshot of the stream.
|
||||
* The time snapshot can give information about the time in the driver of the
|
||||
* graph, the delay to the edge of the graph and the internal queuing in the
|
||||
* stream.
|
||||
|
|
@ -211,11 +211,7 @@ struct pw_stream_control {
|
|||
* driver. I can be used to generate a timetime to schedule samples as well
|
||||
* as detect discontinuities in the timeline caused by xruns.
|
||||
*
|
||||
* The total delay of data in a stream is the sum of the queued data (not yet
|
||||
* processed data) and the delay to the edge of the graph, usually a playback
|
||||
* or capture device.
|
||||
*
|
||||
* pw_time.delay is expressed as pw_time.ticks, the time domain of the graph. This
|
||||
* pw_time.delay is expressed as pw_time.rate, the time domain of the graph. This
|
||||
* value, and pw_time.ticks, were captured at pw_time.now and can be extrapolated
|
||||
* to the current time like this:
|
||||
*
|
||||
|
|
@ -224,22 +220,50 @@ struct pw_stream_control {
|
|||
* int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw_time.now;
|
||||
* int64_t elapsed = (pw_time.rate.denom * diff) / (pw_time.rate.num * SPA_NSEC_PER_SEC);
|
||||
*
|
||||
* pw_time.queued is expressed in the time domain of the stream, or the format
|
||||
* that is used for the buffers of this stream. The value depends on the specific
|
||||
* format used by the application and how the application manages the pw_buffer.size
|
||||
* field. For audio applications, it is recommended to use the number of samples in
|
||||
* the buffer as the pw_buffer.size field to get meaningful pw_time.queued values.
|
||||
* pw_time.delay contains the total delay that a signal will travel through the
|
||||
* graph. This includes the delay caused by filters in the graph as well as delays
|
||||
* caused by the hardware. The delay is usually quite stable and should only change when
|
||||
* the topology, quantum or samplerate of the graph changes.
|
||||
*
|
||||
* pw_time.queued and pw_time.buffered is expressed in the time domain of the stream,
|
||||
* or the format that is used for the buffers of this stream.
|
||||
*
|
||||
* pw_time.queued is the sum of all the pw_buffer.size fields of the buffers that are
|
||||
* currently queued in the stream but not yet processed. The application can choose
|
||||
* the units of this value, for example, time, samples or bytes (below expressed
|
||||
* as app.rate).
|
||||
*
|
||||
* pw_time.buffered is format dependent, for audio/raw it contains the number of samples
|
||||
* that are buffered inside the resampler/converter.
|
||||
*
|
||||
* The total delay of data in a stream is the sum of the queued and buffered data
|
||||
* (not yet processed data) and the delay to the edge of the graph, usually a
|
||||
* playback or capture device.
|
||||
*
|
||||
* For an audio playback stream, if you were to queue a buffer, the total delay
|
||||
* in milliseconds for the first sample in the newly queued buffer to arrive in the
|
||||
* sink can be calculated as:
|
||||
* in milliseconds for the first sample in the newly queued buffer to be played
|
||||
* by the hardware can be calculated as:
|
||||
*
|
||||
* (pw_time.queued * 1000 / stream.samplerate) +
|
||||
* ((pw_time.delay - elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom)
|
||||
* (pw_time.buffered * 1000 / stream.samplerate) +
|
||||
* (pw_time.queued * 1000 / app.rate) +
|
||||
* ((pw_time.delay - elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom)
|
||||
*
|
||||
* The current extrapolated time (in ms) in the source or sink can be calculated as:
|
||||
*
|
||||
* (pw_time.ticks + elapsed) * 1000 * pw_time.rate.num / pw_time.rate.denom
|
||||
*
|
||||
*
|
||||
* stream time domain graph time domain
|
||||
* /-----------------------\/-----------------------------\
|
||||
*
|
||||
* queue +-+ +-+ +-----------+ +--------+
|
||||
* ----> | | | |->| converter | -> graph -> | kernel | -> speaker
|
||||
* <---- +-+ +-+ +-----------+ +--------+
|
||||
* dequeue buffers \-------------------/\--------/
|
||||
* graph internal
|
||||
* latency latency
|
||||
* \--------/\-------------/\-----------------------------/
|
||||
* queued buffered delay
|
||||
*/
|
||||
struct pw_time {
|
||||
int64_t now; /**< the monotonic time in nanoseconds. This is the time
|
||||
|
|
@ -264,11 +288,10 @@ struct pw_time {
|
|||
* not include the delay caused by queued buffers. */
|
||||
uint64_t queued; /**< data queued in the stream, this is the sum
|
||||
* of the size fields in the pw_buffer that are
|
||||
* currently queued and, for audio streams, the extra
|
||||
* number of samples queued in the resampler. For audio
|
||||
* streams, it is thus highly recommended to use the
|
||||
* pw_buffer size field as the number of samples in
|
||||
* the buffer. */
|
||||
* currently queued */
|
||||
uint64_t buffered; /**< for audio/raw streams, this contains the extra
|
||||
* number of samples buffered in the resampler.
|
||||
* Since 0.3.50. */
|
||||
};
|
||||
|
||||
#include <pipewire/port.h>
|
||||
|
|
@ -426,7 +449,12 @@ const struct pw_stream_control *pw_stream_get_control(struct pw_stream *stream,
|
|||
/** Set control values */
|
||||
int pw_stream_set_control(struct pw_stream *stream, uint32_t id, uint32_t n_values, float *values, ...);
|
||||
|
||||
/** Query the time on the stream */
|
||||
/** Query the time on the stream */
|
||||
int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time *time, size_t size);
|
||||
|
||||
/** Query the time on the stream, deprecated since 0.3.50,
|
||||
* use pw_stream_get_time_n() to get the fields added since 0.3.50. */
|
||||
SPA_DEPRECATED
|
||||
int pw_stream_get_time(struct pw_stream *stream, struct pw_time *time);
|
||||
|
||||
/** Get a buffer that can be filled for playback streams or consumed
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ static void test_abi(void)
|
|||
|
||||
#if defined(__x86_64__) && defined(__LP64__)
|
||||
spa_assert_se(sizeof(struct pw_buffer) == 32);
|
||||
spa_assert_se(sizeof(struct pw_time) == 40);
|
||||
spa_assert_se(sizeof(struct pw_time) == 48);
|
||||
#else
|
||||
fprintf(stderr, "%zd\n", sizeof(struct pw_buffer));
|
||||
fprintf(stderr, "%zd\n", sizeof(struct pw_time));
|
||||
|
|
@ -171,13 +171,14 @@ static void test_create(void)
|
|||
/* check id, only when connected */
|
||||
spa_assert_se(pw_stream_get_node_id(stream) == SPA_ID_INVALID);
|
||||
|
||||
spa_assert_se(pw_stream_get_time(stream, &tm) == 0);
|
||||
spa_assert_se(pw_stream_get_time_n(stream, &tm, sizeof(tm)) == 0);
|
||||
spa_assert_se(tm.now == 0);
|
||||
spa_assert_se(tm.rate.num == 0);
|
||||
spa_assert_se(tm.rate.denom == 0);
|
||||
spa_assert_se(tm.ticks == 0);
|
||||
spa_assert_se(tm.delay == 0);
|
||||
spa_assert_se(tm.queued == 0);
|
||||
spa_assert_se(tm.buffered == 0);
|
||||
|
||||
spa_assert_se(pw_stream_dequeue_buffer(stream) == NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -1007,11 +1007,13 @@ static void do_print_delay(void *userdata, uint64_t expirations)
|
|||
{
|
||||
struct data *data = userdata;
|
||||
struct pw_time time;
|
||||
pw_stream_get_time(data->stream, &time);
|
||||
printf("stream time: now:%"PRIi64" rate:%u/%u ticks:%"PRIu64" delay:%"PRIi64" queued:%"PRIu64"\n",
|
||||
pw_stream_get_time_n(data->stream, &time, sizeof(time));
|
||||
printf("stream time: now:%"PRIi64" rate:%u/%u ticks:%"PRIu64
|
||||
" delay:%"PRIi64" queued:%"PRIu64
|
||||
" buffered:%"PRIi64" \n",
|
||||
time.now,
|
||||
time.rate.num, time.rate.denom,
|
||||
time.ticks, time.delay, time.queued);
|
||||
time.ticks, time.delay, time.queued, time.buffered);
|
||||
}
|
||||
|
||||
enum {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue