node-driver: ensure position doesn't jump

Make sure that the position only advances in the running state.

When we are not following a clock we can simply increment the position
with the duration every time we run.

If we are following a clock. Take the elapsed time of the clock into
account when aligning to the position.

Fixes #3189
This commit is contained in:
Wim Taymans 2023-09-21 09:50:28 +02:00
parent e2f343e844
commit 8249fa3cbf

View file

@ -234,9 +234,9 @@ static inline uint64_t scale_u64(uint64_t val, uint32_t num, uint32_t denom)
static void on_timeout(struct spa_source *source)
{
struct impl *this = source->data;
uint64_t expirations, nsec, duration, current_time, current_position, position;
uint64_t expirations, nsec, duration, position;
uint32_t rate;
double corr = 1.0, err = 0.0;
double corr = 1.0;
int res;
if ((res = spa_system_timerfd_read(this->data_system,
@ -246,6 +246,9 @@ static void on_timeout(struct spa_source *source)
this, spa_strerror(res));
return;
}
nsec = this->next_time;
if (SPA_LIKELY(this->position)) {
duration = this->position->clock.target_duration;
rate = this->position->clock.target_rate.denom;
@ -253,51 +256,50 @@ static void on_timeout(struct spa_source *source)
duration = 1024;
rate = 48000;
}
nsec = this->next_time;
if (this->tracking)
/* we are actually following another clock */
current_time = gettime_nsec(this, this->props.clock_id);
else
current_time = nsec;
current_position = scale_u64(current_time, rate, SPA_NSEC_PER_SEC);
if (this->last_time == 0) {
spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate);
this->max_error = rate * MAX_ERROR_MS / 1000;
position = current_position;
} else if (SPA_LIKELY(this->clock)) {
if (SPA_LIKELY(this->clock)) {
position = this->clock->position + this->clock->duration;
} else {
position = current_position;
position = 0;
}
/* check the elapsed time of the other clock against
* the graph clock elapsed time, feed this error into the
* dll and adjust the timeout of our MONOTONIC clock. */
err = (double)position - (double)current_position;
if (err > this->max_error)
err = this->max_error;
else if (err < -this->max_error)
err = -this->max_error;
this->last_time = current_time;
if (this->tracking) {
double err = 0.0;
uint64_t current_time, current_position;
/* we are actually following another clock */
current_time = gettime_nsec(this, this->props.clock_id);
current_position = scale_u64(current_time, rate, SPA_NSEC_PER_SEC);
if (this->last_time == 0) {
spa_dll_set_bw(&this->dll, SPA_DLL_BW_MIN, duration, rate);
this->max_error = rate * MAX_ERROR_MS / 1000;
this->last_time = current_position - position;
current_position = position;
} else {
current_position -= this->last_time;
}
/* check the elapsed time of the other clock against
* the graph clock elapsed time, feed this error into the
* dll and adjust the timeout of our MONOTONIC clock. */
err = (double)position - (double)current_position;
if (err > this->max_error)
err = this->max_error;
else if (err < -this->max_error)
err = -this->max_error;
corr = spa_dll_update(&this->dll, err);
this->next_time = nsec + duration / corr * 1e9 / rate;
} else {
corr = 1.0;
this->next_time = scale_u64(position + duration, SPA_NSEC_PER_SEC, rate);
}
if (SPA_UNLIKELY((this->next_time - this->base_time) > BW_PERIOD)) {
this->base_time = this->next_time;
spa_log_debug(this->log, "%p: rate:%f "
"bw:%f dur:%"PRIu64" max:%f drift:%f",
this, corr, this->dll.bw, duration,
this->max_error, err);
if (SPA_UNLIKELY((this->next_time - this->base_time) > BW_PERIOD)) {
this->base_time = this->next_time;
spa_log_debug(this->log, "%p: rate:%f "
"bw:%f dur:%"PRIu64" max:%f drift:%f %"PRIu64" %"PRIu64,
this, corr, this->dll.bw, duration,
this->max_error, err, this->last_time, current_position);
}
} else {
this->next_time = nsec + duration * 1e9 / rate;
}
if (SPA_LIKELY(this->clock)) {