mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-06-06 03:02:54 -04:00
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:
parent
902a2113de
commit
d1c8b272ec
1 changed files with 127 additions and 13 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue