diff --git a/spa/include/spa/node/io.h b/spa/include/spa/node/io.h index 8dc326c67..166529ac1 100644 --- a/spa/include/spa/node/io.h +++ b/spa/include/spa/node/io.h @@ -124,7 +124,13 @@ struct spa_io_clock { * positive for capture, negative for playback */ double rate_diff; /**< rate difference between clock and monotonic time */ uint64_t next_nsec; /**< estimated next wakeup time in nanoseconds */ - uint32_t padding[8]; + + struct spa_fraction target_rate; /**< target rate of next cycle */ + uint64_t target_duration; /**< target duration of next cycle */ + uint32_t target_seq; /**< seq counter. must be equal at start and + * end of read and lower bit must be 0 */ + + uint32_t padding[3]; }; /* the size of the video in this cycle */ diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 6da75c049..b515d1736 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -2466,6 +2466,8 @@ static void alsa_on_timeout_event(struct spa_source *source) return; } } + state->position->clock.duration = state->position->clock.target_duration; + state->position->clock.rate = state->position->clock.target_rate; check_position_config(state); @@ -2550,6 +2552,8 @@ int spa_alsa_start(struct state *state) return 0; if (state->position) { + state->position->clock.duration = state->position->clock.target_duration; + state->position->clock.rate = state->position->clock.target_rate; state->duration = state->position->clock.duration; state->rate_denom = state->position->clock.rate.denom; } diff --git a/src/pipewire/context.c b/src/pipewire/context.c index 7e7dd5c7e..3043e3439 100644 --- a/src/pipewire/context.c +++ b/src/pipewire/context.c @@ -1375,16 +1375,17 @@ again: n->target_pending = true; } - if (n->info.state < PW_NODE_STATE_RUNNING && n->target_pending) { - /* the driver node is not actually running and we have a - * pending change. Apply the change to the position now so - * that we have the right values when we change the node - * states of the driver and followers to RUNNING below */ + if (n->target_pending) { + /* we have a pending change. We place the new values in the + * pending fields so that they are picked up by the driver in + * the next cycle */ pw_log_debug("%p: apply duration:%"PRIu64" rate:%u/%u", context, n->target_quantum, n->target_rate.num, n->target_rate.denom); - n->rt.position->clock.duration = n->target_quantum; - n->rt.position->clock.rate = n->target_rate; + SEQ_WRITE(n->rt.position->clock.target_seq); + n->rt.position->clock.target_duration = n->target_quantum; + n->rt.position->clock.target_rate = n->target_rate; + SEQ_WRITE(n->rt.position->clock.target_seq); n->target_pending = false; } diff --git a/src/pipewire/impl-node.c b/src/pipewire/impl-node.c index a985f71f2..fd4d2dab6 100644 --- a/src/pipewire/impl-node.c +++ b/src/pipewire/impl-node.c @@ -688,8 +688,9 @@ static void update_io(struct pw_impl_node *node) pw_log_debug("%p: set position %p", node, &node->rt.activation->position); node->rt.position = &node->rt.activation->position; - node->target_rate = node->rt.position->clock.rate; - node->target_quantum = node->rt.position->clock.duration; + node->target_rate = node->rt.position->clock.target_rate; + node->target_quantum = node->rt.position->clock.target_duration; + node->target_pending = false; } else if (node->driver) { pw_log_warn("%p: can't set position on driver", node); } @@ -804,8 +805,8 @@ do_move_nodes(struct spa_loop *loop, pw_log_trace("%p: set position %p", node, &driver->rt.activation->position); node->rt.position = &driver->rt.activation->position; - node->target_rate = node->rt.position->clock.rate; - node->target_quantum = node->rt.position->clock.duration; + node->target_rate = node->rt.position->clock.target_rate; + node->target_quantum = node->rt.position->clock.target_duration; if (node->source.loop != NULL) { remove_node(node); @@ -1223,8 +1224,8 @@ static void reset_position(struct pw_impl_node *this, struct spa_io_position *po this->target_rate = SPA_FRACTION(1, rate); this->target_quantum = quantum; - pos->clock.rate = this->target_rate; - pos->clock.duration = this->target_quantum; + pos->clock.rate = pos->clock.target_rate = this->target_rate; + pos->clock.duration = pos->clock.target_duration = this->target_quantum; pos->video.flags = SPA_IO_VIDEO_SIZE_VALID; pos->video.size = s->video_size; pos->video.stride = pos->video.size.width * 16; @@ -1683,15 +1684,15 @@ static int node_ready(void *data, int status) node->rt.target.signal_func(node->rt.target.data); } - if (node->target_pending) { - pw_log_debug("apply quantum %"PRIu64"->%"PRIu64" %d->%d", - node->rt.position->clock.duration, - node->target_quantum, - node->rt.position->clock.rate.denom, - node->target_rate.denom); - node->rt.position->clock.duration = node->target_quantum; - node->rt.position->clock.rate = node->target_rate; - node->target_pending = false; + /* This update is done too late, the driver should do this + * before calling the ready callback so that it can use the new target + * duration and rate to schedule the next update. We do this here to + * help drivers that don't support this yet */ + if (node->rt.position->clock.duration != node->rt.position->clock.target_duration || + node->rt.position->clock.rate.denom != node->rt.position->clock.target_rate.denom) { + pw_log_warn("driver %s did not update duration/rate", node->name); + node->rt.position->clock.duration = node->rt.position->clock.target_duration; + node->rt.position->clock.rate = node->rt.position->clock.target_rate; } sync_type = check_updates(node, &reposition_owner);