context: handle transport updates better

The transport update is set in the node properties. If one client tries
to start and another tries to stop we have two conflicting desired states
in the nodes.

Fix this by making the state update a one time property and remove it
from the sever and client properties after updating it. We then need to
keep the new state around and apply it once.

Keep the node driver state as it is unless there was a transport state
update.

Fixes #4543
This commit is contained in:
Wim Taymans 2025-02-11 12:01:20 +01:00
parent 98b6469b88
commit d5f0c8a550
4 changed files with 33 additions and 18 deletions

View file

@ -7297,6 +7297,8 @@ static int transport_update(struct client* c, int active)
PW_CLIENT_NODE_UPDATE_INFO, PW_CLIENT_NODE_UPDATE_INFO,
0, NULL, &c->info); 0, NULL, &c->info);
c->info.change_mask = 0; c->info.change_mask = 0;
pw_properties_set(c->props, PW_KEY_NODE_TRANSPORT, NULL);
pw_thread_loop_unlock(c->context.loop); pw_thread_loop_unlock(c->context.loop);
return 0; return 0;

View file

@ -1519,8 +1519,8 @@ int pw_context_recalc_graph(struct pw_context *context, const char *reason)
struct pw_impl_node *n, *s, *target, *fallback; struct pw_impl_node *n, *s, *target, *fallback;
const uint32_t *rates; const uint32_t *rates;
uint32_t max_quantum, min_quantum, def_quantum, rate_quantum, floor_quantum, ceil_quantum; uint32_t max_quantum, min_quantum, def_quantum, rate_quantum, floor_quantum, ceil_quantum;
uint32_t n_rates, def_rate; uint32_t n_rates, def_rate, transport;
bool freewheel, global_force_rate, global_force_quantum, transport_start; bool freewheel, global_force_rate, global_force_quantum;
struct spa_list collect; struct spa_list collect;
pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason); pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason);
@ -1533,7 +1533,6 @@ int pw_context_recalc_graph(struct pw_context *context, const char *reason)
again: again:
impl->recalc = true; impl->recalc = true;
freewheel = false; freewheel = false;
transport_start = false;
/* clean up the flags first */ /* clean up the flags first */
spa_list_for_each(n, &context->node_list, link) { spa_list_for_each(n, &context->node_list, link) {
@ -1766,10 +1765,16 @@ again:
/* Here we are allowed to change the rate of the driver. /* Here we are allowed to change the rate of the driver.
* Start with the default rate. If the desired rate is * Start with the default rate. If the desired rate is
* allowed, switch to it */ * allowed, switch to it */
target_rate = node_def_rate;
if (rate.denom != 0 && rate.num == 1) if (rate.denom != 0 && rate.num == 1)
target_rate = find_best_rate(node_rates, node_n_rates, target_rate = rate.denom;
rate.denom, target_rate); else
target_rate = node_def_rate;
target_rate = find_best_rate(node_rates, node_n_rates,
target_rate, node_def_rate);
pw_log_debug("%p: def_rate:%d target_rate:%d rate:%d/%d", context,
node_def_rate, target_rate, rate.num, rate.denom);
} }
was_target_pending = n->target_pending; was_target_pending = n->target_pending;
@ -1881,10 +1886,14 @@ again:
n->rt.position->clock.target_duration, n->rt.position->clock.target_duration,
n->rt.position->clock.target_rate.denom, n->name); n->rt.position->clock.target_rate.denom, n->name);
transport = PW_NODE_ACTIVATION_COMMAND_NONE;
/* first change the node states of the followers to the new target */ /* first change the node states of the followers to the new target */
spa_list_for_each(s, &n->follower_list, follower_link) { spa_list_for_each(s, &n->follower_list, follower_link) {
if (s->transport) if (s->transport != PW_NODE_ACTIVATION_COMMAND_NONE) {
transport_start = true; transport = s->transport;
s->transport = PW_NODE_ACTIVATION_COMMAND_NONE;
}
if (s == n) if (s == n)
continue; continue;
pw_log_debug("%p: follower %p: active:%d '%s'", pw_log_debug("%p: follower %p: active:%d '%s'",
@ -1892,10 +1901,10 @@ again:
ensure_state(s, running); ensure_state(s, running);
} }
SPA_ATOMIC_STORE(n->rt.target.activation->command, if (transport != PW_NODE_ACTIVATION_COMMAND_NONE) {
transport_start ? pw_log_info("%s: transport %d", n->name, transport);
PW_NODE_ACTIVATION_COMMAND_START : SPA_ATOMIC_STORE(n->rt.target.activation->command, transport);
PW_NODE_ACTIVATION_COMMAND_STOP); }
/* now that all the followers are ready, start the driver */ /* now that all the followers are ready, start the driver */
ensure_state(n, running); ensure_state(n, running);

View file

@ -1119,7 +1119,7 @@ static void check_properties(struct pw_impl_node *node)
const char *str, *recalc_reason = NULL; const char *str, *recalc_reason = NULL;
struct spa_fraction frac; struct spa_fraction frac;
uint32_t value; uint32_t value;
bool driver, trigger, transport, sync, async; bool driver, trigger, sync, async;
struct match match; struct match match;
match = MATCH_INIT(node); match = MATCH_INIT(node);
@ -1222,10 +1222,13 @@ static void check_properties(struct pw_impl_node *node)
recalc_reason = "sync changed"; recalc_reason = "sync changed";
} }
transport = pw_properties_get_bool(node->properties, PW_KEY_NODE_TRANSPORT, false); str = pw_properties_get(node->properties, PW_KEY_NODE_TRANSPORT);
if (transport != node->transport) { if (str != NULL) {
pw_log_info("%p: transport %d -> %d", node, node->transport, transport); node->transport = spa_atob(str) ?
node->transport = transport; PW_NODE_ACTIVATION_COMMAND_START :
PW_NODE_ACTIVATION_COMMAND_STOP;
pw_log_info("%p: transport %d", node, node->transport);
pw_properties_set(node->properties, PW_KEY_NODE_TRANSPORT, NULL);
recalc_reason = "transport changed"; recalc_reason = "transport changed";
} }
async = pw_properties_get_bool(node->properties, PW_KEY_NODE_ASYNC, false); async = pw_properties_get_bool(node->properties, PW_KEY_NODE_ASYNC, false);

View file

@ -795,10 +795,11 @@ struct pw_impl_node {
unsigned int can_suspend:1; unsigned int can_suspend:1;
unsigned int checked; /**< for sorting */ unsigned int checked; /**< for sorting */
unsigned int sync:1; /**< the sync-groups are active */ unsigned int sync:1; /**< the sync-groups are active */
unsigned int transport:1; /**< the transport is active */
unsigned int async:1; /**< async processing, one cycle latency */ unsigned int async:1; /**< async processing, one cycle latency */
unsigned int lazy:1; /**< the graph is lazy scheduling */ unsigned int lazy:1; /**< the graph is lazy scheduling */
uint32_t transport; /**< latest transport request */
uint32_t port_user_data_size; /**< extra size for port user data */ uint32_t port_user_data_size; /**< extra size for port user data */
struct spa_list driver_link; struct spa_list driver_link;