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:
Wim Taymans 2018-04-12 09:52:15 +02:00
parent bd27b32c39
commit 10fff4b2f8
6 changed files with 166 additions and 55 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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);
} }
} }

View file

@ -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 */