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.
This commit is contained in:
Barnabás Pőcze 2021-06-25 17:56:50 +02:00
parent 902a2113de
commit d1c8b272ec

View file

@ -123,6 +123,9 @@ struct impl {
struct spa_bt_transport *transport; struct spa_bt_transport *transport;
struct spa_hook transport_listener; struct spa_hook transport_listener;
const struct spa_bt_player *player;
struct spa_hook device_listener;
struct port port; struct port port;
unsigned int started:1; 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) static void emit_node_info(struct impl *this, bool full)
{ {
char latency[64] = SPA_STRINGIFY(MIN_LATENCY)"/48000"; const uint64_t old = full ? this->info.change_mask : 0;
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" },
};
if (full) if (full)
this->info.change_mask = this->info_all; this->info.change_mask = this->info_all;
if (this->info.change_mask) { 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) if (this->transport && this->port.have_format)
snprintf(latency, sizeof(latency), "%d/%d", (int)this->props.min_latency, snprintf(latency, sizeof(latency), "%d/%d", (int)this->props.min_latency,
(int)this->port.current_format.info.raw.rate); (int)this->port.current_format.info.raw.rate);
this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items);
spa_node_emit_info(&this->hooks, &this->info); spa_node_emit_info(&this->hooks, &this->info);
this->info.change_mask = old; this->info.change_mask = old;
@ -1232,6 +1255,16 @@ static const struct spa_node_methods impl_node = {
.process = impl_node_process, .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, static int do_transport_destroy(struct spa_loop *loop,
bool async, bool async,
uint32_t seq, uint32_t seq,
@ -1240,8 +1273,13 @@ static int do_transport_destroy(struct spa_loop *loop,
void *user_data) void *user_data)
{ {
struct impl *this = user_data; struct impl *this = user_data;
impl_remove_hooks(this);
this->transport = NULL; this->transport = NULL;
this->transport_acquired = false; this->transport_acquired = false;
this->player = NULL;
return 0; return 0;
} }
@ -1257,6 +1295,76 @@ static const struct spa_bt_transport_events transport_events = {
.destroy = transport_destroy, .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) static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
{ {
struct impl *this; 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) static int impl_clear(struct spa_handle *handle)
{ {
struct impl *this = (struct impl *) handle; struct impl *this = (struct impl *) handle;
if (this->codec_data) if (this->codec_data)
this->codec->deinit(this->codec_data); this->codec->deinit(this->codec_data);
if (this->codec_props && this->codec->clear_props) if (this->codec_props && this->codec->clear_props)
this->codec->clear_props(this->codec_props); this->codec->clear_props(this->codec_props);
if (this->transport)
spa_hook_remove(&this->transport_listener); impl_remove_hooks(this);
return 0; return 0;
} }
@ -1395,6 +1506,9 @@ impl_init(const struct spa_handle_factory *factory,
spa_bt_transport_add_listener(this->transport, spa_bt_transport_add_listener(this->transport,
&this->transport_listener, &transport_events, this); &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; return 0;
} }