diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 56ca527a7..de4f2344d 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -1623,6 +1623,7 @@ int spa_alsa_reassign_follower(struct state *state) SPA_FLAG_IS_SET(state->position->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL); if (state->freewheel != freewheel) { + spa_log_debug(state->log, NAME" %p: freewheel %d->%d", state, state->freewheel, freewheel); state->freewheel = freewheel; if (freewheel) snd_pcm_pause(state->hndl, 1); diff --git a/spa/plugins/support/node-driver.c b/spa/plugins/support/node-driver.c index 32964110d..0d940056f 100644 --- a/spa/plugins/support/node-driver.c +++ b/spa/plugins/support/node-driver.c @@ -71,6 +71,7 @@ struct impl { struct itimerspec timerspec; bool started; + bool following; uint64_t next_time; }; @@ -79,6 +80,69 @@ static void reset_props(struct props *props) props->freewheel = DEFAULT_FREEWHEEL; } +static void set_timeout(struct impl *this, uint64_t next_time) +{ + spa_log_trace(this->log, "set timeout %"PRIu64, next_time); + this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC; + this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC; + spa_system_timerfd_settime(this->data_system, + this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL); +} + +static int set_timers(struct impl *this) +{ + struct timespec now; + int res; + + if ((res = spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now)) < 0) + return res; + this->next_time = SPA_TIMESPEC_TO_NSEC(&now); + + if (this->following) { + set_timeout(this, 0); + } else { + set_timeout(this, this->next_time); + } + return 0; +} + +static inline bool is_following(struct impl *this) +{ + return this->position && this->clock && this->position->clock.id != this->clock->id; +} + +static int do_reassign_follower(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + set_timers(this); + return 0; +} + +static int reassign_follower(struct impl *this) +{ + bool following; + + if (this->clock) + SPA_FLAG_UPDATE(this->clock->flags, + SPA_IO_CLOCK_FLAG_FREEWHEEL, this->props.freewheel); + + if (!this->started) + return 0; + + following = is_following(this); + if (following != this->following) { + spa_log_debug(this->log, NAME" %p: reassign follower %d->%d", this, this->following, following); + this->following = following; + spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); + } + return 0; +} + static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) { struct impl *this = object; @@ -99,15 +163,9 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) default: return -ENOENT; } - return 0; -} + reassign_follower(this); -static void set_timer(struct impl *this, uint64_t next_time) -{ - this->timerspec.it_value.tv_sec = next_time / SPA_NSEC_PER_SEC; - this->timerspec.it_value.tv_nsec = next_time % SPA_NSEC_PER_SEC; - spa_system_timerfd_settime(this->data_system, - this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL); + return 0; } static void on_timeout(struct spa_source *source) @@ -138,8 +196,6 @@ static void on_timeout(struct spa_source *source) this->next_time = nsec + duration * SPA_NSEC_PER_SEC / rate; if (SPA_LIKELY(this->clock)) { - SPA_FLAG_UPDATE(this->clock->flags, - SPA_IO_CLOCK_FLAG_FREEWHEEL, this->props.freewheel); this->clock->nsec = nsec; this->clock->position += duration; this->clock->duration = duration; @@ -151,7 +207,27 @@ static void on_timeout(struct spa_source *source) spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA | SPA_STATUS_NEED_DATA); - set_timer(this, this->next_time); + set_timeout(this, this->next_time); +} + +static int do_start(struct impl *this) +{ + if (this->started) + return 0; + + this->following = is_following(this); + set_timers(this); + this->started = true; + return 0; +} + +static int do_stop(struct impl *this) +{ + if (!this->started) + return 0; + this->started = false; + set_timeout(this, 0); + return 0; } static int impl_node_send_command(void *object, const struct spa_command *command) @@ -164,24 +240,11 @@ static int impl_node_send_command(void *object, const struct spa_command *comman switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: - { - struct timespec now; - - if (this->started) - return 0; - - clock_gettime(CLOCK_MONOTONIC, &now); - this->next_time = SPA_TIMESPEC_TO_NSEC(&now); - this->started = true; - set_timer(this, this->next_time); + do_start(this); break; - } case SPA_NODE_COMMAND_Suspend: case SPA_NODE_COMMAND_Pause: - if (!this->started) - return 0; - this->started = false; - set_timer(this, 0); + do_stop(this); break; default: return -ENOTSUP; @@ -249,7 +312,7 @@ static int impl_node_process(void *object) if (this->props.freewheel) { clock_gettime(CLOCK_MONOTONIC, &now); this->next_time = SPA_TIMESPEC_TO_NSEC(&now); - set_timer(this, this->next_time); + set_timeout(this, this->next_time); } return SPA_STATUS_OK; }