diff --git a/src/examples/audio-src.c b/src/examples/audio-src.c index 67a9aece6..00d8fc006 100644 --- a/src/examples/audio-src.c +++ b/src/examples/audio-src.c @@ -122,7 +122,11 @@ int main(int argc, char *argv[]) data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "audio-src", - NULL, + pw_properties_new( + PW_NODE_PROP_MEDIA, "Audio", + PW_NODE_PROP_CATEGORY, "Playback", + PW_NODE_PROP_ROLE, "Music", + NULL), &stream_events, &data); diff --git a/src/examples/export-sink.c b/src/examples/export-sink.c index 860deee47..047e73567 100644 --- a/src/examples/export-sink.c +++ b/src/examples/export-sink.c @@ -586,6 +586,9 @@ static void make_node(struct data *data) props = pw_properties_new(PW_NODE_PROP_AUTOCONNECT, "1", NULL); if (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->impl_node = impl_node; diff --git a/src/examples/video-play.c b/src/examples/video-play.c index 918a078b5..958ab5b8a 100644 --- a/src/examples/video-play.c +++ b/src/examples/video-play.c @@ -341,7 +341,11 @@ int main(int argc, char *argv[]) data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "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, &data); diff --git a/src/modules/module-audio-dsp.c b/src/modules/module-audio-dsp.c index 85ff1b6d8..60caf877e 100644 --- a/src/modules/module-audio-dsp.c +++ b/src/modules/module-audio-dsp.c @@ -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) 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) 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) 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) return 0; if ((node = make_node(impl, properties, PW_DIRECTION_OUTPUT)) == NULL) diff --git a/src/modules/module-autolink.c b/src/modules/module-autolink.c index 241afbd22..0ed3cf08e 100644 --- a/src/modules/module-autolink.c +++ b/src/modules/module-autolink.c @@ -92,8 +92,6 @@ static void node_info_free(struct node_info *info) free(info); } -static void try_link_port(struct pw_node *node, struct pw_port *port, struct node_info *info); - static void 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 pw_link *link = ld->link; 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); - - if (pw_port_get_direction(port) == PW_DIRECTION_OUTPUT && input) - try_link_port(pw_port_get_node(input), input, info); } static void @@ -184,38 +178,12 @@ static const struct pw_link_events link_events = { .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; - const struct pw_properties *props; - const char *str; - uint32_t path_id; - char *error = NULL; struct pw_link *link; - struct pw_port *target; struct link_data *ld; - struct pw_global *global = pw_node_get_global(info->node); - 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; + char *error = NULL; if (pw_port_get_direction(port) == PW_DIRECTION_INPUT) { 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, sizeof(struct link_data)); if (link == NULL) - goto error; + return -ENOMEM; ld = pw_link_get_user_data(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); try_link_controls(impl, port, target); - - 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; + return 0; } 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) { } +#if 0 static int on_node_port_added(void *data, struct pw_port *port) { node_port_added(data, port); 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); - pw_node_for_each_port(node, PW_DIRECTION_OUTPUT, on_node_port_added, info); + struct node_info *info = data; + 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 @@ -279,7 +363,7 @@ node_state_changed(void *data, enum pw_node_state old, enum pw_node_state state, struct node_info *info = data; 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 = { @@ -309,7 +393,7 @@ core_global_added(void *data, struct pw_global *global) pw_log_debug("module %p: node %p added", impl, node); if (pw_node_get_info(node)->state > PW_NODE_STATE_CREATING) - on_node_created(node, ninfo); + on_node_created(ninfo); } } diff --git a/src/pipewire/node.h b/src/pipewire/node.h index 8c25e9244..95e300540 100644 --- a/src/pipewire/node.h +++ b/src/pipewire/node.h @@ -91,6 +91,14 @@ struct pw_node_events { 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 */ #define PW_NODE_PROP_AUTOCONNECT "pipewire.autoconnect" /** Try to connect the node to this node id */