From 3c2ab98a2a0ab48f087c375ec9ff5a69b0c64dcf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 2 Dec 2020 15:40:23 +0100 Subject: [PATCH] Add support for virtual source A virtual source is usually implemented with a null-sink, it looks like a source to pulseaudio clients but just forwards what it gets as input. Make sure the port names make sense. You can use the null sink now as: pactl load-module module-null-sink sink_name=source object.linger=1 media.class=Audio/Source/Virtual channel_map=FL,FR,RL,RR This creates a node with "input" and "capture" ports and looks like a virtual source for pulseaudio clients. pactl load-module module-null-sink sink_name=source object.linger=1 media.class=Audio/Sink channel_map=FL,FR,RL,RR This creates a node with "playback" and "monitor" ports and looks like a virtual sink for pulseaudio clients. pactl load-module module-null-sink sink_name=source object.linger=1 media.class=Audio/Duplex channel_map=FL,FR,RL,RR This creates a node with "playback" and "capture" ports and looks like a virtual source and sink for pulseaudio clients. --- src/examples/media-session/policy-node.c | 4 +- src/modules/module-adapter/adapter.c | 13 +++-- src/modules/module-protocol-pulse/collect.c | 47 +++++++++---------- .../module-protocol-pulse/pulse-server.c | 5 +- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/examples/media-session/policy-node.c b/src/examples/media-session/policy-node.c index 1ef56b70d..83f092f1d 100644 --- a/src/examples/media-session/policy-node.c +++ b/src/examples/media-session/policy-node.c @@ -287,7 +287,9 @@ handle_node(struct impl *impl, struct sm_object *object) else return 0; - if (strcmp(media_class, "Sink") == 0 || strcmp(media_class, "Duplex") == 0) + if (strcmp(media_class, "Sink") == 0 || + strcmp(media_class, "Duplex") == 0 || + strcmp(media_class, "Source/Virtual") == 0) direction = PW_DIRECTION_INPUT; else if (strcmp(media_class, "Source") == 0) direction = PW_DIRECTION_OUTPUT; diff --git a/src/modules/module-adapter/adapter.c b/src/modules/module-adapter/adapter.c index 2946b441e..d279c5d93 100644 --- a/src/modules/module-adapter/adapter.c +++ b/src/modules/module-adapter/adapter.c @@ -89,7 +89,7 @@ static void node_port_init(void *data, struct pw_impl_port *port) struct pw_properties *new; const char *str, *path, *node_name, *media_class; char position[8], *prefix; - bool is_monitor, is_device, is_duplex; + bool is_monitor, is_device, is_duplex, is_virtual; direction = pw_impl_port_get_direction(port); @@ -112,14 +112,19 @@ static void node_port_init(void *data, struct pw_impl_port *port) is_device = false; is_duplex = media_class != NULL && strstr(media_class, "Duplex") != NULL; + is_virtual = media_class != NULL && strstr(media_class, "Virtual") != NULL; new = pw_properties_new(NULL, NULL); - if (is_monitor && !is_duplex) - prefix = "monitor"; - else if (is_device || is_duplex) + if (is_duplex) prefix = direction == PW_DIRECTION_INPUT ? "playback" : "capture"; + else if (is_virtual) + prefix = direction == PW_DIRECTION_INPUT ? + "input" : "capture"; + else if (is_device) + prefix = direction == PW_DIRECTION_INPUT ? + "playback" : is_monitor ? "monitor" : "capture"; else prefix = direction == PW_DIRECTION_INPUT ? "input" : "output"; diff --git a/src/modules/module-protocol-pulse/collect.c b/src/modules/module-protocol-pulse/collect.c index 4d8d0f210..a479db225 100644 --- a/src/modules/module-protocol-pulse/collect.c +++ b/src/modules/module-protocol-pulse/collect.c @@ -35,31 +35,30 @@ static bool object_is_module(struct pw_manager_object *o) static bool object_is_card(struct pw_manager_object *o) { const char *str; - return - strcmp(o->type, PW_TYPE_INTERFACE_Device) == 0 && - o->props != NULL && - (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && - strcmp(str, "Audio/Device") == 0; + return strcmp(o->type, PW_TYPE_INTERFACE_Device) == 0 && + o->props != NULL && + (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && + strcmp(str, "Audio/Device") == 0; } static bool object_is_sink(struct pw_manager_object *o) { const char *str; - return - strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && - o->props != NULL && - (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && - (strcmp(str, "Audio/Sink") == 0 || strcmp(str, "Audio/Duplex") == 0); + return strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && + o->props != NULL && + (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && + (strcmp(str, "Audio/Sink") == 0 || strcmp(str, "Audio/Duplex") == 0); } static bool object_is_source(struct pw_manager_object *o) { const char *str; - return - strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && - o->props != NULL && - (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && - (strcmp(str, "Audio/Source") == 0 || strcmp(str, "Audio/Duplex") == 0); + return strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && + o->props != NULL && + (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && + (strcmp(str, "Audio/Source") == 0 || + strcmp(str, "Audio/Duplex") == 0 || + strcmp(str, "Audio/Source/Virtual") == 0); } static bool object_is_monitor(struct pw_manager_object *o) @@ -79,21 +78,19 @@ static bool object_is_source_or_monitor(struct pw_manager_object *o) static bool object_is_sink_input(struct pw_manager_object *o) { const char *str; - return - strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && - o->props != NULL && - (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && - strcmp(str, "Stream/Output/Audio") == 0; + return strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && + o->props != NULL && + (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && + strcmp(str, "Stream/Output/Audio") == 0; } static bool object_is_source_output(struct pw_manager_object *o) { const char *str; - return - strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && - o->props != NULL && - (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && - strcmp(str, "Stream/Input/Audio") == 0; + return strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 && + o->props != NULL && + (str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL && + strcmp(str, "Stream/Input/Audio") == 0; } static bool object_is_recordable(struct pw_manager_object *o) diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index b6113f604..c2c8d6957 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -3397,7 +3397,10 @@ static int fill_sink_info(struct client *client, struct message *m, if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) { size_t size = strlen(name) + 10; monitor_name = alloca(size); - snprintf(monitor_name, size, "%s.monitor", name); + if (object_is_source(o)) + snprintf(monitor_name, size, "%s", name); + else + snprintf(monitor_name, size, "%s.monitor", name); } if ((desc = spa_dict_lookup(info->props, PW_KEY_NODE_DESCRIPTION)) == NULL) desc = name;