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:
Wim Taymans 2019-05-16 13:18:45 +02:00
parent c348790ca1
commit 6cfddde39c
6 changed files with 213 additions and 34 deletions

View file

@ -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

View file

@ -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)

View file

@ -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;

View file

@ -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);
}

View file

@ -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)

View file

@ -1,5 +1,6 @@
bluez5_sources = ['plugin.c',
'a2dp-codecs.c',
'a2dp-sink.c',
'a2dp-source.c',
'bluez5-device.c',