mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-06-06 03:02:54 -04:00
Merge branch 'spa_bluez_follow_MediaPlayer1' into 'master'
Draft: RFC: spa: bluez: follow MediaPlayer1 objects and set media.* keys in a2dp-source See merge request pipewire/pipewire!802
This commit is contained in:
commit
3b79ecad7a
4 changed files with 498 additions and 14 deletions
|
|
@ -307,6 +307,40 @@ static inline bool spa_atod(const char *str, double *val)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find \a needle in the \a haystack array of \a n strings.
|
||||
*
|
||||
* \param haystack an array of \a n strings
|
||||
* \param n number of strings in \a haystack
|
||||
* \param cmp a three-way comparator
|
||||
* \param index pointer to save the index into if \a needle is found,
|
||||
* may be NULL; it is not modified if \a needle is not found
|
||||
*
|
||||
* \return true if \a needle is found, false otherwise
|
||||
*
|
||||
* \since 0.3.31
|
||||
*/
|
||||
static inline bool spa_find_str(const char * const haystack[], size_t n,
|
||||
const char *needle,
|
||||
int (*cmp)(const char *, const char *),
|
||||
size_t *index)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (cmp(needle, haystack[i]) != 0)
|
||||
continue;
|
||||
|
||||
if (index != NULL)
|
||||
*index = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#define spa_find_str_arr(haystack, needle, cmp, index) \
|
||||
(spa_find_str((haystack), SPA_N_ELEMENTS(haystack), (needle), (cmp), (index)))
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ struct spa_bt_monitor {
|
|||
struct spa_list device_list;
|
||||
struct spa_list remote_endpoint_list;
|
||||
struct spa_list transport_list;
|
||||
struct spa_list player_list;
|
||||
|
||||
unsigned int filters_added:1;
|
||||
unsigned int objects_listed:1;
|
||||
|
|
@ -747,6 +748,238 @@ static void adapter_free(struct spa_bt_adapter *adapter)
|
|||
free(adapter);
|
||||
}
|
||||
|
||||
static void device_remove_player(struct spa_bt_player *player)
|
||||
{
|
||||
if (player->device == NULL)
|
||||
return;
|
||||
|
||||
spa_list_remove(&player->device_link);
|
||||
|
||||
spa_hook_list_call(&player->device->listener_list,
|
||||
struct spa_bt_device_events, player_removed, SPA_VERSION_BT_DEVICE_EVENTS,
|
||||
player);
|
||||
|
||||
player->device = NULL;
|
||||
}
|
||||
|
||||
static void device_add_player(struct spa_bt_device *device, struct spa_bt_player *player)
|
||||
{
|
||||
if (player->device == device)
|
||||
return;
|
||||
|
||||
device_remove_player(player);
|
||||
|
||||
player->device = device;
|
||||
if (player->device == NULL)
|
||||
return;
|
||||
|
||||
spa_list_append(&player->device->player_list, &player->device_link);
|
||||
|
||||
spa_hook_list_call(&player->device->listener_list,
|
||||
struct spa_bt_device_events, player_added, SPA_VERSION_BT_DEVICE_EVENTS,
|
||||
player);
|
||||
}
|
||||
|
||||
static struct spa_bt_player *player_create(struct spa_bt_monitor *monitor, const char *path)
|
||||
{
|
||||
struct spa_bt_player *p = calloc(1, sizeof(*p));
|
||||
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
|
||||
p->monitor = monitor;
|
||||
p->path = strdup(path);
|
||||
|
||||
spa_list_append(&monitor->player_list, &p->link);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static struct spa_bt_player *player_find(const struct spa_bt_monitor *monitor, const char *path)
|
||||
{
|
||||
struct spa_bt_player *p;
|
||||
|
||||
spa_list_for_each(p, &monitor->player_list, link)
|
||||
if (spa_streq(p->path, path))
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char * const player_state_names[] =
|
||||
{
|
||||
[SPA_BT_PLAYER_STATE_UNKNOWN] = "unknown",
|
||||
[SPA_BT_PLAYER_STATE_PLAYING] = "playing",
|
||||
[SPA_BT_PLAYER_STATE_STOPPED] = "stopped",
|
||||
[SPA_BT_PLAYER_STATE_PAUSED] = "paused",
|
||||
[SPA_BT_PLAYER_STATE_FORWARD_SEEK] = "forward-seek",
|
||||
[SPA_BT_PLAYER_STATE_ERROR] = "error",
|
||||
};
|
||||
|
||||
static enum spa_bt_player_state str_to_player_state(const char *s)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if (!spa_find_str_arr(player_state_names, s, strcasecmp, &index))
|
||||
return SPA_BT_PLAYER_STATE_UNKNOWN;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static const char * const player_type_names[] =
|
||||
{
|
||||
[SPA_BT_PLAYER_TYPE_UNKNOWN] = "unknown",
|
||||
[SPA_BT_PLAYER_TYPE_AUDIO] = "audio",
|
||||
[SPA_BT_PLAYER_TYPE_VIDEO] = "video",
|
||||
[SPA_BT_PLAYER_TYPE_AUDIO_BROADCASTING] = "audio broadcasting",
|
||||
[SPA_BT_PLAYER_TYPE_VIDEO_BROADCASTING] = "video broadcasting",
|
||||
};
|
||||
|
||||
static enum spa_bt_player_type str_to_player_type(const char *s)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if (!spa_find_str_arr(player_type_names, s, strcasecmp, &index))
|
||||
return SPA_BT_PLAYER_TYPE_UNKNOWN;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static void player_parse_track(struct spa_bt_player *player, DBusMessageIter *props_iter)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = player->monitor;
|
||||
|
||||
while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
|
||||
DBusMessageIter it[2];
|
||||
const char *key;
|
||||
int type;
|
||||
|
||||
dbus_message_iter_recurse(props_iter, &it[0]);
|
||||
dbus_message_iter_get_basic(&it[0], &key);
|
||||
dbus_message_iter_next(&it[0]);
|
||||
dbus_message_iter_recurse(&it[0], &it[1]);
|
||||
|
||||
type = dbus_message_iter_get_arg_type(&it[1]);
|
||||
|
||||
if (type == DBUS_TYPE_STRING) {
|
||||
const char *value;
|
||||
|
||||
dbus_message_iter_get_basic(&it[1], &value);
|
||||
|
||||
spa_log_debug(monitor->log, "player %p track: %s=%s", player, key, value);
|
||||
|
||||
if (spa_streq(key, "Title")) {
|
||||
free(player->track.title);
|
||||
player->track.title = strdup(value);
|
||||
}
|
||||
else if (spa_streq(key, "Artist")) {
|
||||
free(player->track.artist);
|
||||
player->track.artist = strdup(value);
|
||||
}
|
||||
else if (spa_streq(key, "Album")) {
|
||||
free(player->track.album);
|
||||
player->track.album = strdup(value);
|
||||
}
|
||||
else if (spa_streq(key, "Genre")) {
|
||||
free(player->track.genre);
|
||||
player->track.genre = strdup(value);
|
||||
}
|
||||
else if (spa_streq(key, "TrackNumber")) {
|
||||
if (!spa_atou32(value, &player->track.number, 10))
|
||||
player->track.number = -1;
|
||||
}
|
||||
else if (spa_streq(key, "Duration")) {
|
||||
if (!spa_atou64(value, &player->track.duration, 10))
|
||||
player->track.duration = -1;
|
||||
}
|
||||
}
|
||||
|
||||
dbus_message_iter_next(props_iter);
|
||||
}
|
||||
}
|
||||
|
||||
static int player_update_props(struct spa_bt_player *player,
|
||||
DBusMessageIter *props_iter,
|
||||
DBusMessageIter *invalidated_iter)
|
||||
{
|
||||
struct spa_bt_monitor * const monitor = player->monitor;
|
||||
|
||||
while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
|
||||
DBusMessageIter it[2];
|
||||
const char *key;
|
||||
int type;
|
||||
|
||||
dbus_message_iter_recurse(props_iter, &it[0]);
|
||||
dbus_message_iter_get_basic(&it[0], &key);
|
||||
dbus_message_iter_next(&it[0]);
|
||||
dbus_message_iter_recurse(&it[0], &it[1]);
|
||||
|
||||
type = dbus_message_iter_get_arg_type(&it[1]);
|
||||
|
||||
if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
|
||||
const char *value;
|
||||
|
||||
dbus_message_iter_get_basic(&it[1], &value);
|
||||
|
||||
spa_log_debug(monitor->log, "player %p: %s=%s", player, key, value);
|
||||
|
||||
if (spa_streq(key, "Status")) {
|
||||
player->status = str_to_player_state(value);
|
||||
}
|
||||
else if (spa_streq(key, "Device")) {
|
||||
struct spa_bt_device *d = spa_bt_device_find(monitor, value);
|
||||
|
||||
spa_log_debug(monitor->log, "player %p: device -> %p", player, d);
|
||||
|
||||
device_add_player(d, player);
|
||||
}
|
||||
else if (spa_streq(key, "Name")) {
|
||||
free(player->name);
|
||||
player->name = strdup(value);
|
||||
}
|
||||
else if (spa_streq(key, "Type")) {
|
||||
player->type = str_to_player_type(value);
|
||||
}
|
||||
}
|
||||
else if (type == DBUS_TYPE_ARRAY) {
|
||||
if (spa_streq(key, "Track")) {
|
||||
DBusMessageIter dict_iter;
|
||||
|
||||
dbus_message_iter_recurse(&it[1], &dict_iter);
|
||||
player_parse_track(player, &dict_iter);
|
||||
}
|
||||
}
|
||||
|
||||
dbus_message_iter_next(props_iter);
|
||||
}
|
||||
|
||||
if (player->device != NULL)
|
||||
spa_hook_list_call(&player->device->listener_list,
|
||||
struct spa_bt_device_events, player_changed, SPA_VERSION_BT_DEVICE_EVENTS,
|
||||
player);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void player_free(struct spa_bt_player *player)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = player->monitor;
|
||||
|
||||
spa_log_debug(monitor->log, "%p", player);
|
||||
|
||||
device_remove_player(player);
|
||||
|
||||
spa_list_remove(&player->link);
|
||||
|
||||
free(player->track.title);
|
||||
free(player->track.artist);
|
||||
free(player->track.album);
|
||||
free(player->track.genre);
|
||||
free(player->path);
|
||||
free(player->name);
|
||||
free(player);
|
||||
}
|
||||
|
||||
struct spa_bt_device *spa_bt_device_find(struct spa_bt_monitor *monitor, const char *path)
|
||||
{
|
||||
struct spa_bt_device *d;
|
||||
|
|
@ -783,6 +1016,7 @@ static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const
|
|||
spa_list_init(&d->remote_endpoint_list);
|
||||
spa_list_init(&d->transport_list);
|
||||
spa_list_init(&d->codec_switch_list);
|
||||
spa_list_init(&d->player_list);
|
||||
|
||||
spa_hook_list_init(&d->listener_list);
|
||||
|
||||
|
|
@ -806,6 +1040,7 @@ static void device_free(struct spa_bt_device *device)
|
|||
struct spa_bt_remote_endpoint *ep, *tep;
|
||||
struct spa_bt_a2dp_codec_switch *sw;
|
||||
struct spa_bt_transport *t, *tt;
|
||||
struct spa_bt_player *p, *tp;
|
||||
struct spa_bt_monitor *monitor = device->monitor;
|
||||
|
||||
spa_log_debug(monitor->log, "%p", device);
|
||||
|
|
@ -836,6 +1071,11 @@ static void device_free(struct spa_bt_device *device)
|
|||
spa_list_consume(sw, &device->codec_switch_list, device_link)
|
||||
a2dp_codec_switch_free(sw);
|
||||
|
||||
spa_list_for_each_safe(p, tp, &device->player_list, device_link) {
|
||||
spa_assert(p->device == device);
|
||||
device_remove_player(p);
|
||||
}
|
||||
|
||||
spa_list_remove(&device->link);
|
||||
free(device->path);
|
||||
free(device->alias);
|
||||
|
|
@ -3356,6 +3596,21 @@ static void interface_added(struct spa_bt_monitor *monitor,
|
|||
if (d)
|
||||
spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles);
|
||||
}
|
||||
else if (spa_streq(interface_name, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
|
||||
struct spa_bt_player *p;
|
||||
|
||||
p = player_find(monitor, object_path);
|
||||
if (p == NULL) {
|
||||
p = player_create(monitor, object_path);
|
||||
if (p == NULL) {
|
||||
spa_log_warn(monitor->log, "can't create Bluetooth player %s: %m",
|
||||
object_path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
player_update_props(p, props_iter, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void interfaces_added(struct spa_bt_monitor *monitor, DBusMessageIter *arg_iter)
|
||||
|
|
@ -3418,6 +3673,12 @@ static void interfaces_removed(struct spa_bt_monitor *monitor, DBusMessageIter *
|
|||
if (d)
|
||||
spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles);
|
||||
}
|
||||
} else if (spa_streq(interface_name, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
|
||||
struct spa_bt_player *p;
|
||||
|
||||
p = player_find(monitor, object_path);
|
||||
if (p != NULL)
|
||||
player_free(p);
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&it);
|
||||
|
|
@ -3518,6 +3779,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
|
|||
struct spa_bt_device *d;
|
||||
struct spa_bt_remote_endpoint *ep;
|
||||
struct spa_bt_transport *t;
|
||||
struct spa_bt_player *p;
|
||||
|
||||
monitor->objects_listed = false;
|
||||
|
||||
|
|
@ -3534,6 +3796,8 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
|
|||
device_free(d);
|
||||
spa_list_consume(a, &monitor->adapter_list, link)
|
||||
adapter_free(a);
|
||||
spa_list_consume(p, &monitor->player_list, link)
|
||||
player_free(p);
|
||||
}
|
||||
|
||||
if (has_new_owner) {
|
||||
|
|
@ -3689,7 +3953,20 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
|
|||
|
||||
transport_update_props(transport, &it[1], NULL);
|
||||
}
|
||||
}
|
||||
else if (spa_streq(iface, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
|
||||
struct spa_bt_player *p;
|
||||
|
||||
p = player_find(monitor, path);
|
||||
if (p == NULL) {
|
||||
spa_log_warn(monitor->log, "properties changed in unknown player %s", path);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
spa_log_debug(monitor->log, "properties changed on player %s", path);
|
||||
|
||||
player_update_props(p, &it[1], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
dbus_error_free(&err);
|
||||
|
|
@ -3749,6 +4026,10 @@ static void add_filters(struct spa_bt_monitor *this)
|
|||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_MEDIA_PLAYER_INTERFACE "'", &err);
|
||||
|
||||
this->filters_added = true;
|
||||
|
||||
|
|
@ -3813,6 +4094,7 @@ static int impl_clear(struct spa_handle *handle)
|
|||
struct spa_bt_device *d;
|
||||
struct spa_bt_remote_endpoint *ep;
|
||||
struct spa_bt_transport *t;
|
||||
struct spa_bt_player *p;
|
||||
|
||||
monitor = (struct spa_bt_monitor *) handle;
|
||||
|
||||
|
|
@ -3837,6 +4119,8 @@ static int impl_clear(struct spa_handle *handle)
|
|||
device_free(d);
|
||||
spa_list_consume(a, &monitor->adapter_list, link)
|
||||
adapter_free(a);
|
||||
spa_list_consume(p, &monitor->player_list, link)
|
||||
player_free(p);
|
||||
|
||||
if (monitor->backend_native) {
|
||||
spa_bt_backend_free(monitor->backend_native);
|
||||
|
|
@ -4049,6 +4333,7 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
spa_list_init(&this->device_list);
|
||||
spa_list_init(&this->remote_endpoint_list);
|
||||
spa_list_init(&this->transport_list);
|
||||
spa_list_init(&this->player_list);
|
||||
|
||||
if ((res = parse_codec_array(this, info)) < 0)
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ extern "C" {
|
|||
#define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
|
||||
#define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
|
||||
#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
|
||||
#define BLUEZ_MEDIA_PLAYER_INTERFACE BLUEZ_SERVICE ".MediaPlayer1"
|
||||
#define BLUEZ_INTERFACE_BATTERY_PROVIDER BLUEZ_SERVICE ".BatteryProvider1"
|
||||
#define BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER BLUEZ_SERVICE ".BatteryProviderManager1"
|
||||
|
||||
|
|
@ -411,6 +412,7 @@ static inline enum spa_bt_form_factor spa_bt_form_factor_from_class(uint32_t blu
|
|||
|
||||
struct spa_bt_a2dp_codec_switch;
|
||||
struct spa_bt_transport;
|
||||
struct spa_bt_player;
|
||||
|
||||
struct spa_bt_device_events {
|
||||
#define SPA_VERSION_BT_DEVICE_EVENTS 0
|
||||
|
|
@ -427,6 +429,12 @@ struct spa_bt_device_events {
|
|||
|
||||
/** Device freed */
|
||||
void (*destroy) (void *data);
|
||||
|
||||
void (*player_added) (void *data, struct spa_bt_player *player);
|
||||
|
||||
void (*player_changed) (void *data, struct spa_bt_player *player);
|
||||
|
||||
void (*player_removed) (void *data, struct spa_bt_player *player);
|
||||
};
|
||||
|
||||
struct spa_bt_device {
|
||||
|
|
@ -460,6 +468,7 @@ struct spa_bt_device {
|
|||
struct spa_list remote_endpoint_list;
|
||||
struct spa_list transport_list;
|
||||
struct spa_list codec_switch_list;
|
||||
struct spa_list player_list;
|
||||
uint8_t battery;
|
||||
int has_battery;
|
||||
|
||||
|
|
@ -644,6 +653,48 @@ static inline enum spa_bt_transport_state spa_bt_transport_state_from_string(con
|
|||
return SPA_BT_TRANSPORT_STATE_IDLE;
|
||||
}
|
||||
|
||||
enum spa_bt_player_state {
|
||||
SPA_BT_PLAYER_STATE_UNKNOWN,
|
||||
SPA_BT_PLAYER_STATE_PLAYING,
|
||||
SPA_BT_PLAYER_STATE_STOPPED,
|
||||
SPA_BT_PLAYER_STATE_PAUSED,
|
||||
SPA_BT_PLAYER_STATE_FORWARD_SEEK,
|
||||
SPA_BT_PLAYER_STATE_ERROR,
|
||||
};
|
||||
|
||||
enum spa_bt_player_type {
|
||||
SPA_BT_PLAYER_TYPE_UNKNOWN,
|
||||
SPA_BT_PLAYER_TYPE_AUDIO,
|
||||
SPA_BT_PLAYER_TYPE_VIDEO,
|
||||
SPA_BT_PLAYER_TYPE_AUDIO_BROADCASTING,
|
||||
SPA_BT_PLAYER_TYPE_VIDEO_BROADCASTING,
|
||||
};
|
||||
|
||||
struct spa_bt_player {
|
||||
struct spa_list link;
|
||||
|
||||
struct spa_bt_monitor *monitor;
|
||||
|
||||
struct spa_bt_device *device;
|
||||
struct spa_list device_link;
|
||||
|
||||
char *name;
|
||||
char *path;
|
||||
|
||||
struct {
|
||||
uint32_t number;
|
||||
uint64_t duration;
|
||||
|
||||
char *title;
|
||||
char *artist;
|
||||
char *album;
|
||||
char *genre;
|
||||
} track;
|
||||
|
||||
enum spa_bt_player_state status;
|
||||
enum spa_bt_player_type type;
|
||||
};
|
||||
|
||||
#define DEFAULT_AG_VOLUME 1.0f
|
||||
#define DEFAULT_RX_VOLUME 1.0f
|
||||
#define DEFAULT_TX_VOLUME 0.064f /* spa_bt_volume_hw_to_linear(40, 100) */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue