From 3cf182255ff5774d9d16f0d264948aabfe7ad547 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 29 Sep 2025 14:38:19 +0200 Subject: [PATCH] context: handle leaf nodes better Find leaf nodes by looking at the number of max in/out ports and the link group. This should give us nodes that only consume/produce data. If a leaf node is linked to a driver with only passive links, it will never be able to be scheduled unless we also make it runnable when the driver is made runnable from another node. This can happen when you do: pw-record -P '{ node.passive=true }' test.wav and then pw-record test2.wav Without this, the first pw-record would never be scheduled. With the patch it will be scheduled when the second pw-record is started. Fixes #4915 --- src/pipewire/context.c | 18 ++++++++++++++++-- src/pipewire/impl-node.c | 4 ++++ src/pipewire/private.h | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/pipewire/context.c b/src/pipewire/context.c index 7b6d426ab..8956c9c03 100644 --- a/src/pipewire/context.c +++ b/src/pipewire/context.c @@ -1101,11 +1101,25 @@ static int collect_nodes(struct pw_context *context, struct pw_impl_node *node, pw_log_debug(" next node %p: '%s' runnable:%u %p %p %p", n, n->name, n->runnable, n->groups, n->link_groups, sync); } - spa_list_for_each(n, collect, sort_link) - if (!n->driving && n->runnable) { + /* All non-driver runnable nodes (ie. reachable with a non-passive link) now make + * all linked nodes up and downstream runnable as well */ + spa_list_for_each(n, collect, sort_link) { + if (!n->driver && n->runnable) { run_nodes(context, n, collect, PW_DIRECTION_OUTPUT, 0); run_nodes(context, n, collect, PW_DIRECTION_INPUT, 0); } + } + /* now we might have made a driver runnable, if the node is not runnable at this point + * it means it was linked to the driver with passives links and some other node + * made the driver active. If the node is a leaf it can not be activated in any other + * way and we will also make it, and all its peers, runnable */ + spa_list_for_each(n, collect, sort_link) { + if (!n->driver && n->driver_node->runnable && !n->runnable && n->leaf) { + n->runnable = true; + run_nodes(context, n, collect, PW_DIRECTION_OUTPUT, 0); + run_nodes(context, n, collect, PW_DIRECTION_INPUT, 0); + } + } return 0; } diff --git a/src/pipewire/impl-node.c b/src/pipewire/impl-node.c index 75e945617..87fbe58e2 100644 --- a/src/pipewire/impl-node.c +++ b/src/pipewire/impl-node.c @@ -1309,6 +1309,10 @@ static void check_properties(struct pw_impl_node *node) } } node->lock_rate = pw_properties_get_bool(node->properties, PW_KEY_NODE_LOCK_RATE, false); + /* the leaf node is one that only produces/consumes the data. We can deduce this from the + * absence of a link-group and the fact that it has no output/input ports. */ + node->leaf = node->link_groups == NULL && + (node->info.max_input_ports == 0 || node->info.max_output_ports == 0); value = pw_properties_get_uint32(node->properties, PW_KEY_NODE_FORCE_RATE, SPA_ID_INVALID); if (value == 0) diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 072eac596..ebe0c6d5d 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -784,6 +784,7 @@ struct pw_impl_node { unsigned int async:1; /**< async processing, one cycle latency */ unsigned int lazy:1; /**< the graph is lazy scheduling */ unsigned int exclusive:1; /**< ports can only be linked once */ + unsigned int leaf:1; /**< node only produces/consumes data */ uint32_t transport; /**< latest transport request */