Merge branch 'master' into proxy-refactoring
This commit is contained in:
@@ -68,7 +68,7 @@ group_find_backend (struct group *group, WpCore *core)
|
|||||||
/* find the backend */
|
/* find the backend */
|
||||||
g_variant_dict_init (&d, NULL);
|
g_variant_dict_init (&d, NULL);
|
||||||
g_variant_dict_insert (&d, "action", "s", "mixer");
|
g_variant_dict_insert (&d, "action", "s", "mixer");
|
||||||
g_variant_dict_insert (&d, "media.class", "s", "Audio/Sink");
|
g_variant_dict_insert (&d, "media.class", "s", "Alsa/Sink");
|
||||||
g_variant_dict_insert (&d, "media.role", "s", group->name);
|
g_variant_dict_insert (&d, "media.role", "s", group->name);
|
||||||
|
|
||||||
backend = wp_policy_find_endpoint (core, g_variant_dict_end (&d),
|
backend = wp_policy_find_endpoint (core, g_variant_dict_end (&d),
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
* and automatically creates endpoints for all alsa device nodes that appear
|
* and automatically creates endpoints for all alsa device nodes that appear
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <spa/utils/keys.h>
|
||||||
#include <spa/utils/names.h>
|
#include <spa/utils/names.h>
|
||||||
#include <spa/monitor/monitor.h>
|
#include <spa/monitor/monitor.h>
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
@@ -86,6 +87,89 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
|
|||||||
g_steal_pointer (&endpoint));
|
g_steal_pointer (&endpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_alsa_properties (WpProperties *props, const gchar **name,
|
||||||
|
const gchar **media_class, enum pw_direction *direction)
|
||||||
|
{
|
||||||
|
const char *local_name = NULL;
|
||||||
|
const char *local_media_class = NULL;
|
||||||
|
enum pw_direction local_direction;
|
||||||
|
|
||||||
|
/* Get the name */
|
||||||
|
local_name = wp_properties_get (props, PW_KEY_NODE_NAME);
|
||||||
|
if (!local_name)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Get the media class */
|
||||||
|
local_media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
|
||||||
|
if (!local_media_class)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Get the direction */
|
||||||
|
if (g_str_has_prefix (local_media_class, "Audio/Sink"))
|
||||||
|
local_direction = PW_DIRECTION_INPUT;
|
||||||
|
else if (g_str_has_prefix (local_media_class, "Audio/Source"))
|
||||||
|
local_direction = PW_DIRECTION_OUTPUT;
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Set the name */
|
||||||
|
if (name)
|
||||||
|
*name = local_name;
|
||||||
|
|
||||||
|
/* Set the media class */
|
||||||
|
if (media_class) {
|
||||||
|
switch (local_direction) {
|
||||||
|
case PW_DIRECTION_INPUT:
|
||||||
|
*media_class = "Alsa/Sink";
|
||||||
|
break;
|
||||||
|
case PW_DIRECTION_OUTPUT:
|
||||||
|
*media_class = "Alsa/Source";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the direction */
|
||||||
|
if (direction)
|
||||||
|
*direction = local_direction;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: we need to find a better way to do this */
|
||||||
|
static gboolean
|
||||||
|
is_alsa_node (WpProperties * props)
|
||||||
|
{
|
||||||
|
const gchar *name = NULL;
|
||||||
|
const gchar *media_class = NULL;
|
||||||
|
|
||||||
|
/* Get the name */
|
||||||
|
name = wp_properties_get (props, "node.name");
|
||||||
|
if (!name)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Get the media class */
|
||||||
|
media_class = wp_properties_get (props, SPA_KEY_MEDIA_CLASS);
|
||||||
|
if (!media_class)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Check if it is an audio device */
|
||||||
|
if (!g_str_has_prefix (media_class, "Audio/"))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Check it is not a convert */
|
||||||
|
if (g_str_has_prefix (media_class, "Audio/Convert"))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Check if it is not a bluez device */
|
||||||
|
if (g_str_has_prefix (name, "bluez5."))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_node_added(WpRemotePipewire *rp, WpProxy *proxy, struct impl *impl)
|
on_node_added(WpRemotePipewire *rp, WpProxy *proxy, struct impl *impl)
|
||||||
{
|
{
|
||||||
@@ -99,38 +183,21 @@ on_node_added(WpRemotePipewire *rp, WpProxy *proxy, struct impl *impl)
|
|||||||
props = wp_proxy_get_global_properties (proxy);
|
props = wp_proxy_get_global_properties (proxy);
|
||||||
g_return_if_fail(props);
|
g_return_if_fail(props);
|
||||||
|
|
||||||
/* Get the media_class */
|
/* Only handle alsa nodes */
|
||||||
media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
|
if (!is_alsa_node (props))
|
||||||
|
|
||||||
/* Make sure the media class is non-convert audio */
|
|
||||||
if (!g_str_has_prefix (media_class, "Audio/"))
|
|
||||||
return;
|
|
||||||
if (g_str_has_prefix (media_class, "Audio/Convert"))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Get the name */
|
/* Parse the alsa properties */
|
||||||
name = wp_properties_get (props, PW_KEY_MEDIA_NAME);
|
if (!parse_alsa_properties (props, &name, &media_class, &direction)) {
|
||||||
if (!name)
|
g_critical ("failed to parse alsa properties");
|
||||||
name = wp_properties_get (props, PW_KEY_NODE_NAME);
|
|
||||||
|
|
||||||
/* Don't handle bluetooth nodes */
|
|
||||||
if (g_str_has_prefix (name, "api.bluez5"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Get the direction */
|
|
||||||
if (g_str_has_prefix (media_class, "Audio/Sink")) {
|
|
||||||
direction = PW_DIRECTION_INPUT;
|
|
||||||
} else if (g_str_has_prefix (media_class, "Audio/Source")) {
|
|
||||||
direction = PW_DIRECTION_OUTPUT;
|
|
||||||
} else {
|
|
||||||
g_critical ("failed to parse direction");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the properties */
|
/* Set the properties */
|
||||||
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
|
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
|
||||||
g_variant_builder_add (&b, "{sv}",
|
g_variant_builder_add (&b, "{sv}",
|
||||||
"name", g_variant_new_string (name));
|
"name", g_variant_new_take_string (g_strdup_printf (
|
||||||
|
"Alsa %u (%s)", wp_proxy_get_global_id (proxy), name)));
|
||||||
g_variant_builder_add (&b, "{sv}",
|
g_variant_builder_add (&b, "{sv}",
|
||||||
"media-class", g_variant_new_string (media_class));
|
"media-class", g_variant_new_string (media_class));
|
||||||
g_variant_builder_add (&b, "{sv}",
|
g_variant_builder_add (&b, "{sv}",
|
||||||
@@ -168,33 +235,32 @@ create_node(struct impl *impl, struct device *dev, uint32_t id,
|
|||||||
const struct spa_device_object_info *info)
|
const struct spa_device_object_info *info)
|
||||||
{
|
{
|
||||||
struct node *node;
|
struct node *node;
|
||||||
const char *str;
|
const char *name;
|
||||||
g_autoptr (WpProperties) props = NULL;
|
g_autoptr (WpProperties) props = NULL;
|
||||||
|
|
||||||
/* Check if the type is a node */
|
/* Check if the type is a node */
|
||||||
if (info->type != SPA_TYPE_INTERFACE_Node)
|
if (info->type != SPA_TYPE_INTERFACE_Node)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Create the node */
|
|
||||||
node = g_slice_new0(struct node);
|
|
||||||
|
|
||||||
/* Set the node properties */
|
|
||||||
props = wp_properties_new_copy (dev->props);
|
props = wp_properties_new_copy (dev->props);
|
||||||
|
|
||||||
str = wp_properties_get (props, SPA_KEY_DEVICE_NICK);
|
/* Get the alsa name */
|
||||||
if (str == NULL)
|
name = wp_properties_get (props, SPA_KEY_DEVICE_NICK);
|
||||||
str = wp_properties_get (props, SPA_KEY_DEVICE_NAME);
|
if (name == NULL)
|
||||||
if (str == NULL)
|
name = wp_properties_get (props, SPA_KEY_DEVICE_NAME);
|
||||||
str = wp_properties_get (props, SPA_KEY_DEVICE_ALIAS);
|
if (name == NULL)
|
||||||
if (str == NULL)
|
name = wp_properties_get (props, SPA_KEY_DEVICE_ALIAS);
|
||||||
str = "alsa-device";
|
if (name == NULL)
|
||||||
|
name = "alsa-device";
|
||||||
|
|
||||||
|
/* Create the properties */
|
||||||
wp_properties_update_from_dict (props, info->props);
|
wp_properties_update_from_dict (props, info->props);
|
||||||
wp_properties_set(props, PW_KEY_NODE_NAME, str);
|
wp_properties_set(props, PW_KEY_NODE_NAME, name);
|
||||||
wp_properties_set(props, "factory.name", info->factory_name);
|
wp_properties_set(props, PW_KEY_FACTORY_NAME, info->factory_name);
|
||||||
wp_properties_set(props, "merger.monitor", "1");
|
wp_properties_set(props, "merger.monitor", "1");
|
||||||
|
|
||||||
/* Set the node info */
|
/* Create the node */
|
||||||
|
node = g_slice_new0(struct node);
|
||||||
node->impl = impl;
|
node->impl = impl;
|
||||||
node->device = dev;
|
node->device = dev;
|
||||||
node->id = id;
|
node->id = id;
|
||||||
@@ -345,12 +411,18 @@ update_device(struct impl *impl, struct device *dev,
|
|||||||
static void
|
static void
|
||||||
destroy_device(struct impl *impl, struct device *dev)
|
destroy_device(struct impl *impl, struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct node *node;
|
||||||
|
|
||||||
/* Remove the device from the list */
|
/* Remove the device from the list */
|
||||||
spa_list_remove(&dev->link);
|
spa_list_remove(&dev->link);
|
||||||
|
|
||||||
/* Remove the device listener */
|
/* Remove the device listener */
|
||||||
spa_hook_remove(&dev->device_listener);
|
spa_hook_remove(&dev->device_listener);
|
||||||
|
|
||||||
|
/* Destry all the nodes that the device has */
|
||||||
|
spa_list_consume(node, &dev->node_list, link)
|
||||||
|
destroy_node(impl, dev, node);
|
||||||
|
|
||||||
/* Destroy the device proxy */
|
/* Destroy the device proxy */
|
||||||
pw_proxy_destroy(dev->proxy);
|
pw_proxy_destroy(dev->proxy);
|
||||||
|
|
||||||
|
@@ -11,11 +11,18 @@
|
|||||||
* and automatically creates pipewire audio nodes to play and capture audio
|
* and automatically creates pipewire audio nodes to play and capture audio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <spa/utils/keys.h>
|
||||||
#include <spa/utils/names.h>
|
#include <spa/utils/names.h>
|
||||||
#include <spa/monitor/monitor.h>
|
#include <spa/monitor/monitor.h>
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
#include <wp/wp.h>
|
#include <wp/wp.h>
|
||||||
|
|
||||||
|
enum wp_bluez_profile {
|
||||||
|
WP_BLUEZ_A2DP = 0,
|
||||||
|
WP_BLUEZ_HEADUNIT = 1, /* HSP/HFP Head Unit (Headsets) */
|
||||||
|
WP_BLUEZ_GATEWAY = 2 /* HSP/HFP Gateway (Phones) */
|
||||||
|
};
|
||||||
|
|
||||||
struct monitor {
|
struct monitor {
|
||||||
struct spa_handle *handle;
|
struct spa_handle *handle;
|
||||||
struct spa_monitor *monitor;
|
struct spa_monitor *monitor;
|
||||||
@@ -87,6 +94,115 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
|
|||||||
endpoint);
|
endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_bluez_properties (WpProperties *props, const gchar **name,
|
||||||
|
const gchar **media_class, enum pw_direction *direction)
|
||||||
|
{
|
||||||
|
const char *local_name = NULL;
|
||||||
|
const char *local_media_class = NULL;
|
||||||
|
enum pw_direction local_direction;
|
||||||
|
enum wp_bluez_profile profile;
|
||||||
|
|
||||||
|
/* Get the name */
|
||||||
|
local_name = wp_properties_get (props, PW_KEY_NODE_NAME);
|
||||||
|
if (!local_name)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Get the media class */
|
||||||
|
local_media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
|
||||||
|
if (!local_media_class)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Get the direction */
|
||||||
|
if (g_str_has_prefix (local_media_class, "Audio/Sink"))
|
||||||
|
local_direction = PW_DIRECTION_INPUT;
|
||||||
|
else if (g_str_has_prefix (local_media_class, "Audio/Source"))
|
||||||
|
local_direction = PW_DIRECTION_OUTPUT;
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Get the bluez profile */
|
||||||
|
if (g_str_has_prefix (local_name, "bluez5.a2dp"))
|
||||||
|
profile = WP_BLUEZ_A2DP;
|
||||||
|
else if (g_str_has_prefix (local_name, "bluez5.hsp-hs"))
|
||||||
|
profile = WP_BLUEZ_HEADUNIT;
|
||||||
|
else if (g_str_has_prefix (local_name, "bluez5.hfp-hf"))
|
||||||
|
profile = WP_BLUEZ_HEADUNIT;
|
||||||
|
else if (g_str_has_prefix (local_name, "bluez5.hsp-ag"))
|
||||||
|
profile = WP_BLUEZ_GATEWAY;
|
||||||
|
else if (g_str_has_prefix (local_name, "bluez5.hfp-ag"))
|
||||||
|
profile = WP_BLUEZ_GATEWAY;
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Set the name */
|
||||||
|
if (name)
|
||||||
|
*name = local_name;
|
||||||
|
|
||||||
|
/* Set the media class */
|
||||||
|
if (media_class) {
|
||||||
|
switch (local_direction) {
|
||||||
|
case PW_DIRECTION_INPUT:
|
||||||
|
switch (profile) {
|
||||||
|
case WP_BLUEZ_A2DP:
|
||||||
|
*media_class = "Bluez/Sink/A2dp";
|
||||||
|
break;
|
||||||
|
case WP_BLUEZ_HEADUNIT:
|
||||||
|
*media_class = "Bluez/Sink/Headunit";
|
||||||
|
break;
|
||||||
|
case WP_BLUEZ_GATEWAY:
|
||||||
|
*media_class = "Bluez/Sink/Gateway";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PW_DIRECTION_OUTPUT:
|
||||||
|
switch (profile) {
|
||||||
|
case WP_BLUEZ_A2DP:
|
||||||
|
*media_class = "Bluez/Source/A2dp";
|
||||||
|
break;
|
||||||
|
case WP_BLUEZ_HEADUNIT:
|
||||||
|
*media_class = "Bluez/Source/Headunit";
|
||||||
|
break;
|
||||||
|
case WP_BLUEZ_GATEWAY:
|
||||||
|
*media_class = "Bluez/Source/Gateway";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the direction */
|
||||||
|
if (direction)
|
||||||
|
*direction = local_direction;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: we need to find a better way to do this */
|
||||||
|
static gboolean
|
||||||
|
is_bluez_node (WpProperties *props)
|
||||||
|
{
|
||||||
|
const gchar *name = NULL;
|
||||||
|
|
||||||
|
/* Get the name */
|
||||||
|
name = wp_properties_get (props, PW_KEY_NODE_NAME);
|
||||||
|
if (!name)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Check if it is a bluez device */
|
||||||
|
if (!g_str_has_prefix (name, "bluez5."))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_node_added (WpRemotePipewire *rp, WpProxy *proxy, struct impl *data)
|
on_node_added (WpRemotePipewire *rp, WpProxy *proxy, struct impl *data)
|
||||||
{
|
{
|
||||||
@@ -101,34 +217,21 @@ on_node_added (WpRemotePipewire *rp, WpProxy *proxy, struct impl *data)
|
|||||||
props = wp_proxy_get_global_properties (proxy);
|
props = wp_proxy_get_global_properties (proxy);
|
||||||
g_return_if_fail(props);
|
g_return_if_fail(props);
|
||||||
|
|
||||||
/* Get the media_class */
|
/* Only handle bluez nodes */
|
||||||
media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
|
if (!is_bluez_node (props))
|
||||||
|
|
||||||
/* Get the name */
|
|
||||||
name = wp_properties_get (props, PW_KEY_MEDIA_NAME);
|
|
||||||
if (!name)
|
|
||||||
name = wp_properties_get (props, PW_KEY_NODE_NAME);
|
|
||||||
|
|
||||||
/* Only handle bluetooth nodes */
|
|
||||||
if (!g_str_has_prefix (name, "api.bluez5"))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Get the direction */
|
/* Parse the bluez properties */
|
||||||
if (g_str_has_prefix (media_class, "Audio/Sink")) {
|
if (!parse_bluez_properties (props, &name, &media_class, &direction)) {
|
||||||
direction = PW_DIRECTION_INPUT;
|
g_critical ("failed to parse bluez properties");
|
||||||
} else if (g_str_has_prefix (media_class, "Audio/Source")) {
|
|
||||||
direction = PW_DIRECTION_OUTPUT;
|
|
||||||
} else {
|
|
||||||
g_critical ("failed to parse direction");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the properties */
|
/* Set the properties */
|
||||||
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
|
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
|
||||||
g_variant_builder_add (&b, "{sv}",
|
g_variant_builder_add (&b, "{sv}",
|
||||||
"name", name ?
|
"name", g_variant_new_take_string (
|
||||||
g_variant_new_take_string (g_strdup_printf ("Stream %u (%s)", id, name)) :
|
g_strdup_printf ("Bluez %u (%s)", id, name)));
|
||||||
g_variant_new_take_string (g_strdup_printf ("Stream %u", id)));
|
|
||||||
g_variant_builder_add (&b, "{sv}",
|
g_variant_builder_add (&b, "{sv}",
|
||||||
"media-class", g_variant_new_string (media_class));
|
"media-class", g_variant_new_string (media_class));
|
||||||
g_variant_builder_add (&b, "{sv}",
|
g_variant_builder_add (&b, "{sv}",
|
||||||
@@ -164,44 +267,48 @@ create_node(struct impl *impl, struct device *dev, uint32_t id,
|
|||||||
const struct spa_device_object_info *info)
|
const struct spa_device_object_info *info)
|
||||||
{
|
{
|
||||||
struct node *node;
|
struct node *node;
|
||||||
const char *str;
|
const char *name, *profile;
|
||||||
struct pw_properties *props = NULL;
|
struct pw_properties *props = NULL;
|
||||||
struct pw_factory *factory = NULL;
|
struct pw_factory *factory = NULL;
|
||||||
struct pw_node *adapter = NULL;
|
struct pw_node *adapter = NULL;
|
||||||
struct pw_proxy *proxy = NULL;
|
|
||||||
|
|
||||||
/* Check if the type is a node */
|
/* Check if the type is a node */
|
||||||
if (info->type != SPA_TYPE_INTERFACE_Node)
|
if (info->type != SPA_TYPE_INTERFACE_Node)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Create the properties */
|
/* Get the bluez name */
|
||||||
props = pw_properties_new_dict(info->props);
|
name = pw_properties_get(dev->props, SPA_KEY_DEVICE_DESCRIPTION);
|
||||||
str = pw_properties_get(dev->props, SPA_KEY_DEVICE_DESCRIPTION);
|
if (name == NULL)
|
||||||
if (str == NULL)
|
name = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME);
|
||||||
str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME);
|
if (name == NULL)
|
||||||
if (str == NULL)
|
name = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK);
|
||||||
str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK);
|
if (name == NULL)
|
||||||
if (str == NULL)
|
name = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS);
|
||||||
str = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS);
|
if (name == NULL)
|
||||||
if (str == NULL)
|
name = "bluetooth-device";
|
||||||
str = "bluetooth-device";
|
|
||||||
pw_properties_setf(props, PW_KEY_NODE_NAME, "%s.%s", info->factory_name, str);
|
/* Get the bluez profile */
|
||||||
pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, str);
|
profile = spa_dict_lookup(info->props, SPA_KEY_API_BLUEZ5_PROFILE);
|
||||||
pw_properties_set(props, "factory.name", info->factory_name);
|
if (!profile)
|
||||||
|
profile = "null";
|
||||||
|
|
||||||
/* Find the factory */
|
/* Find the factory */
|
||||||
factory = wp_remote_pipewire_find_factory(impl->remote_pipewire, "adapter");
|
factory = wp_remote_pipewire_find_factory(impl->remote_pipewire, "adapter");
|
||||||
g_return_val_if_fail (factory, NULL);
|
g_return_val_if_fail (factory, NULL);
|
||||||
|
|
||||||
|
/* Create the properties */
|
||||||
|
props = pw_properties_new_dict(info->props);
|
||||||
|
pw_properties_setf(props, PW_KEY_NODE_NAME, "bluez5.%s.%s", profile, name);
|
||||||
|
pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, name);
|
||||||
|
pw_properties_set(props, PW_KEY_FACTORY_NAME, info->factory_name);
|
||||||
|
|
||||||
/* Create the adapter */
|
/* Create the adapter */
|
||||||
adapter = pw_factory_create_object(factory, NULL, PW_TYPE_INTERFACE_Node,
|
adapter = pw_factory_create_object(factory, NULL, PW_TYPE_INTERFACE_Node,
|
||||||
PW_VERSION_NODE_PROXY, props, 0);
|
PW_VERSION_NODE_PROXY, props, 0);
|
||||||
g_return_val_if_fail (adapter, NULL);
|
if (!adapter) {
|
||||||
|
pw_properties_free(props);
|
||||||
/* Create the proxy */
|
return NULL;
|
||||||
proxy = wp_remote_pipewire_export(impl->remote_pipewire,
|
}
|
||||||
PW_TYPE_INTERFACE_Node, props, adapter, 0);
|
|
||||||
g_return_val_if_fail (proxy, NULL);
|
|
||||||
|
|
||||||
/* Create the node */
|
/* Create the node */
|
||||||
node = g_slice_new0(struct node);
|
node = g_slice_new0(struct node);
|
||||||
@@ -210,7 +317,13 @@ create_node(struct impl *impl, struct device *dev, uint32_t id,
|
|||||||
node->id = id;
|
node->id = id;
|
||||||
node->props = props;
|
node->props = props;
|
||||||
node->adapter = adapter;
|
node->adapter = adapter;
|
||||||
node->proxy = proxy;
|
node->proxy = wp_remote_pipewire_export(impl->remote_pipewire,
|
||||||
|
PW_TYPE_INTERFACE_Node, props, adapter, 0);
|
||||||
|
if (!node->proxy) {
|
||||||
|
pw_properties_free(props);
|
||||||
|
g_slice_free (struct node, node);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add the node to the list */
|
/* Add the node to the list */
|
||||||
spa_list_append(&dev->node_list, &node->link);
|
spa_list_append(&dev->node_list, &node->link);
|
||||||
@@ -342,12 +455,18 @@ update_device(struct impl *impl, struct device *dev,
|
|||||||
static void
|
static void
|
||||||
destroy_device(struct impl *impl, struct device *dev)
|
destroy_device(struct impl *impl, struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct node *node;
|
||||||
|
|
||||||
/* Remove the device from the list */
|
/* Remove the device from the list */
|
||||||
spa_list_remove(&dev->link);
|
spa_list_remove(&dev->link);
|
||||||
|
|
||||||
/* Remove the device listener */
|
/* Remove the device listener */
|
||||||
spa_hook_remove(&dev->device_listener);
|
spa_hook_remove(&dev->device_listener);
|
||||||
|
|
||||||
|
/* Destry all the nodes that the device has */
|
||||||
|
spa_list_consume(node, &dev->node_list, link)
|
||||||
|
destroy_node(impl, dev, node);
|
||||||
|
|
||||||
/* Destroy the device proxy */
|
/* Destroy the device proxy */
|
||||||
pw_proxy_destroy(dev->proxy);
|
pw_proxy_destroy(dev->proxy);
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <wp/wp.h>
|
#include <wp/wp.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DIRECTION_SINK = 0,
|
DIRECTION_SINK = 0,
|
||||||
@@ -125,41 +126,80 @@ select_endpoint (WpSimplePolicy *self, gint direction, WpEndpoint *ep,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
select_new_endpoint (WpSimplePolicy *self)
|
try_select_new_endpoint (WpSimplePolicy *self, gint direction,
|
||||||
|
const gchar *media_class)
|
||||||
{
|
{
|
||||||
g_autoptr (WpCore) core = NULL;
|
g_autoptr (WpCore) core = NULL;
|
||||||
g_autoptr (GPtrArray) ptr_array = NULL;
|
g_autoptr (GPtrArray) ptr_array = NULL;
|
||||||
const gchar *media_class = NULL;
|
|
||||||
WpEndpoint *other;
|
WpEndpoint *other;
|
||||||
guint32 control_id;
|
guint32 control_id;
|
||||||
gint direction, i;
|
gint i;
|
||||||
|
|
||||||
if (!self->selected[DIRECTION_SINK]) {
|
|
||||||
direction = DIRECTION_SINK;
|
|
||||||
media_class = "Audio/Sink";
|
|
||||||
} else if (!self->selected[DIRECTION_SOURCE]) {
|
|
||||||
direction = DIRECTION_SOURCE;
|
|
||||||
media_class = "Audio/Source";
|
|
||||||
} else
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
|
|
||||||
|
/* Get the list of endpoints matching the media class */
|
||||||
core = wp_policy_get_core (WP_POLICY (self));
|
core = wp_policy_get_core (WP_POLICY (self));
|
||||||
|
|
||||||
/* Get all the endpoints with the same media class */
|
|
||||||
ptr_array = wp_endpoint_find (core, media_class);
|
ptr_array = wp_endpoint_find (core, media_class);
|
||||||
|
|
||||||
/* select the first available that has the "selected" control */
|
/* Find the endpoint in the list */
|
||||||
for (i = 0; i < (ptr_array ? ptr_array->len : 0); i++) {
|
for (i = 0; i < (ptr_array ? ptr_array->len : 0); i++) {
|
||||||
other = g_ptr_array_index (ptr_array, i);
|
other = g_ptr_array_index (ptr_array, i);
|
||||||
|
if (g_str_has_prefix (media_class, "Alsa/")) {
|
||||||
|
/* If Alsa, select the "selected" endpoint */
|
||||||
control_id =
|
control_id =
|
||||||
wp_endpoint_find_control (other, WP_STREAM_ID_NONE, "selected");
|
wp_endpoint_find_control (other, WP_STREAM_ID_NONE, "selected");
|
||||||
if (control_id == WP_CONTROL_ID_NONE)
|
if (control_id == WP_CONTROL_ID_NONE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
select_endpoint (self, direction, other, control_id);
|
select_endpoint (self, direction, other, control_id);
|
||||||
break;
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
/* If non-Alsa (Bluez and Stream), select the first endpoint */
|
||||||
|
select_endpoint (self, direction, other, WP_CONTROL_ID_NONE);
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
select_new_endpoint (WpSimplePolicy *self)
|
||||||
|
{
|
||||||
|
gint direction;
|
||||||
|
const gchar *bluez_headunit_media_class = NULL;
|
||||||
|
const gchar *bluez_a2dp_media_class = NULL;
|
||||||
|
const gchar *alsa_media_class = NULL;
|
||||||
|
|
||||||
|
if (!self->selected[DIRECTION_SINK]) {
|
||||||
|
direction = DIRECTION_SINK;
|
||||||
|
bluez_headunit_media_class = "Bluez/Sink/Headunit";
|
||||||
|
bluez_a2dp_media_class = "Bluez/Sink/A2dp";
|
||||||
|
alsa_media_class = "Alsa/Sink";
|
||||||
|
} else if (!self->selected[DIRECTION_SOURCE]) {
|
||||||
|
direction = DIRECTION_SOURCE;
|
||||||
|
bluez_headunit_media_class = "Bluez/Source/Headunit";
|
||||||
|
bluez_a2dp_media_class = "Bluez/Source/A2dp";
|
||||||
|
alsa_media_class = "Alsa/Source";
|
||||||
|
} else
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
|
/* Bluez has higher priority than Alsa. Bluez A2DP profile has lower
|
||||||
|
* priority than Bluez non-gatewat profile (Headunit). Bluez Gateway profiles
|
||||||
|
* are not handled here because they always need to be linked with Alsa
|
||||||
|
* endpoints, so the priority list is as folows (from higher to lower):
|
||||||
|
* - Bluez Headunit
|
||||||
|
* - Bluez A2DP
|
||||||
|
* - Alsa
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Try to select a Bluez Headunit endpoint */
|
||||||
|
if (try_select_new_endpoint (self, direction, bluez_headunit_media_class))
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
|
/* Try to select a Bluez A2dp endpoint */
|
||||||
|
if (try_select_new_endpoint (self, direction, bluez_a2dp_media_class))
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
|
/* Try to select an Alsa endpoint */
|
||||||
|
try_select_new_endpoint (self, direction, alsa_media_class);
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
}
|
}
|
||||||
@@ -172,8 +212,8 @@ simple_policy_endpoint_added (WpPolicy *policy, WpEndpoint *ep)
|
|||||||
guint32 control_id;
|
guint32 control_id;
|
||||||
gint direction;
|
gint direction;
|
||||||
|
|
||||||
/* we only care about audio device endpoints here */
|
/* we only care about alsa device endpoints here */
|
||||||
if (!g_str_has_prefix (media_class, "Audio/"))
|
if (!g_str_has_prefix (media_class, "Alsa/"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* verify it has the "selected" control available */
|
/* verify it has the "selected" control available */
|
||||||
@@ -264,42 +304,32 @@ on_endpoint_link_created(GObject *initable, GAsyncResult *res, gpointer d)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
handle_client (WpPolicy *policy, WpEndpoint *ep)
|
link_endpoint (WpPolicy *policy, WpEndpoint *ep, GVariant *target_props)
|
||||||
{
|
{
|
||||||
const char *media_class = wp_endpoint_get_media_class(ep);
|
|
||||||
GVariantDict d;
|
|
||||||
g_autoptr (WpCore) core = NULL;
|
g_autoptr (WpCore) core = NULL;
|
||||||
g_autoptr (WpEndpoint) target = NULL;
|
g_autoptr (WpEndpoint) target = NULL;
|
||||||
guint32 stream_id;
|
guint32 stream_id;
|
||||||
|
guint direction;
|
||||||
gboolean is_capture = FALSE;
|
gboolean is_capture = FALSE;
|
||||||
g_autofree gchar *role, *target_name = NULL;
|
|
||||||
|
|
||||||
/* Detect if the client is doing capture or playback */
|
/* Check if the endpoint is capture or not */
|
||||||
is_capture = g_str_has_prefix (media_class, "Stream/Input");
|
direction = wp_endpoint_get_direction (WP_ENDPOINT (ep));
|
||||||
|
switch (direction) {
|
||||||
/* Locate the target endpoint */
|
case PW_DIRECTION_INPUT:
|
||||||
g_variant_dict_init (&d, NULL);
|
is_capture = TRUE;
|
||||||
g_variant_dict_insert (&d, "action", "s", "link");
|
break;
|
||||||
g_variant_dict_insert (&d, "media.class", "s",
|
case PW_DIRECTION_OUTPUT:
|
||||||
is_capture ? "Audio/Source" : "Audio/Sink");
|
is_capture = FALSE;
|
||||||
|
break;
|
||||||
g_object_get (ep, "role", &role, NULL);
|
default:
|
||||||
if (role)
|
return FALSE;
|
||||||
g_variant_dict_insert (&d, "media.role", "s", role);
|
}
|
||||||
|
|
||||||
g_object_get (ep, "target", &target_name, NULL);
|
|
||||||
if (target_name)
|
|
||||||
g_variant_dict_insert (&d, "media.name", "s", target_name);
|
|
||||||
|
|
||||||
/* TODO: more properties are needed here */
|
|
||||||
|
|
||||||
core = wp_policy_get_core (policy);
|
core = wp_policy_get_core (policy);
|
||||||
target = wp_policy_find_endpoint (core, g_variant_dict_end (&d), &stream_id);
|
target = wp_policy_find_endpoint (core, target_props, &stream_id);
|
||||||
if (!target) {
|
if (!target)
|
||||||
g_warning ("Could not find target endpoint");
|
return FALSE;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the client is already linked... */
|
/* if the client is already linked... */
|
||||||
if (wp_endpoint_is_linked (ep)) {
|
if (wp_endpoint_is_linked (ep)) {
|
||||||
@@ -315,7 +345,7 @@ handle_client (WpPolicy *policy, WpEndpoint *ep)
|
|||||||
/* ... do nothing if it's already linked to the correct target */
|
/* ... do nothing if it's already linked to the correct target */
|
||||||
g_debug ("Client '%s' already linked correctly",
|
g_debug ("Client '%s' already linked correctly",
|
||||||
wp_endpoint_get_name (ep));
|
wp_endpoint_get_name (ep));
|
||||||
return;
|
return TRUE;
|
||||||
} else {
|
} else {
|
||||||
/* ... or else unlink it and continue */
|
/* ... or else unlink it and continue */
|
||||||
g_debug ("Unlink client '%s' from its previous target",
|
g_debug ("Unlink client '%s' from its previous target",
|
||||||
@@ -340,7 +370,7 @@ handle_client (WpPolicy *policy, WpEndpoint *ep)
|
|||||||
wp_endpoint_unlink (target);
|
wp_endpoint_unlink (target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Link the client with the target */
|
/* Link the endpoint with the target */
|
||||||
if (is_capture) {
|
if (is_capture) {
|
||||||
wp_endpoint_link_new (core, target, stream_id, ep, 0,
|
wp_endpoint_link_new (core, target, stream_id, ep, 0,
|
||||||
on_endpoint_link_created, NULL);
|
on_endpoint_link_created, NULL);
|
||||||
@@ -349,7 +379,103 @@ handle_client (WpPolicy *policy, WpEndpoint *ep)
|
|||||||
on_endpoint_link_created, NULL);
|
on_endpoint_link_created, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_client (WpPolicy *policy, WpEndpoint *ep)
|
||||||
|
{
|
||||||
|
const char *media_class = wp_endpoint_get_media_class(ep);
|
||||||
|
GVariantDict d;
|
||||||
|
gboolean is_capture = FALSE;
|
||||||
|
const gchar *role, *target_name = NULL;
|
||||||
|
|
||||||
|
/* Detect if the client is doing capture or playback */
|
||||||
|
is_capture = g_str_has_prefix (media_class, "Stream/Input");
|
||||||
|
|
||||||
|
/* All Stream client endpoints need to be linked with a Bluez non-gateway
|
||||||
|
* endpoint if any. If there is no Bluez non-gateway endpoints, the Stream
|
||||||
|
* client needs to be linked with a Bluez A2DP endpoint. Finally, if none
|
||||||
|
* of the previous endpoints are found, the Stream client needs to be linked
|
||||||
|
* with an Alsa endpoint.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Link endpoint with Bluez non-gateway target endpoint */
|
||||||
|
g_variant_dict_init (&d, NULL);
|
||||||
|
g_variant_dict_insert (&d, "action", "s", "link");
|
||||||
|
g_variant_dict_insert (&d, "media.class", "s",
|
||||||
|
is_capture ? "Bluez/Source/Headunit" : "Bluez/Sink/Headunit");
|
||||||
|
if (link_endpoint (policy, ep, g_variant_dict_end (&d)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Link endpoint with Bluez A2DP target endpoint */
|
||||||
|
g_variant_dict_init (&d, NULL);
|
||||||
|
g_variant_dict_insert (&d, "action", "s", "link");
|
||||||
|
g_variant_dict_insert (&d, "media.class", "s",
|
||||||
|
is_capture ? "Bluez/Source/A2dp" : "Bluez/Sink/A2dp");
|
||||||
|
if (link_endpoint (policy, ep, g_variant_dict_end (&d)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Link endpoint with Alsa target endpoint */
|
||||||
|
g_variant_dict_init (&d, NULL);
|
||||||
|
g_variant_dict_insert (&d, "action", "s", "link");
|
||||||
|
g_variant_dict_insert (&d, "media.class", "s",
|
||||||
|
is_capture ? "Alsa/Source" : "Alsa/Sink");
|
||||||
|
g_object_get (ep, "role", &role, NULL);
|
||||||
|
if (role)
|
||||||
|
g_variant_dict_insert (&d, "media.role", "s", role);
|
||||||
|
g_object_get (ep, "target", &target_name, NULL);
|
||||||
|
if (target_name)
|
||||||
|
g_variant_dict_insert (&d, "media.name", "s", target_name);
|
||||||
|
if (!link_endpoint (policy, ep, g_variant_dict_end (&d)))
|
||||||
|
g_info ("Could not find alsa target endpoint for client stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_bluez_non_gateway (WpPolicy *policy, WpEndpoint *ep)
|
||||||
|
{
|
||||||
|
GVariantDict d;
|
||||||
|
const char *media_class = wp_endpoint_get_media_class(ep);
|
||||||
|
gboolean is_sink = FALSE;
|
||||||
|
|
||||||
|
/* All bluetooth non-gateway endpoints (A2DP/HSP_HS/HFP_HF) always
|
||||||
|
* need to be linked with the stream endpoints so that the computer
|
||||||
|
* does not play any sound
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Detect if the client is a sink or not */
|
||||||
|
is_sink = g_str_has_prefix (media_class, "Bluez/Sink");
|
||||||
|
|
||||||
|
/* Link endpoint with Stream target endpoint */
|
||||||
|
g_variant_dict_init (&d, NULL);
|
||||||
|
g_variant_dict_insert (&d, "action", "s", "link");
|
||||||
|
g_variant_dict_insert (&d, "media.class", "s",
|
||||||
|
is_sink ? "Stream/Output/Audio" : "Stream/Input/Audio");
|
||||||
|
if (!link_endpoint (policy, ep, g_variant_dict_end (&d)))
|
||||||
|
g_info ("Could not find stream target endpoint for non-gateway bluez");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_bluez_gateway (WpPolicy *policy, WpEndpoint *ep)
|
||||||
|
{
|
||||||
|
/* All bluetooth gateway endpoints (HSP_GW/HFP_GW) always need to
|
||||||
|
* be linked with the alsa endpoints so that the computer can act
|
||||||
|
* as a head unit
|
||||||
|
*/
|
||||||
|
GVariantDict d;
|
||||||
|
const char *media_class = wp_endpoint_get_media_class(ep);
|
||||||
|
gboolean is_sink = FALSE;
|
||||||
|
|
||||||
|
/* Detect if the client is a sink or not */
|
||||||
|
is_sink = g_str_has_prefix (media_class, "Bluez/Sink");
|
||||||
|
|
||||||
|
/* Link endpoint with Alsa target endpoint */
|
||||||
|
g_variant_dict_init (&d, NULL);
|
||||||
|
g_variant_dict_insert (&d, "action", "s", "link");
|
||||||
|
g_variant_dict_insert (&d, "media.class", "s",
|
||||||
|
is_sink ? "Alsa/Source" : "Alsa/Sink");
|
||||||
|
if (!link_endpoint (policy, ep, g_variant_dict_end (&d)))
|
||||||
|
g_info ("Could not find alsa target endpoint for gateway bluez");
|
||||||
}
|
}
|
||||||
|
|
||||||
static gint
|
static gint
|
||||||
@@ -393,35 +519,103 @@ compare_client_priority (gconstpointer a, gconstpointer b, gpointer user_data)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gint
|
||||||
simple_policy_rescan_in_idle (WpSimplePolicy *self)
|
compare_bluez_non_gateway_priority (gconstpointer a, gconstpointer b,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
WpEndpoint *ae = *(const gpointer *) a;
|
||||||
|
WpEndpoint *be = *(const gpointer *) b;
|
||||||
|
const char *a_media_class, *b_media_class;
|
||||||
|
gint a_priority, b_priority;
|
||||||
|
|
||||||
|
/* Bluez priorities (Gateway is a different case):
|
||||||
|
- Headset (1)
|
||||||
|
- A2dp (0)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get endpoint A priority */
|
||||||
|
a_media_class = wp_endpoint_get_media_class(ae);
|
||||||
|
a_priority = g_str_has_suffix (a_media_class, "Headset") ? 1 : 0;
|
||||||
|
|
||||||
|
/* Get endpoint B priority */
|
||||||
|
b_media_class = wp_endpoint_get_media_class(be);
|
||||||
|
b_priority = g_str_has_suffix (b_media_class, "Headset") ? 1 : 0;
|
||||||
|
|
||||||
|
/* Return the difference of both priorities */
|
||||||
|
return a_priority - b_priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
compare_bluez_gateway_priority (gconstpointer a, gconstpointer b,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
/* Since Bluez Gateway profile does not have any priorities, just
|
||||||
|
* return positive to indicate endpoint A has higher priority than
|
||||||
|
* endpoint B */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rescan_sink_endpoints (WpSimplePolicy *self, const gchar *media_class,
|
||||||
|
void (*handler) (WpPolicy *policy, WpEndpoint *ep))
|
||||||
{
|
{
|
||||||
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
|
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
|
||||||
g_autoptr (GPtrArray) endpoints = NULL;
|
g_autoptr (GPtrArray) endpoints = NULL;
|
||||||
WpEndpoint *ep;
|
WpEndpoint *ep;
|
||||||
gint i;
|
gint i;
|
||||||
|
|
||||||
g_debug ("rescanning for clients that need linking");
|
endpoints = wp_endpoint_find (core, media_class);
|
||||||
|
|
||||||
endpoints = wp_endpoint_find (core, "Stream/Input/Audio");
|
|
||||||
if (endpoints) {
|
if (endpoints) {
|
||||||
/* link all capture clients */
|
/* link all sink endpoints */
|
||||||
for (i = 0; i < endpoints->len; i++) {
|
for (i = 0; i < endpoints->len; i++) {
|
||||||
ep = g_ptr_array_index (endpoints, i);
|
ep = g_ptr_array_index (endpoints, i);
|
||||||
handle_client (WP_POLICY (self), ep);
|
handler (WP_POLICY (self), ep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
endpoints = wp_endpoint_find (core, "Stream/Output/Audio");
|
static void
|
||||||
|
rescan_source_endpoints (WpSimplePolicy *self, const gchar *media_class,
|
||||||
|
void (*handle) (WpPolicy *policy, WpEndpoint *ep),
|
||||||
|
GCompareDataFunc comp_func)
|
||||||
|
{
|
||||||
|
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
|
||||||
|
g_autoptr (GPtrArray) endpoints = NULL;
|
||||||
|
WpEndpoint *ep;
|
||||||
|
|
||||||
|
endpoints = wp_endpoint_find (core, media_class);
|
||||||
if (endpoints && endpoints->len > 0) {
|
if (endpoints && endpoints->len > 0) {
|
||||||
/* sort based on role priorities */
|
/* sort based on priorities */
|
||||||
g_ptr_array_sort_with_data (endpoints, compare_client_priority,
|
g_ptr_array_sort_with_data (endpoints, comp_func, self->role_priorities);
|
||||||
self->role_priorities);
|
|
||||||
|
|
||||||
/* link the highest priority client */
|
/* link the highest priority */
|
||||||
ep = g_ptr_array_index (endpoints, 0);
|
ep = g_ptr_array_index (endpoints, 0);
|
||||||
handle_client (WP_POLICY (self), ep);
|
handle (WP_POLICY (self), ep);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
simple_policy_rescan_in_idle (WpSimplePolicy *self)
|
||||||
|
{
|
||||||
|
/* Alsa endpoints are never handled */
|
||||||
|
|
||||||
|
/* Handle clients */
|
||||||
|
rescan_sink_endpoints (self, "Stream/Input/Audio", handle_client);
|
||||||
|
rescan_source_endpoints (self, "Stream/Output/Audio", handle_client,
|
||||||
|
compare_client_priority);
|
||||||
|
|
||||||
|
/* Handle Bluez non-gateway */
|
||||||
|
rescan_sink_endpoints (self, "Bluez/Sink/Headunit", handle_bluez_non_gateway);
|
||||||
|
rescan_source_endpoints (self, "Bluez/Source/Headunit",
|
||||||
|
handle_bluez_non_gateway, compare_bluez_non_gateway_priority);
|
||||||
|
rescan_sink_endpoints (self, "Bluez/Sink/A2dp", handle_bluez_non_gateway);
|
||||||
|
rescan_source_endpoints (self, "Bluez/Source/A2dp", handle_bluez_non_gateway,
|
||||||
|
compare_bluez_non_gateway_priority);
|
||||||
|
|
||||||
|
/* Handle Bluez gateway */
|
||||||
|
rescan_sink_endpoints (self, "Bluez/Sink/Gateway", handle_bluez_gateway);
|
||||||
|
rescan_source_endpoints (self, "Bluez/Source/Gateway",
|
||||||
|
handle_bluez_gateway, compare_bluez_gateway_priority);
|
||||||
|
|
||||||
self->pending_rescan = 0;
|
self->pending_rescan = 0;
|
||||||
return G_SOURCE_REMOVE;
|
return G_SOURCE_REMOVE;
|
||||||
@@ -439,18 +633,17 @@ static gboolean
|
|||||||
simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
|
simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
|
||||||
{
|
{
|
||||||
WpSimplePolicy *self = WP_SIMPLE_POLICY (policy);
|
WpSimplePolicy *self = WP_SIMPLE_POLICY (policy);
|
||||||
const char *media_class = NULL;
|
const char *media_class = wp_endpoint_get_media_class(ep);
|
||||||
|
|
||||||
/* TODO: For now we only accept audio stream clients */
|
/* Schedule rescan only if endpoint is audio stream or bluez */
|
||||||
media_class = wp_endpoint_get_media_class(ep);
|
if ((g_str_has_prefix (media_class, "Stream") &&
|
||||||
if (!g_str_has_prefix (media_class, "Stream") ||
|
g_str_has_suffix (media_class, "Audio")) ||
|
||||||
!g_str_has_suffix (media_class, "Audio")) {
|
g_str_has_prefix (media_class, "Bluez")) {
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Schedule a rescan that will handle the endpoint */
|
|
||||||
simple_policy_rescan (self);
|
simple_policy_rescan (self);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WpEndpoint *
|
static WpEndpoint *
|
||||||
@@ -478,7 +671,7 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Find the endpoint with the matching name, otherwise get the one with the
|
/* Find the endpoint with the matching name, otherwise get the one with the
|
||||||
* "selected" flag */
|
* "selected" flag (if it is an alsa endpoint) */
|
||||||
for (i = 0; i < ptr_array->len; i++) {
|
for (i = 0; i < ptr_array->len; i++) {
|
||||||
ep = g_ptr_array_index (ptr_array, i);
|
ep = g_ptr_array_index (ptr_array, i);
|
||||||
if (name) {
|
if (name) {
|
||||||
@@ -486,7 +679,7 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
|||||||
g_object_ref (ep);
|
g_object_ref (ep);
|
||||||
goto select_stream;
|
goto select_stream;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (g_str_has_prefix (media_class, "Alsa/")) {
|
||||||
g_autoptr (GVariant) value = NULL;
|
g_autoptr (GVariant) value = NULL;
|
||||||
guint id;
|
guint id;
|
||||||
|
|
||||||
@@ -506,6 +699,10 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
|||||||
ep = (ptr_array->len > 0) ?
|
ep = (ptr_array->len > 0) ?
|
||||||
g_object_ref (g_ptr_array_index (ptr_array, 0)) : NULL;
|
g_object_ref (g_ptr_array_index (ptr_array, 0)) : NULL;
|
||||||
|
|
||||||
|
/* Don't select any stream if it is not an alsa endpoint */
|
||||||
|
if (!g_str_has_prefix (media_class, "Alsa/"))
|
||||||
|
return ep;
|
||||||
|
|
||||||
select_stream:
|
select_stream:
|
||||||
g_variant_lookup (props, "media.role", "&s", &role);
|
g_variant_lookup (props, "media.role", "&s", &role);
|
||||||
if (!g_strcmp0 (action, "mixer") && !g_strcmp0 (role, "Master"))
|
if (!g_strcmp0 (action, "mixer") && !g_strcmp0 (role, "Master"))
|
||||||
|
Reference in New Issue
Block a user