From d1c8b272ec1a7ecdd8b2aae0cfebaee45fc7852f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Fri, 25 Jun 2021 17:56:50 +0200 Subject: [PATCH] spa: bluez: a2dp-source: set media.* keys based on the device's player Find a spa_bt_player among the devices of the transport and set a couple media.* node properties based on the player's data. --- spa/plugins/bluez5/a2dp-source.c | 140 ++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 13 deletions(-) diff --git a/spa/plugins/bluez5/a2dp-source.c b/spa/plugins/bluez5/a2dp-source.c index 46ad23e46..f9ec1688a 100644 --- a/spa/plugins/bluez5/a2dp-source.c +++ b/spa/plugins/bluez5/a2dp-source.c @@ -123,6 +123,9 @@ struct impl { struct spa_bt_transport *transport; struct spa_hook transport_listener; + const struct spa_bt_player *player; + struct spa_hook device_listener; + struct port port; unsigned int started:1; @@ -762,24 +765,44 @@ static int impl_node_send_command(void *object, const struct spa_command *comman static void emit_node_info(struct impl *this, bool full) { - char latency[64] = SPA_STRINGIFY(MIN_LATENCY)"/48000"; - uint64_t old = full ? this->info.change_mask : 0; - - struct spa_dict_item node_info_items[] = { - { SPA_KEY_DEVICE_API, "bluez5" }, - { SPA_KEY_MEDIA_CLASS, this->is_input ? "Audio/Source" : "Stream/Output/Audio" }, - { SPA_KEY_NODE_LATENCY, latency }, - { "media.name", ((this->transport && this->transport->device->name) ? - this->transport->device->name : "A2DP") }, - { SPA_KEY_NODE_DRIVER, this->is_input ? "true" : "false" }, - }; + const uint64_t old = full ? this->info.change_mask : 0; if (full) this->info.change_mask = this->info_all; + if (this->info.change_mask) { + const char * const device_name = this->transport && this->transport->device->name + ? this->transport->device->name + : "A2DP"; + const char * const artist = this->player ? this->player->track.artist : NULL; + const char * const title = this->player ? this->player->track.title : NULL; + const char * const album = this->player ? this->player->track.album : NULL; + const char * const software = this->player ? this->player->name : NULL; + char latency[64] = SPA_STRINGIFY(MIN_LATENCY) "/48000"; + char media_name[256]; + + const struct spa_dict_item node_info_items[] = { + { SPA_KEY_DEVICE_API, "bluez5" }, + { SPA_KEY_MEDIA_CLASS, this->is_input ? "Audio/Source" : "Stream/Output/Audio" }, + { SPA_KEY_NODE_LATENCY, latency }, + { SPA_KEY_NODE_DRIVER, this->is_input ? "true" : "false" }, + { "media.name", media_name }, + { "media.artist", artist }, + { "media.title", title }, + { "media.software", software }, + }; + + snprintf(media_name, sizeof(media_name), "[%s] %s: %s (%s)", + device_name, + artist ? artist : "?", + title ? title : "?", + album ? album : "?" + ); + if (this->transport && this->port.have_format) snprintf(latency, sizeof(latency), "%d/%d", (int)this->props.min_latency, (int)this->port.current_format.info.raw.rate); + this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); spa_node_emit_info(&this->hooks, &this->info); this->info.change_mask = old; @@ -1232,6 +1255,16 @@ static const struct spa_node_methods impl_node = { .process = impl_node_process, }; +static void impl_remove_hooks(struct impl *this) +{ + if (this->transport) { + spa_hook_remove(&this->transport_listener); + + if (this->transport->device) + spa_hook_remove(&this->device_listener); + } +} + static int do_transport_destroy(struct spa_loop *loop, bool async, uint32_t seq, @@ -1240,8 +1273,13 @@ static int do_transport_destroy(struct spa_loop *loop, void *user_data) { struct impl *this = user_data; + + impl_remove_hooks(this); + this->transport = NULL; this->transport_acquired = false; + this->player = NULL; + return 0; } @@ -1257,6 +1295,76 @@ static const struct spa_bt_transport_events transport_events = { .destroy = transport_destroy, }; +static bool try_set_player(struct impl *this, const struct spa_bt_player *player) +{ + if (player->type != SPA_BT_PLAYER_TYPE_AUDIO && player->type != SPA_BT_PLAYER_TYPE_AUDIO_BROADCASTING) + return false; + + spa_assert(this->transport->device == player->device); + this->player = player; + + return true; +} + +static void try_find_player(struct impl *this) +{ + const struct spa_bt_player *p; + + if (this->transport == NULL) + return; + + if (this->transport->device == NULL) + return; + + spa_list_for_each(p, &this->transport->device->player_list, device_link) + if (try_set_player(this, p)) + break; +} + +static void player_added(void *data, struct spa_bt_player *player) +{ + struct impl * const this = data; + + if (this->player != NULL) + return; + + if (try_set_player(this, player)) + emit_node_info(this, true); +} + +static void player_removed(void *data, struct spa_bt_player *player) +{ + struct impl * const this = data; + + if (this->player != player) + return; + + this->player = NULL; + + try_find_player(this); + emit_node_info(this, true); +} + +static void player_changed(void *data, struct spa_bt_player *player) +{ + struct impl * const this = data; + + if (this->player == NULL) + try_set_player(this, player); + + if (this->player != player) + return; + + emit_node_info(this, true); +} + +static const struct spa_bt_device_events device_events = { + SPA_VERSION_BT_DEVICE_EVENTS, + .player_added = player_added, + .player_changed = player_changed, + .player_removed = player_removed, +}; + static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) { struct impl *this; @@ -1277,12 +1385,15 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void static int impl_clear(struct spa_handle *handle) { struct impl *this = (struct impl *) handle; + if (this->codec_data) this->codec->deinit(this->codec_data); + if (this->codec_props && this->codec->clear_props) this->codec->clear_props(this->codec_props); - if (this->transport) - spa_hook_remove(&this->transport_listener); + + impl_remove_hooks(this); + return 0; } @@ -1395,6 +1506,9 @@ impl_init(const struct spa_handle_factory *factory, spa_bt_transport_add_listener(this->transport, &this->transport_listener, &transport_events, this); + spa_bt_device_add_listener(this->transport->device, &this->device_listener, &device_events, this); + try_find_player(this); + return 0; }