From c473cecbedb8dc6c59cd152b5a05deb335de76c6 Mon Sep 17 00:00:00 2001 From: Julian Bouzas Date: Wed, 28 Aug 2019 11:11:02 -0400 Subject: [PATCH] bluez: cleaned module and set media class to Bluez// --- modules/module-pw-bluez.c | 193 +++++++++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 45 deletions(-) diff --git a/modules/module-pw-bluez.c b/modules/module-pw-bluez.c index 6b3bf526..e1392dd1 100644 --- a/modules/module-pw-bluez.c +++ b/modules/module-pw-bluez.c @@ -11,11 +11,18 @@ * and automatically creates pipewire audio nodes to play and capture audio */ +#include #include #include #include #include +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 spa_handle *handle; struct spa_monitor *monitor; @@ -87,6 +94,112 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d) endpoint); } + +static gboolean +parse_bluez_properties (const struct spa_dict *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 = spa_dict_lookup (props, "node.name"); + if (!local_name) + return FALSE; + + /* Get the media class */ + local_media_class = spa_dict_lookup(props, SPA_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 */ + /* TODO: We need to read the specific profile from a property. For now we + * get the bluez profile from the name, and we asume SCO is never a gateway. + * This will change once pipewire sets the profile property */ + if (g_str_has_prefix (local_name, "api.bluez5.a2dp")) + profile = WP_BLUEZ_A2DP; + else if (g_str_has_prefix (local_name, "api.bluez5.sco")) + profile = WP_BLUEZ_HEADUNIT; + 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_device (const struct spa_dict *props) +{ + const gchar *name = NULL; + + /* Get the name */ + name = spa_dict_lookup (props, "node.name"); + if (!name) + return FALSE; + + /* Check if it is a bluez device */ + if (!g_str_has_prefix (name, "api.bluez5")) + return FALSE; + + return TRUE; +} + static void on_node_added (WpRemotePipewire *rp, guint id, gconstpointer p, gpointer d) { @@ -98,37 +211,22 @@ on_node_added (WpRemotePipewire *rp, guint id, gconstpointer p, gpointer d) GVariantBuilder b; g_autoptr (GVariant) endpoint_props = NULL; - /* Make sure the node has properties */ + /* Only handle bluez nodes */ g_return_if_fail(props); - - /* Get the media_class */ - media_class = spa_dict_lookup(props, "media.class"); - - /* Get the name */ - name = spa_dict_lookup (props, "media.name"); - if (!name) - name = spa_dict_lookup (props, "node.name"); - - /* Only handle bluetooth nodes */ - if (!g_str_has_prefix (name, "api.bluez5")) + if (!is_bluez_device (props)) 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"); + /* Parse the bluez properties */ + if (!parse_bluez_properties (props, &name, &media_class, &direction)) { + g_critical ("failed to parse bluez properties"); return; } /* Set the properties */ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", - "name", name ? - g_variant_new_take_string (g_strdup_printf ("Stream %u (%s)", id, name)) : - g_variant_new_take_string (g_strdup_printf ("Stream %u", id))); + "name", g_variant_new_take_string ( + g_strdup_printf ("Bluez %u (%s)", id, name))); g_variant_builder_add (&b, "{sv}", "media-class", g_variant_new_string (media_class)); g_variant_builder_add (&b, "{sv}", @@ -164,44 +262,43 @@ create_node(struct impl *impl, struct device *dev, uint32_t id, const struct spa_device_object_info *info) { struct node *node; - const char *str; + const char *name; struct pw_properties *props = NULL; struct pw_factory *factory = NULL; struct pw_node *adapter = NULL; - struct pw_proxy *proxy = NULL; /* Check if the type is a node */ if (info->type != SPA_TYPE_INTERFACE_Node) return NULL; - /* Create the properties */ - props = pw_properties_new_dict(info->props); - str = pw_properties_get(dev->props, SPA_KEY_DEVICE_DESCRIPTION); - if (str == NULL) - str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME); - if (str == NULL) - str = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK); - if (str == NULL) - str = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS); - if (str == NULL) - str = "bluetooth-device"; - pw_properties_setf(props, PW_KEY_NODE_NAME, "%s.%s", info->factory_name, str); - pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, str); - pw_properties_set(props, "factory.name", info->factory_name); + /* Get the bluez name */ + name = pw_properties_get(dev->props, SPA_KEY_DEVICE_DESCRIPTION); + if (name == NULL) + name = pw_properties_get(dev->props, SPA_KEY_DEVICE_NAME); + if (name == NULL) + name = pw_properties_get(dev->props, SPA_KEY_DEVICE_NICK); + if (name == NULL) + name = pw_properties_get(dev->props, SPA_KEY_DEVICE_ALIAS); + if (name == NULL) + name = "bluetooth-device"; /* Find the factory */ factory = wp_remote_pipewire_find_factory(impl->remote_pipewire, "adapter"); 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, "%s.%s", info->factory_name, name); + pw_properties_set(props, PW_KEY_NODE_DESCRIPTION, name); + pw_properties_set(props, PW_KEY_FACTORY_NAME, info->factory_name); + /* Create the adapter */ adapter = pw_factory_create_object(factory, NULL, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE_PROXY, props, 0); - g_return_val_if_fail (adapter, NULL); - - /* Create the proxy */ - proxy = wp_remote_pipewire_export(impl->remote_pipewire, - PW_TYPE_INTERFACE_Node, props, adapter, 0); - g_return_val_if_fail (proxy, NULL); + if (!adapter) { + pw_properties_free(props); + return NULL; + } /* Create the node */ node = g_slice_new0(struct node); @@ -210,7 +307,13 @@ create_node(struct impl *impl, struct device *dev, uint32_t id, node->id = id; node->props = props; 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 */ spa_list_append(&dev->node_list, &node->link);