mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
node: add better node autolink
Add node media, category and role properties and use those to select the target node. Tag the DSP node with the right media.class
This commit is contained in:
parent
bd27b32c39
commit
10fff4b2f8
6 changed files with 166 additions and 55 deletions
|
|
@ -122,7 +122,11 @@ int main(int argc, char *argv[])
|
||||||
data.stream = pw_stream_new_simple(
|
data.stream = pw_stream_new_simple(
|
||||||
pw_main_loop_get_loop(data.loop),
|
pw_main_loop_get_loop(data.loop),
|
||||||
"audio-src",
|
"audio-src",
|
||||||
NULL,
|
pw_properties_new(
|
||||||
|
PW_NODE_PROP_MEDIA, "Audio",
|
||||||
|
PW_NODE_PROP_CATEGORY, "Playback",
|
||||||
|
PW_NODE_PROP_ROLE, "Music",
|
||||||
|
NULL),
|
||||||
&stream_events,
|
&stream_events,
|
||||||
&data);
|
&data);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -586,6 +586,9 @@ static void make_node(struct data *data)
|
||||||
props = pw_properties_new(PW_NODE_PROP_AUTOCONNECT, "1", NULL);
|
props = pw_properties_new(PW_NODE_PROP_AUTOCONNECT, "1", NULL);
|
||||||
if (data->path)
|
if (data->path)
|
||||||
pw_properties_set(props, PW_NODE_PROP_TARGET_NODE, data->path);
|
pw_properties_set(props, PW_NODE_PROP_TARGET_NODE, data->path);
|
||||||
|
pw_properties_set(props, PW_NODE_PROP_MEDIA, "Video");
|
||||||
|
pw_properties_set(props, PW_NODE_PROP_CATEGORY, "Capture");
|
||||||
|
pw_properties_set(props, PW_NODE_PROP_ROLE, "Camera");
|
||||||
|
|
||||||
data->node = pw_node_new(data->core, "SDL-sink", props, 0);
|
data->node = pw_node_new(data->core, "SDL-sink", props, 0);
|
||||||
data->impl_node = impl_node;
|
data->impl_node = impl_node;
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,11 @@ int main(int argc, char *argv[])
|
||||||
data.stream = pw_stream_new_simple(
|
data.stream = pw_stream_new_simple(
|
||||||
pw_main_loop_get_loop(data.loop),
|
pw_main_loop_get_loop(data.loop),
|
||||||
"video-play",
|
"video-play",
|
||||||
pw_properties_new("pipewire.client.reuse", "1", NULL),
|
pw_properties_new(
|
||||||
|
PW_NODE_PROP_MEDIA, "Video",
|
||||||
|
PW_NODE_PROP_CATEGORY, "Capture",
|
||||||
|
PW_NODE_PROP_ROLE, "Camera",
|
||||||
|
NULL),
|
||||||
&stream_events,
|
&stream_events,
|
||||||
&data);
|
&data);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -741,7 +741,15 @@ static struct pw_node *make_node(struct impl *impl, const struct pw_properties *
|
||||||
if ((alias = pw_properties_get(props, "alsa.card")) == NULL)
|
if ((alias = pw_properties_get(props, "alsa.card")) == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
node = pw_node_new(impl->core, node_name, NULL, sizeof(struct node));
|
node = pw_node_new(impl->core, node_name,
|
||||||
|
pw_properties_new(
|
||||||
|
"alsa.card", alias,
|
||||||
|
"media.class",
|
||||||
|
direction == PW_DIRECTION_OUTPUT ?
|
||||||
|
"Audio/DSP/Playback" :
|
||||||
|
"Audio/DSP/Capture",
|
||||||
|
NULL),
|
||||||
|
sizeof(struct node));
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
@ -815,7 +823,7 @@ static int on_global(void *data, struct pw_global *global)
|
||||||
if ((str = pw_properties_get(properties, "media.class")) == NULL)
|
if ((str = pw_properties_get(properties, "media.class")) == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (strcmp(str, "Audio/Sink") == 0 || strcmp(str, "Audio/Mixer") == 0) {
|
if (strcmp(str, "Audio/Sink") == 0) {
|
||||||
if ((ip = pw_node_get_free_port(n, PW_DIRECTION_INPUT)) == NULL)
|
if ((ip = pw_node_get_free_port(n, PW_DIRECTION_INPUT)) == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
if ((node = make_node(impl, properties, PW_DIRECTION_OUTPUT)) == NULL)
|
if ((node = make_node(impl, properties, PW_DIRECTION_OUTPUT)) == NULL)
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,6 @@ static void node_info_free(struct node_info *info)
|
||||||
free(info);
|
free(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void try_link_port(struct pw_node *node, struct pw_port *port, struct node_info *info);
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
link_port_unlinked(void *data, struct pw_port *port)
|
link_port_unlinked(void *data, struct pw_port *port)
|
||||||
{
|
{
|
||||||
|
|
@ -101,12 +99,8 @@ link_port_unlinked(void *data, struct pw_port *port)
|
||||||
struct node_info *info = ld->node_info;
|
struct node_info *info = ld->node_info;
|
||||||
struct pw_link *link = ld->link;
|
struct pw_link *link = ld->link;
|
||||||
struct impl *impl = info->impl;
|
struct impl *impl = info->impl;
|
||||||
struct pw_port *input = pw_link_get_input(link);
|
|
||||||
|
|
||||||
pw_log_debug("module %p: link %p: port %p unlinked", impl, link, port);
|
pw_log_debug("module %p: link %p: port %p unlinked", impl, link, port);
|
||||||
|
|
||||||
if (pw_port_get_direction(port) == PW_DIRECTION_OUTPUT && input)
|
|
||||||
try_link_port(pw_port_get_node(input), input, info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -184,38 +178,12 @@ static const struct pw_link_events link_events = {
|
||||||
.state_changed = link_state_changed,
|
.state_changed = link_state_changed,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void try_link_port(struct pw_node *node, struct pw_port *port, struct node_info *info)
|
static int link_ports(struct node_info *info, struct pw_port *port, struct pw_port *target)
|
||||||
{
|
{
|
||||||
struct impl *impl = info->impl;
|
struct impl *impl = info->impl;
|
||||||
const struct pw_properties *props;
|
|
||||||
const char *str;
|
|
||||||
uint32_t path_id;
|
|
||||||
char *error = NULL;
|
|
||||||
struct pw_link *link;
|
struct pw_link *link;
|
||||||
struct pw_port *target;
|
|
||||||
struct link_data *ld;
|
struct link_data *ld;
|
||||||
struct pw_global *global = pw_node_get_global(info->node);
|
char *error = NULL;
|
||||||
struct pw_client *owner = pw_global_get_owner(global);
|
|
||||||
|
|
||||||
props = pw_node_get_properties(node);
|
|
||||||
|
|
||||||
str = pw_properties_get(props, PW_NODE_PROP_TARGET_NODE);
|
|
||||||
if (str != NULL)
|
|
||||||
path_id = atoi(str);
|
|
||||||
else {
|
|
||||||
str = pw_properties_get(props, PW_NODE_PROP_AUTOCONNECT);
|
|
||||||
if (str == NULL || !pw_properties_parse_bool(str)) {
|
|
||||||
pw_log_debug("module %p: node does not need autoconnect", impl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
path_id = SPA_ID_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
pw_log_debug("module %p: try to find and link to node '%d'", impl, path_id);
|
|
||||||
|
|
||||||
target = pw_core_find_port(impl->core, port, path_id, NULL, 0, NULL, &error);
|
|
||||||
if (target == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if (pw_port_get_direction(port) == PW_DIRECTION_INPUT) {
|
if (pw_port_get_direction(port) == PW_DIRECTION_INPUT) {
|
||||||
struct pw_port *tmp = target;
|
struct pw_port *tmp = target;
|
||||||
|
|
@ -229,7 +197,7 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod
|
||||||
&error,
|
&error,
|
||||||
sizeof(struct link_data));
|
sizeof(struct link_data));
|
||||||
if (link == NULL)
|
if (link == NULL)
|
||||||
goto error;
|
return -ENOMEM;
|
||||||
|
|
||||||
ld = pw_link_get_user_data(link);
|
ld = pw_link_get_user_data(link);
|
||||||
ld->link = link;
|
ld->link = link;
|
||||||
|
|
@ -240,37 +208,153 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod
|
||||||
pw_link_register(link, NULL, pw_module_get_global(impl->module), NULL);
|
pw_link_register(link, NULL, pw_module_get_global(impl->module), NULL);
|
||||||
|
|
||||||
try_link_controls(impl, port, target);
|
try_link_controls(impl, port, target);
|
||||||
|
return 0;
|
||||||
return;
|
|
||||||
|
|
||||||
error:
|
|
||||||
pw_log_error("module %p: can't link node '%s'", impl, error);
|
|
||||||
if (owner)
|
|
||||||
pw_resource_error(pw_client_get_core_resource(owner), -EINVAL, error);
|
|
||||||
free(error);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void node_port_added(void *data, struct pw_port *port)
|
static void node_port_added(void *data, struct pw_port *port)
|
||||||
{
|
{
|
||||||
struct node_info *info = data;
|
|
||||||
try_link_port(info->node, port, info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void node_port_removed(void *data, struct pw_port *port)
|
static void node_port_removed(void *data, struct pw_port *port)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
static int on_node_port_added(void *data, struct pw_port *port)
|
static int on_node_port_added(void *data, struct pw_port *port)
|
||||||
{
|
{
|
||||||
node_port_added(data, port);
|
node_port_added(data, port);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void on_node_created(struct pw_node *node, struct node_info *info)
|
static int on_peer_port(void *data, struct pw_port *port)
|
||||||
{
|
{
|
||||||
pw_node_for_each_port(node, PW_DIRECTION_INPUT, on_node_port_added, info);
|
struct node_info *info = data;
|
||||||
pw_node_for_each_port(node, PW_DIRECTION_OUTPUT, on_node_port_added, info);
|
struct pw_port *p;
|
||||||
|
|
||||||
|
p = pw_node_get_free_port(info->node, pw_direction_reverse(port->direction));
|
||||||
|
if (p == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return link_ports(info, p, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct find_data {
|
||||||
|
struct node_info *info;
|
||||||
|
uint32_t path_id;
|
||||||
|
const char *media_class;
|
||||||
|
struct pw_global *global;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int find_global(void *data, struct pw_global *global)
|
||||||
|
{
|
||||||
|
struct find_data *find = data;
|
||||||
|
struct node_info *info = find->info;
|
||||||
|
struct impl *impl = info->impl;
|
||||||
|
struct pw_node *node;
|
||||||
|
const struct pw_properties *props;
|
||||||
|
const char *str;
|
||||||
|
|
||||||
|
if (pw_global_get_type(global) != impl->t->node)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pw_log_debug("module %p: looking at node '%d'", impl, pw_global_get_id(global));
|
||||||
|
|
||||||
|
node = pw_global_get_object(global);
|
||||||
|
|
||||||
|
if ((props = pw_node_get_properties(node)) == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((str = pw_properties_get(props, "media.class")) == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (strcmp(str, find->media_class) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pw_log_debug("module %p: found node '%d'", impl, pw_global_get_id(global));
|
||||||
|
|
||||||
|
find->global = global;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_node_created(struct node_info *info)
|
||||||
|
{
|
||||||
|
struct impl *impl = info->impl;
|
||||||
|
const struct pw_properties *props;
|
||||||
|
struct pw_node *peer;
|
||||||
|
const char *media, *category, *role, *str;
|
||||||
|
bool exclusive;
|
||||||
|
struct find_data find;
|
||||||
|
|
||||||
|
find.info = info;
|
||||||
|
|
||||||
|
props = pw_node_get_properties(info->node);
|
||||||
|
if (props == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
str = pw_properties_get(props, PW_NODE_PROP_AUTOCONNECT);
|
||||||
|
if (str == NULL || !pw_properties_parse_bool(str))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((media = pw_properties_get(props, PW_NODE_PROP_MEDIA)) == NULL)
|
||||||
|
media = "Audio";
|
||||||
|
|
||||||
|
if ((category = pw_properties_get(props, PW_NODE_PROP_CATEGORY)) == NULL) {
|
||||||
|
if (strcmp(media, "Video") == 0)
|
||||||
|
category = "Capture";
|
||||||
|
else
|
||||||
|
category = "Playback";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((role = pw_properties_get(props, PW_NODE_PROP_ROLE)) == NULL) {
|
||||||
|
if (strcmp(media, "Audio") == 0)
|
||||||
|
role = "Music";
|
||||||
|
else if (strcmp(media, "Video") == 0)
|
||||||
|
role = "Camera";
|
||||||
|
|
||||||
|
}
|
||||||
|
if ((str = pw_properties_get(props, PW_NODE_PROP_EXCLUSIVE)) != NULL)
|
||||||
|
exclusive = pw_properties_parse_bool(str);
|
||||||
|
else
|
||||||
|
exclusive = false;
|
||||||
|
|
||||||
|
pw_log_debug("module %p: '%s' '%s' '%s' %d", impl, media, category, role, exclusive);
|
||||||
|
|
||||||
|
if (strcmp(media, "Audio") == 0) {
|
||||||
|
if (strcmp(category, "Playback") == 0)
|
||||||
|
find.media_class = exclusive ? "Audio/Sink" : "Audio/DSP/Playback";
|
||||||
|
else if (strcmp(category, "Capture") == 0)
|
||||||
|
find.media_class = exclusive ? "Audio/Source" : "Audio/DSP/Capture";
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (strcmp(media, "Video") == 0) {
|
||||||
|
if (strcmp(category, "Capture") == 0)
|
||||||
|
find.media_class = "Video/Source";
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
str = pw_properties_get(props, PW_NODE_PROP_TARGET_NODE);
|
||||||
|
if (str != NULL)
|
||||||
|
find.path_id = atoi(str);
|
||||||
|
else
|
||||||
|
find.path_id = SPA_ID_INVALID;
|
||||||
|
|
||||||
|
pw_log_debug("module %p: try to find and link to node '%d'", impl, find.path_id);
|
||||||
|
|
||||||
|
find.global = NULL;
|
||||||
|
if (pw_core_for_each_global(impl->core, find_global, &find) != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
peer = pw_global_get_object(find.global);
|
||||||
|
|
||||||
|
if (strcmp(category, "Capture") == 0)
|
||||||
|
pw_node_for_each_port(peer, PW_DIRECTION_OUTPUT, on_peer_port, info);
|
||||||
|
if (strcmp(category, "Playback") == 0)
|
||||||
|
pw_node_for_each_port(peer, PW_DIRECTION_INPUT, on_peer_port, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -279,7 +363,7 @@ node_state_changed(void *data, enum pw_node_state old, enum pw_node_state state,
|
||||||
struct node_info *info = data;
|
struct node_info *info = data;
|
||||||
|
|
||||||
if (old == PW_NODE_STATE_CREATING && state == PW_NODE_STATE_SUSPENDED)
|
if (old == PW_NODE_STATE_CREATING && state == PW_NODE_STATE_SUSPENDED)
|
||||||
on_node_created(info->node, info);
|
on_node_created(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pw_node_events node_events = {
|
static const struct pw_node_events node_events = {
|
||||||
|
|
@ -309,7 +393,7 @@ core_global_added(void *data, struct pw_global *global)
|
||||||
pw_log_debug("module %p: node %p added", impl, node);
|
pw_log_debug("module %p: node %p added", impl, node);
|
||||||
|
|
||||||
if (pw_node_get_info(node)->state > PW_NODE_STATE_CREATING)
|
if (pw_node_get_info(node)->state > PW_NODE_STATE_CREATING)
|
||||||
on_node_created(node, ninfo);
|
on_node_created(ninfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,14 @@ struct pw_node_events {
|
||||||
void (*finish) (void *data);
|
void (*finish) (void *data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Media type of the node, Audio, Video */
|
||||||
|
#define PW_NODE_PROP_MEDIA "pipewire.media"
|
||||||
|
/** Category: Playback, Capture, Duplex */
|
||||||
|
#define PW_NODE_PROP_CATEGORY "pipewire.category"
|
||||||
|
/** Role: Movie, Music, Camera, Screen, Chat, Game, Speech, DSP */
|
||||||
|
#define PW_NODE_PROP_ROLE "pipewire.role"
|
||||||
|
/** exclusive access to device */
|
||||||
|
#define PW_NODE_PROP_EXCLUSIVE "pipewire.exclusive"
|
||||||
/** Automatically connect this node to a compatible node */
|
/** Automatically connect this node to a compatible node */
|
||||||
#define PW_NODE_PROP_AUTOCONNECT "pipewire.autoconnect"
|
#define PW_NODE_PROP_AUTOCONNECT "pipewire.autoconnect"
|
||||||
/** Try to connect the node to this node id */
|
/** Try to connect the node to this node id */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue