mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-05 13:30:02 -05:00
media-session: simplify target node handling + fix priorities
Handle all node.target behavior in rescan_node. Make distinction between target nodes set in session manager via metadata or via restore-stream; priorities are metadata > client's node.target > restore-stream. Allow metadata override to also remove the target node setting.
This commit is contained in:
parent
e2d810a9b9
commit
8c6b111ea6
3 changed files with 81 additions and 118 deletions
|
|
@ -155,8 +155,8 @@ struct sm_node {
|
||||||
struct pw_node_info *info;
|
struct pw_node_info *info;
|
||||||
struct spa_list port_list;
|
struct spa_list port_list;
|
||||||
|
|
||||||
char *target_node; /** desired target node from stored
|
char *target_node; /**< desired target node */
|
||||||
* preferences */
|
unsigned int fixed_target:1; /**< target_node has priority over node.target */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sm_port {
|
struct sm_port {
|
||||||
|
|
|
||||||
|
|
@ -127,14 +127,11 @@ struct node {
|
||||||
unsigned int configured:1;
|
unsigned int configured:1;
|
||||||
unsigned int dont_remix:1;
|
unsigned int dont_remix:1;
|
||||||
unsigned int monitor:1;
|
unsigned int monitor:1;
|
||||||
unsigned int moving:1;
|
|
||||||
unsigned int capture_sink:1;
|
unsigned int capture_sink:1;
|
||||||
unsigned int virtual:1;
|
unsigned int virtual:1;
|
||||||
unsigned int linking:1;
|
unsigned int linking:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int check_new_target(struct impl *impl, struct node *target);
|
|
||||||
|
|
||||||
static bool find_format(struct node *node)
|
static bool find_format(struct node *node)
|
||||||
{
|
{
|
||||||
struct impl *impl = node->impl;
|
struct impl *impl = node->impl;
|
||||||
|
|
@ -235,8 +232,10 @@ static int configure_node(struct node *node, struct spa_audio_info *info, bool f
|
||||||
|
|
||||||
node->configured = true;
|
node->configured = true;
|
||||||
|
|
||||||
if (node->type == NODE_TYPE_DEVICE)
|
if (node->type == NODE_TYPE_DEVICE) {
|
||||||
check_new_target(impl, node);
|
/* Schedule rescan in case we need to move streams */
|
||||||
|
sm_media_session_schedule_rescan(impl->session);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -717,14 +716,7 @@ static int link_nodes(struct node *node, struct node *peer)
|
||||||
|
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
node->peer = peer;
|
node->peer = peer;
|
||||||
node->failed_peer = NULL;
|
|
||||||
node->connect_count++;
|
node->connect_count++;
|
||||||
node->failed_count = 0;
|
|
||||||
} else {
|
|
||||||
if (node->failed_peer != peer)
|
|
||||||
node->failed_count = 0;
|
|
||||||
node->failed_peer = peer;
|
|
||||||
node->failed_count++;
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -757,6 +749,42 @@ static int unlink_nodes(struct node *node, struct node *peer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int relink_node(struct impl *impl, struct node *n, struct node *peer)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
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 -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->failed_peer != peer)
|
||||||
|
n->failed_count = 0;
|
||||||
|
n->failed_peer = peer;
|
||||||
|
n->failed_count++;
|
||||||
|
|
||||||
|
if (!can_link(impl, n, peer)) {
|
||||||
|
pw_log_debug("can't link node %d to %d: same link-group", n->id,
|
||||||
|
peer->id);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->peer != NULL)
|
||||||
|
if ((res = unlink_nodes(n, n->peer)) < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
pw_log_debug(NAME" %p: linking node %d to node %d", impl, n->id, peer->id);
|
||||||
|
|
||||||
|
/* NB. if link_nodes returns error, n may have been invalidated */
|
||||||
|
if ((res = link_nodes(n, peer)) > 0) {
|
||||||
|
n->failed_peer = NULL;
|
||||||
|
n->failed_count = 0;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static int rescan_node(struct impl *impl, struct node *n)
|
static int rescan_node(struct impl *impl, struct node *n)
|
||||||
{
|
{
|
||||||
struct spa_dict *props;
|
struct spa_dict *props;
|
||||||
|
|
@ -771,10 +799,6 @@ static int rescan_node(struct impl *impl, struct node *n)
|
||||||
pw_log_debug(NAME " %p: node %d is not active", impl, n->id);
|
pw_log_debug(NAME " %p: node %d is not active", impl, n->id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (n->moving) {
|
|
||||||
pw_log_debug(NAME " %p: node %d is moving", impl, n->id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n->type == NODE_TYPE_DEVICE) {
|
if (n->type == NODE_TYPE_DEVICE) {
|
||||||
configure_node(n, NULL, false);
|
configure_node(n, NULL, false);
|
||||||
|
|
@ -825,9 +849,12 @@ static int rescan_node(struct impl *impl, struct node *n)
|
||||||
exclusive = str ? pw_properties_parse_bool(str) : false;
|
exclusive = str ? pw_properties_parse_bool(str) : false;
|
||||||
pw_log_debug(NAME " %p: exclusive:%d", impl, exclusive);
|
pw_log_debug(NAME " %p: exclusive:%d", impl, exclusive);
|
||||||
|
|
||||||
/* we always honour the target node asked for by the client */
|
/* honor target node set by user or asked for by the client */
|
||||||
path_id = SPA_ID_INVALID;
|
path_id = SPA_ID_INVALID;
|
||||||
if ((str = spa_dict_lookup(props, PW_KEY_NODE_TARGET)) != NULL) {
|
if (n->obj->target_node != NULL)
|
||||||
|
path_id = find_device_for_name(impl, n->obj->target_node);
|
||||||
|
if (!n->obj->fixed_target &&
|
||||||
|
(str = spa_dict_lookup(props, PW_KEY_NODE_TARGET)) != NULL) {
|
||||||
bool has_target = ((uint32_t)atoi(str) != SPA_ID_INVALID);
|
bool has_target = ((uint32_t)atoi(str) != SPA_ID_INVALID);
|
||||||
path_id = find_device_for_name(impl, str);
|
path_id = find_device_for_name(impl, str);
|
||||||
if (!reconnect && has_target && path_id == SPA_ID_INVALID) {
|
if (!reconnect && has_target && path_id == SPA_ID_INVALID) {
|
||||||
|
|
@ -836,8 +863,6 @@ static int rescan_node(struct impl *impl, struct node *n)
|
||||||
goto do_link;
|
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);
|
|
||||||
|
|
||||||
if (n->peer != NULL) {
|
if (n->peer != NULL) {
|
||||||
/* Do we need to check again where to link to? */
|
/* Do we need to check again where to link to? */
|
||||||
|
|
@ -934,23 +959,7 @@ do_link:
|
||||||
}
|
}
|
||||||
n->exclusive = exclusive;
|
n->exclusive = exclusive;
|
||||||
|
|
||||||
if (n->peer != NULL) {
|
return relink_node(impl, n, peer);
|
||||||
pw_log_debug(NAME " %p: node %d change link (%d -> %d)", impl, n->id,
|
|
||||||
n->peer->id, peer->id);
|
|
||||||
unlink_nodes(n, n->peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void session_info(void *data, const struct pw_core_info *info)
|
static void session_info(void *data, const struct pw_core_info *info)
|
||||||
|
|
@ -1043,68 +1052,6 @@ static const struct sm_media_session_events session_events = {
|
||||||
.destroy = session_destroy,
|
.destroy = session_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int do_move_node(struct node *n, struct node *src, struct node *dst)
|
|
||||||
{
|
|
||||||
n->moving = true;
|
|
||||||
if (src)
|
|
||||||
unlink_nodes(n, src);
|
|
||||||
if (dst)
|
|
||||||
link_nodes(n, dst);
|
|
||||||
n->moving = false;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_move(struct impl *impl, struct node *src_node, struct node *dst_node)
|
|
||||||
{
|
|
||||||
const char *str;
|
|
||||||
struct pw_node_info *info;
|
|
||||||
|
|
||||||
if (src_node->peer == dst_node)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((info = src_node->obj->info) == NULL)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
if ((str = spa_dict_lookup(info->props, PW_KEY_NODE_DONT_RECONNECT)) != NULL &&
|
|
||||||
pw_properties_parse_bool(str)) {
|
|
||||||
pw_log_warn("can't reconnect node %d to %d: dont-reconnect flag set",
|
|
||||||
src_node->id, dst_node->id);
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
if (!can_link(impl, src_node, dst_node)) {
|
|
||||||
pw_log_debug("can't link node %d to %d: same link-group", src_node->id,
|
|
||||||
dst_node->id);
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
pw_log_info("move node %d: from peer %d to %d", src_node->id,
|
|
||||||
src_node->peer ? src_node->peer->id : SPA_ID_INVALID,
|
|
||||||
dst_node->id);
|
|
||||||
|
|
||||||
free(src_node->obj->target_node);
|
|
||||||
str = get_device_name(dst_node);
|
|
||||||
src_node->obj->target_node = str ? strdup(str) : NULL;
|
|
||||||
|
|
||||||
return do_move_node(src_node, src_node->peer, dst_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_new_target(struct impl *impl, struct node *target)
|
|
||||||
{
|
|
||||||
struct node *node;
|
|
||||||
const char *str = get_device_name(target);
|
|
||||||
|
|
||||||
spa_list_for_each(node, &impl->node_list, link) {
|
|
||||||
pw_log_debug(NAME" %p: node %d target '%s' find:%s", impl,
|
|
||||||
node->id, node->obj->target_node, str);
|
|
||||||
|
|
||||||
if (node->obj->target_node != NULL &&
|
|
||||||
spa_streq(node->obj->target_node , str)) {
|
|
||||||
handle_move(impl, node, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int metadata_property(void *object, uint32_t subject,
|
static int metadata_property(void *object, uint32_t subject,
|
||||||
const char *key, const char *type, const char *value)
|
const char *key, const char *type, const char *value)
|
||||||
{
|
{
|
||||||
|
|
@ -1143,26 +1090,40 @@ static int metadata_property(void *object, uint32_t subject,
|
||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
sm_media_session_schedule_rescan(impl->session);
|
sm_media_session_schedule_rescan(impl->session);
|
||||||
} else if (key != NULL && spa_streq(key, "target.node")) {
|
} else if (key == NULL || spa_streq(key, "target.node")) {
|
||||||
if (value != NULL) {
|
|
||||||
struct node *src_node, *dst_node;
|
|
||||||
|
|
||||||
dst_node = find_node_by_id_name(impl, SPA_ID_INVALID, value);
|
|
||||||
src_node = dst_node ? find_node_by_id_name(impl, subject, NULL) : NULL;
|
|
||||||
|
|
||||||
if (dst_node && src_node)
|
|
||||||
handle_move(impl, src_node, dst_node);
|
|
||||||
} else {
|
|
||||||
/* Unset target node. Schedule rescan to re-link, if needed. */
|
|
||||||
struct node *src_node;
|
struct node *src_node;
|
||||||
|
|
||||||
src_node = find_node_by_id_name(impl, subject, NULL);
|
src_node = find_node_by_id_name(impl, subject, NULL);
|
||||||
if (src_node) {
|
if (!src_node)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Set target and schedule rescan */
|
||||||
|
if (key == NULL || value == NULL) {
|
||||||
free(src_node->obj->target_node);
|
free(src_node->obj->target_node);
|
||||||
src_node->obj->target_node = NULL;
|
src_node->obj->target_node = NULL;
|
||||||
|
src_node->obj->fixed_target = false;
|
||||||
|
} else {
|
||||||
|
const char *str;
|
||||||
|
struct node *dst_node;
|
||||||
|
|
||||||
|
dst_node = find_node_by_id_name(impl, SPA_ID_INVALID, value);
|
||||||
|
if (dst_node) {
|
||||||
|
str = get_device_name(dst_node);
|
||||||
|
if (!str)
|
||||||
|
return 0;
|
||||||
|
} else if ((uint32_t)atoi(value) == SPA_ID_INVALID) {
|
||||||
|
str = NULL;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(src_node->obj->target_node);
|
||||||
|
src_node->obj->target_node = str ? strdup(str) : NULL;
|
||||||
|
src_node->obj->fixed_target = true;
|
||||||
|
}
|
||||||
|
|
||||||
sm_media_session_schedule_rescan(impl->session);
|
sm_media_session_schedule_rescan(impl->session);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -372,8 +372,10 @@ static int restore_stream(struct stream *str)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
pw_log_info("stream %d: target '%s'", str->obj->obj.id, name);
|
pw_log_info("stream %d: target '%s'", str->obj->obj.id, name);
|
||||||
|
if (!str->obj->fixed_target) {
|
||||||
free(str->obj->target_node);
|
free(str->obj->target_node);
|
||||||
str->obj->target_node = strdup(name);
|
str->obj->target_node = strdup(name);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (spa_json_next(&it[1], &value) <= 0)
|
if (spa_json_next(&it[1], &value) <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue