mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez: fix a2dp source
Actually compile the codec config and declare the config as extern or else it's just all 0. Only acquire the transport when ACTIVE. Implement transport state_change signal and acquire the transport when going to pending/active. Try to acquire the transport before we expose the device if we can. Force device expose when we get the device ServicesResolved=1 property update. Free transport when the rfcomm is closed. Implement various DBus signals to detect dynamic property updates.
This commit is contained in:
parent
c348790ca1
commit
6cfddde39c
6 changed files with 213 additions and 34 deletions
|
|
@ -284,15 +284,15 @@ static inline int a2dp_sbc_get_frequency(a2dp_sbc_t *config)
|
|||
}
|
||||
}
|
||||
|
||||
const a2dp_sbc_t bluez_a2dp_sbc;
|
||||
extern const a2dp_sbc_t bluez_a2dp_sbc;
|
||||
#if ENABLE_MP3
|
||||
const a2dp_mpeg_t bluez_a2dp_mpeg;
|
||||
extern const a2dp_mpeg_t bluez_a2dp_mpeg;
|
||||
#endif
|
||||
#if ENABLE_AAC
|
||||
const a2dp_aac_t bluez_a2dp_aac;
|
||||
extern const a2dp_aac_t bluez_a2dp_aac;
|
||||
#endif
|
||||
#if ENABLE_APTX
|
||||
const a2dp_aptx_t bluez_a2dp_aptx;
|
||||
extern const a2dp_aptx_t bluez_a2dp_aptx;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
|
|
@ -375,7 +376,8 @@ static void decode_sbc_data(struct impl *this, uint8_t *src, size_t src_size)
|
|||
this->sample_count += data[0].chunk->size / port->frame_size;
|
||||
|
||||
/* add the buffer to the queue */
|
||||
spa_log_debug(this->log, "data decoded successfully for buffer_id=%d", buffer->id);
|
||||
spa_log_debug(this->log, "data decoded %d successfully for buffer_id=%d",
|
||||
data[0].chunk->size, buffer->id);
|
||||
spa_list_append(&port->ready, &buffer->link);
|
||||
|
||||
spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_BUFFER);
|
||||
|
|
@ -432,24 +434,19 @@ stop:
|
|||
spa_loop_remove_source(this->data_loop, &this->source);
|
||||
}
|
||||
|
||||
static int do_start(struct impl *this)
|
||||
static int transport_start(struct impl *this)
|
||||
{
|
||||
int res, val;
|
||||
|
||||
if (this->started)
|
||||
return 0;
|
||||
|
||||
if (this->transport == NULL)
|
||||
return -EIO;
|
||||
|
||||
spa_log_debug(this->log, "a2dp-source %p: start", this);
|
||||
|
||||
if ((res = spa_bt_transport_acquire(this->transport, false)) < 0)
|
||||
return res;
|
||||
|
||||
sbc_init_a2dp(&this->sbc, 0, this->transport->configuration,
|
||||
this->transport->configuration_len);
|
||||
|
||||
val = fcntl(this->transport->fd, F_GETFL);
|
||||
fcntl(this->transport->fd, F_SETFL, val | O_NONBLOCK);
|
||||
|
||||
val = FILL_FRAMES * this->transport->write_mtu;
|
||||
if (setsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0)
|
||||
spa_log_warn(this->log, "a2dp-source %p: SO_SNDBUF %m", this);
|
||||
|
|
@ -472,11 +469,30 @@ static int do_start(struct impl *this)
|
|||
spa_loop_add_source(this->data_loop, &this->source);
|
||||
|
||||
this->sample_count = 0;
|
||||
this->started = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start(struct impl *this)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (this->started)
|
||||
return 0;
|
||||
|
||||
if (this->transport == NULL)
|
||||
return -EIO;
|
||||
|
||||
spa_log_debug(this->log, "a2dp-source %p: start", this);
|
||||
|
||||
if (this->transport->state == SPA_BT_TRANSPORT_STATE_ACTIVE)
|
||||
res = transport_start(this);
|
||||
|
||||
this->started = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int do_remove_source(struct spa_loop *loop,
|
||||
bool async,
|
||||
uint32_t seq,
|
||||
|
|
@ -1039,9 +1055,20 @@ static void transport_destroy(void *data)
|
|||
this->transport = NULL;
|
||||
}
|
||||
|
||||
static void transport_state_changed(void *data, enum spa_bt_transport_state old,
|
||||
enum spa_bt_transport_state state)
|
||||
{
|
||||
struct impl *this = data;
|
||||
if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING) {
|
||||
if (this->started)
|
||||
transport_start(this);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct spa_bt_transport_events transport_events = {
|
||||
SPA_VERSION_BT_TRANSPORT_EVENTS,
|
||||
.destroy = transport_destroy,
|
||||
.state_changed = transport_state_changed,
|
||||
};
|
||||
|
||||
static int impl_get_interface(struct spa_handle *handle, uint32_t type, void **interface)
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ static int emit_source_node(struct impl *this)
|
|||
snprintf(transport, 16, "%p", t);
|
||||
items[0] = SPA_DICT_ITEM_INIT("bluez5.transport", transport);
|
||||
|
||||
spa_bt_transport_acquire(t, true);
|
||||
|
||||
info = SPA_DEVICE_OBJECT_INFO_INIT();
|
||||
info.type = SPA_TYPE_INTERFACE_Node;
|
||||
info.factory = &spa_a2dp_source_factory;
|
||||
|
|
|
|||
|
|
@ -434,13 +434,16 @@ static int adapter_update_props(struct spa_bt_adapter *adapter,
|
|||
|
||||
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
|
||||
const char *uuid;
|
||||
enum spa_bt_profile profile;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &uuid);
|
||||
|
||||
spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, uuid);
|
||||
|
||||
adapter->profiles |= spa_bt_profile_from_uuid(uuid);
|
||||
profile = spa_bt_profile_from_uuid(uuid);
|
||||
|
||||
if (profile && (adapter->profiles & profile) == 0) {
|
||||
spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, uuid);
|
||||
adapter->profiles |= profile;
|
||||
}
|
||||
dbus_message_iter_next(&iter);
|
||||
}
|
||||
}
|
||||
|
|
@ -615,7 +618,7 @@ static int device_stop_timer(struct spa_bt_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int check_profiles(struct spa_bt_device *device)
|
||||
static int device_check_profiles(struct spa_bt_device *device, bool force)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = device->monitor;
|
||||
uint32_t connected_profiles = device->connected_profiles;
|
||||
|
|
@ -634,7 +637,7 @@ static int check_profiles(struct spa_bt_device *device)
|
|||
device_remove(monitor, device);
|
||||
}
|
||||
}
|
||||
else if ((device->profiles & connected_profiles) == device->profiles) {
|
||||
else if (force || (device->profiles & connected_profiles) == device->profiles) {
|
||||
device_stop_timer(device);
|
||||
device_add(monitor, device);
|
||||
} else {
|
||||
|
|
@ -651,7 +654,7 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
|
|||
device->connected = connected;
|
||||
|
||||
if (connected)
|
||||
check_profiles(device);
|
||||
device_check_profiles(device, false);
|
||||
else
|
||||
device_stop_timer(device);
|
||||
}
|
||||
|
|
@ -659,7 +662,7 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
|
|||
static int device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile)
|
||||
{
|
||||
device->connected_profiles |= profile;
|
||||
check_profiles(device);
|
||||
device_check_profiles(device, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -720,7 +723,7 @@ static int device_update_props(struct spa_bt_device *device,
|
|||
|
||||
dbus_message_iter_get_basic(&it[1], &value);
|
||||
|
||||
spa_log_debug(monitor->log, "device %p: %s=%d", device, key, value);
|
||||
spa_log_debug(monitor->log, "device %p: %s=%08x", device, key, value);
|
||||
|
||||
if (strcmp(key, "Class") == 0)
|
||||
device->bluetooth_class = value;
|
||||
|
|
@ -764,6 +767,10 @@ static int device_update_props(struct spa_bt_device *device,
|
|||
else if (strcmp(key, "Blocked") == 0) {
|
||||
device->blocked = value;
|
||||
}
|
||||
else if (strcmp(key, "ServicesResolved") == 0) {
|
||||
if (value)
|
||||
device_check_profiles(device, true);
|
||||
}
|
||||
}
|
||||
else if (strcmp(key, "UUIDs") == 0) {
|
||||
DBusMessageIter iter;
|
||||
|
|
@ -775,13 +782,15 @@ static int device_update_props(struct spa_bt_device *device,
|
|||
|
||||
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
|
||||
const char *uuid;
|
||||
enum spa_bt_profile profile;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &uuid);
|
||||
|
||||
spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid);
|
||||
|
||||
device->profiles |= spa_bt_profile_from_uuid(uuid);
|
||||
|
||||
profile = spa_bt_profile_from_uuid(uuid);
|
||||
if (profile && (device->profiles & profile) == 0) {
|
||||
spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid);
|
||||
device->profiles |= profile;
|
||||
}
|
||||
dbus_message_iter_next(&iter);
|
||||
}
|
||||
}
|
||||
|
|
@ -821,9 +830,26 @@ static struct spa_bt_transport *transport_create(struct spa_bt_monitor *monitor,
|
|||
|
||||
return t;
|
||||
}
|
||||
static void transport_set_state(struct spa_bt_transport *transport, enum spa_bt_transport_state state)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = transport->monitor;
|
||||
enum spa_bt_transport_state old = transport->state;
|
||||
|
||||
if (old != state) {
|
||||
transport->state = state;
|
||||
spa_log_debug(monitor->log, "transport %p: %s state changed %d -> %d",
|
||||
transport, transport->path, old, state);
|
||||
spa_bt_transport_emit_state_changed(transport, old, state);
|
||||
}
|
||||
}
|
||||
|
||||
static void transport_free(struct spa_bt_transport *transport)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = transport->monitor;
|
||||
|
||||
spa_log_debug(monitor->log, "transport %p: free %s", transport, transport->path);
|
||||
|
||||
transport_set_state(transport, SPA_BT_TRANSPORT_STATE_IDLE);
|
||||
|
||||
spa_bt_transport_emit_destroy(transport);
|
||||
|
||||
|
|
@ -877,7 +903,7 @@ static int transport_update_props(struct spa_bt_transport *transport,
|
|||
}
|
||||
}
|
||||
else if (strcmp(key, "State") == 0) {
|
||||
transport->state = spa_bt_transport_state_from_string(value);
|
||||
transport_set_state(transport, spa_bt_transport_state_from_string(value));
|
||||
}
|
||||
else if (strcmp(key, "Device") == 0) {
|
||||
transport->device = device_find(monitor, value);
|
||||
|
|
@ -960,7 +986,6 @@ static int transport_acquire(void *data, bool optional)
|
|||
spa_log_error(monitor->log, "Transport %s() failed for transport %s (%s)",
|
||||
method, transport->path, err.message);
|
||||
}
|
||||
|
||||
dbus_error_free(&err);
|
||||
return -EIO;
|
||||
}
|
||||
|
|
@ -1117,7 +1142,7 @@ static DBusHandlerResult endpoint_clear_configuration(DBusConnection *conn, DBus
|
|||
|
||||
transport_free(transport);
|
||||
if (device != NULL)
|
||||
check_profiles(device);
|
||||
device_check_profiles(device, false);
|
||||
}
|
||||
|
||||
if ((r = dbus_message_new_method_return(m)) == NULL)
|
||||
|
|
@ -1424,7 +1449,10 @@ static void rfcomm_event(struct spa_source *source)
|
|||
spa_log_error(monitor->log, "RFCOMM write error: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
transport_free(t);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1724,6 +1752,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
|
|||
|
||||
sco_listen(t);
|
||||
|
||||
spa_log_debug(monitor->log, "Transport %s available for profile %s", t->path, handler);
|
||||
|
||||
if ((r = dbus_message_new_method_return(m)) == NULL)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
if (!dbus_connection_send(conn, r, NULL))
|
||||
|
|
@ -1775,7 +1805,7 @@ static DBusHandlerResult profile_request_disconnection(DBusConnection *conn, DBu
|
|||
if (t->profile == profile)
|
||||
transport_free(t);
|
||||
}
|
||||
check_profiles(d);
|
||||
device_check_profiles(d, false);
|
||||
|
||||
if ((r = dbus_message_new_method_return(m)) == NULL)
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
|
|
@ -2039,6 +2069,120 @@ static void get_managed_objects(struct spa_bt_monitor *monitor)
|
|||
dbus_message_unref(m);
|
||||
}
|
||||
|
||||
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data)
|
||||
{
|
||||
struct spa_bt_monitor *monitor = user_data;
|
||||
DBusError err;
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
|
||||
spa_log_debug(monitor->log, "Name owner changed %s", dbus_message_get_path(m));
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
|
||||
spa_log_debug(monitor->log, "interfaces added %s", dbus_message_get_path(m));
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
|
||||
spa_log_debug(monitor->log, "interfaces removed %s", dbus_message_get_path(m));
|
||||
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
|
||||
DBusMessageIter it[2];
|
||||
const char *iface, *path;
|
||||
|
||||
if (!dbus_message_iter_init(m, &it[0]) ||
|
||||
strcmp(dbus_message_get_signature(m), "sa{sv}as") != 0) {
|
||||
spa_log_error(monitor->log, "Invalid signature found in PropertiesChanged");
|
||||
goto fail;
|
||||
}
|
||||
path = dbus_message_get_path(m);
|
||||
|
||||
dbus_message_iter_get_basic(&it[0], &iface);
|
||||
dbus_message_iter_next(&it[0]);
|
||||
dbus_message_iter_recurse(&it[0], &it[1]);
|
||||
|
||||
if (strcmp(iface, BLUEZ_ADAPTER_INTERFACE) == 0) {
|
||||
struct spa_bt_adapter *a;
|
||||
|
||||
a = adapter_find(monitor, path);
|
||||
if (a == NULL) {
|
||||
spa_log_warn(monitor->log,
|
||||
"Properties changed in unknown adapter %s", path);
|
||||
goto fail;
|
||||
}
|
||||
spa_log_debug(monitor->log, "Properties changed in adapter %s", path);
|
||||
|
||||
adapter_update_props(a, &it[1], NULL);
|
||||
}
|
||||
else if (strcmp(iface, BLUEZ_DEVICE_INTERFACE) == 0) {
|
||||
struct spa_bt_device *d;
|
||||
|
||||
d = device_find(monitor, path);
|
||||
if (d == NULL) {
|
||||
spa_log_warn(monitor->log,
|
||||
"Properties changed in unknown device %s", path);
|
||||
goto fail;
|
||||
}
|
||||
spa_log_debug(monitor->log, "Properties changed in device %s", path);
|
||||
|
||||
device_update_props(d, &it[1], NULL);
|
||||
}
|
||||
else if (strcmp(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
|
||||
struct spa_bt_transport *transport;
|
||||
|
||||
transport = transport_find(monitor, path);
|
||||
if (transport == NULL) {
|
||||
spa_log_warn(monitor->log,
|
||||
"Properties changed in unknown transport %s", path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spa_log_debug(monitor->log, "Properties changed in transport %s", path);
|
||||
|
||||
transport_update_props(transport, &it[1], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
static void add_filters(struct spa_bt_monitor *this)
|
||||
{
|
||||
DBusError err;
|
||||
|
||||
dbus_error_init(&err);
|
||||
|
||||
if (!dbus_connection_add_filter(this->conn, filter_cb, this, NULL)) {
|
||||
spa_log_error(this->log, "failed to add filter function");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='org.freedesktop.DBus',"
|
||||
"interface='org.freedesktop.DBus',member='NameOwnerChanged',"
|
||||
"arg0='" BLUEZ_SERVICE "'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_ADAPTER_INTERFACE "'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_DEVICE_INTERFACE "'", &err);
|
||||
dbus_bus_add_match(this->conn,
|
||||
"type='signal',sender='" BLUEZ_SERVICE "',"
|
||||
"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
|
||||
"arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", &err);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
dbus_error_free(&err);
|
||||
}
|
||||
|
||||
static int
|
||||
impl_monitor_set_callbacks(struct spa_monitor *monitor,
|
||||
const struct spa_monitor_callbacks *callbacks,
|
||||
|
|
@ -2053,6 +2197,7 @@ impl_monitor_set_callbacks(struct spa_monitor *monitor,
|
|||
this->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
|
||||
|
||||
if (callbacks) {
|
||||
add_filters(this);
|
||||
get_managed_objects(this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,8 @@ struct spa_bt_transport_events {
|
|||
uint32_t version;
|
||||
|
||||
void (*destroy) (void *data);
|
||||
void (*state_changed) (void *data, enum spa_bt_transport_state old,
|
||||
enum spa_bt_transport_state state);
|
||||
};
|
||||
|
||||
struct spa_bt_transport_implementation {
|
||||
|
|
@ -220,9 +222,11 @@ struct spa_bt_transport {
|
|||
struct spa_callbacks impl;
|
||||
};
|
||||
|
||||
#define spa_bt_transport_emit(t,m,v,...) spa_hook_list_call(&(t)->listener_list, \
|
||||
struct spa_bt_transport_events, m, v, ##__VA_ARGS__)
|
||||
#define spa_bt_transport_emit_destroy(t) spa_bt_transport_emit(t, destroy, 0)
|
||||
#define spa_bt_transport_emit(t,m,v,...) spa_hook_list_call(&(t)->listener_list, \
|
||||
struct spa_bt_transport_events, \
|
||||
m, v, ##__VA_ARGS__)
|
||||
#define spa_bt_transport_emit_destroy(t) spa_bt_transport_emit(t, destroy, 0)
|
||||
#define spa_bt_transport_emit_state_changed(t,...) spa_bt_transport_emit(t, state_changed, 0, __VA_ARGS__)
|
||||
|
||||
#define spa_bt_transport_add_listener(t,listener,events,data) \
|
||||
spa_hook_list_append(&(t)->listener_list, listener, events, data)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
bluez5_sources = ['plugin.c',
|
||||
'a2dp-codecs.c',
|
||||
'a2dp-sink.c',
|
||||
'a2dp-source.c',
|
||||
'bluez5-device.c',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue