mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
media-session: fix linking nodes with targets to wrong nodes
For nodes that have node.target set, when the target is not available, media-session links them to any available node. If the target node appears afterward, media-session would not re-link them, leading to output being directed to wrong place (e.g. pavucontrol input monitor) even though the intended target or a better fallback is available. This occurs e.g. when devices are removed and re-added. Fix this by (i) for reconnectable nodes, reconnect them if target appears later, and (ii) for non-reconnectable nodes, raise error to the client if node.target is set but not found (but proceed to fallback if obj->target_node is set). Also slightly reorganize policy-node.c:rescan_node for clarity.
This commit is contained in:
parent
780f2d645d
commit
e2d810a9b9
1 changed files with 53 additions and 47 deletions
|
|
@ -762,12 +762,10 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
struct spa_dict *props;
|
||||
const char *str;
|
||||
bool exclusive, reconnect, autoconnect;
|
||||
struct find_data find;
|
||||
struct pw_node_info *info;
|
||||
struct node *peer;
|
||||
struct sm_object *obj;
|
||||
uint32_t path_id;
|
||||
bool follows_default;
|
||||
|
||||
if (!n->active) {
|
||||
pw_log_debug(NAME " %p: node %d is not active", impl, n->id);
|
||||
|
|
@ -794,18 +792,6 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
str = spa_dict_lookup(props, PW_KEY_NODE_DONT_RECONNECT);
|
||||
reconnect = str ? !pw_properties_parse_bool(str) : true;
|
||||
|
||||
follows_default = (impl->streams_follow_default &&
|
||||
n->type == NODE_TYPE_STREAM &&
|
||||
reconnect &&
|
||||
n->obj->target_node == NULL &&
|
||||
((str = spa_dict_lookup(props, PW_KEY_NODE_TARGET)) == NULL ||
|
||||
(uint32_t)atoi(str) == SPA_ID_INVALID));
|
||||
|
||||
if (n->peer != NULL && !follows_default) {
|
||||
pw_log_debug(NAME " %p: node %d is already linked", impl, n->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((str = spa_dict_lookup(props, PW_KEY_STREAM_DONT_REMIX)) != NULL)
|
||||
n->dont_remix = pw_properties_parse_bool(str);
|
||||
|
||||
|
|
@ -837,42 +823,41 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
|
||||
str = spa_dict_lookup(props, PW_KEY_NODE_EXCLUSIVE);
|
||||
exclusive = str ? pw_properties_parse_bool(str) : false;
|
||||
|
||||
pw_log_debug(NAME " %p: exclusive:%d", impl, exclusive);
|
||||
|
||||
spa_zero(find);
|
||||
find.impl = impl;
|
||||
find.media = n->media;
|
||||
find.capture_sink = n->capture_sink;
|
||||
find.direction = n->direction;
|
||||
find.exclusive = exclusive;
|
||||
find.link_group = n->peer == NULL ? spa_dict_lookup(props, PW_KEY_NODE_LINK_GROUP) : NULL;
|
||||
|
||||
/* we always honour the target node asked for by the client */
|
||||
path_id = SPA_ID_INVALID;
|
||||
if ((str = spa_dict_lookup(props, PW_KEY_NODE_TARGET)) != NULL)
|
||||
if ((str = spa_dict_lookup(props, PW_KEY_NODE_TARGET)) != NULL) {
|
||||
bool has_target = ((uint32_t)atoi(str) != SPA_ID_INVALID);
|
||||
path_id = find_device_for_name(impl, str);
|
||||
if (!reconnect && has_target && path_id == SPA_ID_INVALID) {
|
||||
/* don't use fallbacks for non-reconnecting nodes */
|
||||
peer = NULL;
|
||||
goto do_link;
|
||||
}
|
||||
}
|
||||
if (path_id == SPA_ID_INVALID && n->obj->target_node != NULL)
|
||||
path_id = find_device_for_name(impl, n->obj->target_node);
|
||||
|
||||
pw_log_info("trying to link node %d exclusive:%d reconnect:%d target:%d follows-default:%d", n->id,
|
||||
exclusive, reconnect, path_id, follows_default);
|
||||
|
||||
if (n->peer != NULL) {
|
||||
spa_list_for_each(peer, &impl->node_list, link)
|
||||
find_node(&find, peer);
|
||||
|
||||
if (follows_default && find.node != NULL &&
|
||||
find.node != n->peer && can_link(impl, n, find.node)) {
|
||||
pw_log_debug(NAME " %p: node %d follows default, changed (%d -> %d)", impl, n->id,
|
||||
n->peer->id, find.node->id);
|
||||
unlink_nodes(n, n->peer);
|
||||
} else {
|
||||
pw_log_debug(NAME " %p: node %d already linked (not changing)", impl, n->id);
|
||||
/* Do we need to check again where to link to? */
|
||||
bool target_found = (path_id != SPA_ID_INVALID);
|
||||
bool peer_is_target = (target_found && n->peer->obj->obj.id == path_id);
|
||||
bool follows_default = (impl->streams_follow_default &&
|
||||
n->type == NODE_TYPE_STREAM);
|
||||
bool recheck = !peer_is_target && (follows_default || target_found) &&
|
||||
reconnect;
|
||||
if (!recheck) {
|
||||
pw_log_debug(NAME " %p: node %d is already linked, peer-is-target:%d "
|
||||
"follows-default:%d", impl, n->id, peer_is_target,
|
||||
follows_default);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
pw_log_info("trying to link node %d exclusive:%d reconnect:%d target:%d",
|
||||
n->id, exclusive, reconnect, path_id);
|
||||
|
||||
if (path_id != SPA_ID_INVALID) {
|
||||
pw_log_debug(NAME " %p: target:%d", impl, path_id);
|
||||
|
||||
|
|
@ -884,23 +869,35 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
path_id, obj->type);
|
||||
if (spa_streq(obj->type, PW_TYPE_INTERFACE_Node)) {
|
||||
peer = sm_object_get_data(obj, SESSION_KEY);
|
||||
if (peer == NULL)
|
||||
return -ENOENT;
|
||||
goto do_link;
|
||||
}
|
||||
}
|
||||
pw_log_warn("node %d target:%d not found, find fallback:%d", n->id,
|
||||
path_id, reconnect);
|
||||
}
|
||||
|
||||
if (path_id == SPA_ID_INVALID && (reconnect || n->connect_count == 0)) {
|
||||
if (find.node == NULL)
|
||||
/* find fallback */
|
||||
struct find_data find;
|
||||
|
||||
spa_zero(find);
|
||||
find.impl = impl;
|
||||
find.media = n->media;
|
||||
find.capture_sink = n->capture_sink;
|
||||
find.direction = n->direction;
|
||||
find.exclusive = exclusive;
|
||||
find.link_group = n->peer == NULL ? spa_dict_lookup(props, PW_KEY_NODE_LINK_GROUP) : NULL;
|
||||
|
||||
spa_list_for_each(peer, &impl->node_list, link)
|
||||
find_node(&find, peer);
|
||||
|
||||
peer = find.node;
|
||||
} else {
|
||||
find.node = NULL;
|
||||
peer = NULL;
|
||||
}
|
||||
|
||||
if (find.node == NULL) {
|
||||
do_link:
|
||||
if (peer == NULL) {
|
||||
struct sm_object *obj;
|
||||
|
||||
if (!reconnect) {
|
||||
|
|
@ -925,8 +922,11 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
n->id, -ENOENT, "no node available");
|
||||
}
|
||||
return -ENOENT;
|
||||
} else if (peer == n->peer) {
|
||||
pw_log_debug(NAME " %p: node %d already linked to %d (not changing)",
|
||||
impl, n->id, peer->id);
|
||||
return 0;
|
||||
}
|
||||
peer = find.node;
|
||||
|
||||
if (exclusive && peer->obj->info->state == PW_NODE_STATE_RUNNING) {
|
||||
pw_log_warn("node %d busy, can't get exclusive access", peer->id);
|
||||
|
|
@ -934,15 +934,21 @@ static int rescan_node(struct impl *impl, struct node *n)
|
|||
}
|
||||
n->exclusive = exclusive;
|
||||
|
||||
pw_log_debug(NAME" %p: linking to node '%d'", impl, peer->id);
|
||||
if (n->peer != NULL) {
|
||||
pw_log_debug(NAME " %p: node %d change link (%d -> %d)", impl, n->id,
|
||||
n->peer->id, peer->id);
|
||||
unlink_nodes(n, n->peer);
|
||||
}
|
||||
|
||||
do_link:
|
||||
if (peer == n->failed_peer && n->failed_count > MAX_LINK_RETRY) {
|
||||
/* Break rescan -> failed link -> rescan loop. */
|
||||
pw_log_debug(NAME" %p: tried to link '%d' on last rescan, not retrying",
|
||||
impl, peer->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pw_log_debug(NAME" %p: linking node %d to node %d", impl, n->id, peer->id);
|
||||
|
||||
link_nodes(n, peer);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue