mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-06-07 03:01:36 -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
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue