bluez5: backend-hsphfpd: Add mSBC support

mSBC can be enabled by passing "bluez5.msbc-support=true" parameter to
pipewire-media-session.
mSBC is automatically selected if remote device supports it.
This commit is contained in:
Frédéric Danis 2020-08-18 17:57:09 +02:00
parent 7d28b51713
commit 6308c5d806
4 changed files with 110 additions and 23 deletions

View file

@ -53,6 +53,7 @@ struct spa_bt_backend {
bool acquire_in_progress;
unsigned int filters_added:1;
unsigned int msbc_supported:1;
};
enum hsphfpd_volume_control {
@ -92,6 +93,7 @@ struct hsphfpd_endpoint {
char *local_address;
enum hsphfpd_profile profile;
enum hsphfpd_role role;
int air_codecs;
};
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager"
@ -104,8 +106,12 @@ struct hsphfpd_endpoint {
#define APPLICATION_OBJECT_MANAGER_PATH "/Profile/hsphfpd/manager"
#define HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ "/Profile/hsphfpd/pcm_s16le_8khz_agent"
#define HSPHFP_AUDIO_CLIENT_MSBC "/Profile/hsphfpd/msbc_agent"
#define HSPHFP_AIR_CODEC_CVSD "CVSD"
#define HSPHFP_AIR_CODEC_MSBC "mSBC"
#define HSPHFP_AGENT_CODEC_PCM "PCM_s16le_8kHz"
#define HSPHFP_AGENT_CODEC_MSBC "mSBC"
#define APPLICATION_OBJECT_MANAGER_INTROSPECT_XML \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
@ -441,11 +447,11 @@ static void hsphfpd_parse_transport_properties(struct spa_bt_backend *backend, s
set_tx_volume_gain_property(transport, transport_data->tx_volume_gain);
}
static DBusHandlerResult audio_agent_get_property(DBusConnection *conn, DBusMessage *m, void *userdata)
static DBusHandlerResult audio_agent_get_property(DBusConnection *conn, DBusMessage *m, const char *path, void *userdata)
{
const char *interface;
const char *property;
const char *agent_codec = HSPHFP_AGENT_CODEC_PCM;
const char *agent_codec;
DBusMessage *r = NULL;
if (strcmp(dbus_message_get_signature(m), "ss") != 0) {
@ -469,6 +475,15 @@ static DBusHandlerResult audio_agent_get_property(DBusConnection *conn, DBusMess
goto fail;
}
if (strcmp(path, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ) == 0)
agent_codec = HSPHFP_AGENT_CODEC_PCM;
else if (strcmp(path, HSPHFP_AUDIO_CLIENT_MSBC) == 0)
agent_codec = HSPHFP_AGENT_CODEC_MSBC;
else {
r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid path in method call");
goto fail;
}
if ((r = dbus_message_new_method_return(m)) == NULL)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!dbus_message_append_args(r, DBUS_TYPE_STRING, &agent_codec, DBUS_TYPE_INVALID))
@ -482,12 +497,12 @@ fail:
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult audio_agent_getall_properties(DBusConnection *conn, DBusMessage *m, void *userdata)
static DBusHandlerResult audio_agent_getall_properties(DBusConnection *conn, DBusMessage *m, const char *path, void *userdata)
{
const char *interface;
DBusMessageIter iter, array, dict, data;
const char *agent_codec_key = "AgentCodec";
const char *agent_codec = HSPHFP_AGENT_CODEC_PCM;
const char *agent_codec;
DBusMessage *r = NULL;
if (strcmp(dbus_message_get_signature(m), "s") != 0) {
@ -502,6 +517,15 @@ static DBusHandlerResult audio_agent_getall_properties(DBusConnection *conn, DBu
goto fail;
}
if (strcmp(path, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ) == 0)
agent_codec = HSPHFP_AGENT_CODEC_PCM;
else if (strcmp(path, HSPHFP_AUDIO_CLIENT_MSBC) == 0)
agent_codec = HSPHFP_AGENT_CODEC_MSBC;
else {
r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid path in method call");
goto fail;
}
if (strcmp(interface, HSPHFPD_AUDIO_AGENT_INTERFACE) != 0)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@ -524,7 +548,7 @@ fail:
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBusMessage *m, void *userdata)
static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBusMessage *m, const char *path, void *userdata)
{
struct spa_bt_backend *backend = userdata;
DBusMessageIter arg_i;
@ -538,6 +562,7 @@ static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBus
uint16_t rx_volume_gain = -1;
uint16_t tx_volume_gain = -1;
uint16_t mtu = 0;
int codec;
struct hsphfpd_endpoint *endpoint;
struct spa_bt_transport *transport;
struct hsphfpd_transport_data *transport_data;
@ -565,6 +590,15 @@ static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBus
goto fail;
}
if (strcmp(path, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ) == 0)
codec = HFP_AUDIO_CODEC_CVSD;
else if (strcmp(path, HSPHFP_AUDIO_CLIENT_MSBC) == 0)
codec = HFP_AUDIO_CODEC_MSBC;
else {
r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "Invalid path");
goto fail;
}
dbus_message_iter_next(&arg_i);
parse_transport_properties_values(backend, transport_path, &arg_i,
&endpoint_path, &air_codec,
@ -652,6 +686,9 @@ static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBus
goto fail;
}
if (transport->codec != codec)
spa_log_warn(backend->log, "Expecting codec to be %d, got %d", transport->codec, codec);
if (transport->fd >= 0) {
close(fd);
spa_log_error(backend->log, "Endpoint %s has already active transport", endpoint_path);
@ -673,14 +710,13 @@ static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBus
pa_hook_fire(pa_bluetooth_discovery_hook(hsphfpd->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_TX_VOLUME_GAIN_CHANGED), transport);
#endif
transport->codec = HFP_AUDIO_CODEC_CVSD;
# if 0
transport->read_mtu = mtu;
transport->write_mtu = mtu;
#else
transport->read_mtu = 48;
transport->write_mtu = 48;
#endif
if (transport->codec == HFP_AUDIO_CODEC_CVSD) {
transport->read_mtu = 48;
transport->write_mtu = 48;
} else {
transport->read_mtu = mtu;
transport->write_mtu = mtu;
}
transport->fd = fd;
if ((r = dbus_message_new_method_return(m)) == NULL)
@ -721,11 +757,11 @@ static DBusHandlerResult audio_agent_endpoint_handler(DBusConnection *c, DBusMes
dbus_message_unref(r);
res = DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "Get"))
res = audio_agent_get_property(c, m, userdata);
res = audio_agent_get_property(c, m, path, userdata);
else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "GetAll"))
res = audio_agent_getall_properties(c, m, userdata);
res = audio_agent_getall_properties(c, m, path, userdata);
else if (dbus_message_is_method_call(m, HSPHFPD_AUDIO_AGENT_INTERFACE, "NewConnection"))
res = hsphfpd_new_audio_connection(c, m, userdata);
res = hsphfpd_new_audio_connection(c, m, path, userdata);
else
res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@ -790,6 +826,8 @@ static DBusHandlerResult application_object_manager_handler(DBusConnection *c, D
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{oa{sa{sv}}}", &array);
append_audio_agent_object(&array, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ, HSPHFP_AGENT_CODEC_PCM);
if (backend->msbc_supported)
append_audio_agent_object(&array, HSPHFP_AUDIO_CLIENT_MSBC, HSPHFP_AGENT_CODEC_MSBC);
dbus_message_iter_close_container(&iter, &array);
} else
@ -861,7 +899,7 @@ static int hsphfpd_audio_acquire(void *data, bool optional)
struct spa_bt_transport *transport = data;
struct spa_bt_backend *backend = transport->backend;
DBusMessage *m;
const char *air_codec = "CVSD";
const char *air_codec = HSPHFP_AIR_CODEC_CVSD;
const char *agent_codec = HSPHFP_AGENT_CODEC_PCM;
DBusPendingCall *call;
DBusError err;
@ -869,6 +907,11 @@ static int hsphfpd_audio_acquire(void *data, bool optional)
if (backend->acquire_in_progress)
return -EINPROGRESS;
if (transport->codec == HFP_AUDIO_CODEC_MSBC) {
air_codec = HSPHFP_AIR_CODEC_MSBC;
agent_codec = HSPHFP_AGENT_CODEC_MSBC;
}
m = dbus_message_new_method_call(HSPHFPD_SERVICE,
transport->path,
HSPHFPD_ENDPOINT_INTERFACE,
@ -995,6 +1038,26 @@ static DBusHandlerResult hsphfpd_parse_endpoint_properties(struct spa_bt_backend
spa_log_trace(backend->log, " %s: %d", key, value);
}
break;
case DBUS_TYPE_ARRAY:
{
if (strcmp(key, "AudioCodecs") == 0) {
DBusMessageIter array_i;
const char *value;
endpoint->air_codecs = 0;
dbus_message_iter_recurse(&value_i, &array_i);
while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
dbus_message_iter_get_basic(&array_i, &value);
if (strcmp(value, HSPHFP_AIR_CODEC_CVSD) == 0)
endpoint->air_codecs |= HFP_AUDIO_CODEC_CVSD;
if (strcmp(value, HSPHFP_AIR_CODEC_MSBC) == 0)
endpoint->air_codecs |= HFP_AUDIO_CODEC_MSBC;
dbus_message_iter_next(&array_i);
}
}
}
break;
}
dbus_message_iter_next(&element_i);
@ -1010,14 +1073,16 @@ static DBusHandlerResult hsphfpd_parse_endpoint_properties(struct spa_bt_backend
}
if ((t = spa_bt_transport_find(backend->monitor, endpoint->path)) != NULL) {
if (!endpoint->connected) {
/* Release transport on disconnection, or when mSBC is supported if there
is an update of the remote codecs */
if (!endpoint->connected || (backend->msbc_supported && (endpoint->air_codecs & HFP_AUDIO_CODEC_MSBC) && t->codec == HFP_AUDIO_CODEC_CVSD)) {
spa_bt_transport_free(t);
spa_bt_device_check_profiles(d, false);
spa_log_debug(backend->log, "Transport released for %s", endpoint->path);
} else
} else {
spa_log_debug(backend->log, "Transport already configured for %s", endpoint->path);
return DBUS_HANDLER_RESULT_HANDLED;
return DBUS_HANDLER_RESULT_HANDLED;
}
}
if (!endpoint->valid || !endpoint->connected)
@ -1047,6 +1112,10 @@ static DBusHandlerResult hsphfpd_parse_endpoint_properties(struct spa_bt_backend
else if (endpoint->role == HSPHFPD_ROLE_GATEWAY)
t->profile = SPA_BT_PROFILE_HFP_AG;
}
if (backend->msbc_supported && (endpoint->air_codecs & HFP_AUDIO_CODEC_MSBC))
t->codec = HFP_AUDIO_CODEC_MSBC;
else
t->codec = HFP_AUDIO_CODEC_CVSD;
spa_bt_device_connect_profile(t->device, t->profile);
@ -1379,6 +1448,8 @@ void backend_hsphfpd_free(struct spa_bt_backend *backend)
{
struct hsphfpd_endpoint *endpoint;
if (backend->msbc_supported)
dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_MSBC);
dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ);
dbus_connection_unregister_object_path(backend->conn, APPLICATION_OBJECT_MANAGER_PATH);
@ -1390,10 +1461,12 @@ void backend_hsphfpd_free(struct spa_bt_backend *backend)
struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
void *dbus_connection,
const struct spa_dict *info,
const struct spa_support *support,
uint32_t n_support)
{
struct spa_bt_backend *backend;
const char *str;
static const DBusObjectPathVTable vtable_application_object_manager = {
.message_function = application_object_manager_handler,
};
@ -1410,6 +1483,10 @@ struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
backend->conn = dbus_connection;
if (info && (str = spa_dict_lookup(info, "bluez5.msbc-support")))
backend->msbc_supported = strcmp(str, "true") == 0 || atoi(str) == 1;
else
backend->msbc_supported = false;
spa_list_init(&backend->endpoint_list);
@ -1428,6 +1505,15 @@ struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
return NULL;
}
if (backend->msbc_supported && !dbus_connection_register_object_path(backend->conn,
HSPHFP_AUDIO_CLIENT_MSBC,
&vtable_audio_agent_endpoint, backend)) {
dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ);
dbus_connection_unregister_object_path(backend->conn, APPLICATION_OBJECT_MANAGER_PATH);
free(backend);
return NULL;
}
hsphfpd_register_application(backend);
return backend;

View file

@ -1809,7 +1809,7 @@ impl_init(const struct spa_handle_factory *factory,
this->backend_hsp_native = backend_hsp_native_new(this, this->conn, support, n_support);
this->backend_ofono = backend_ofono_new(this, this->conn, support, n_support);
this->backend_hsphfpd = backend_hsphfpd_new(this, this->conn, support, n_support);
this->backend_hsphfpd = backend_hsphfpd_new(this, this->conn, info, support, n_support);
return 0;
}

View file

@ -378,6 +378,7 @@ static inline void backend_ofono_add_filters(struct spa_bt_backend *backend) {}
#ifdef HAVE_BLUEZ_5_BACKEND_HSPHFPD
struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
void *dbus_connection,
const struct spa_dict *info,
const struct spa_support *support,
uint32_t n_support);
void backend_hsphfpd_free(struct spa_bt_backend *backend);

View file

@ -447,7 +447,7 @@ int sm_bluez5_monitor_start(struct sm_media_session *session)
void *iface;
struct impl *impl;
handle = pw_context_load_spa_handle(context, SPA_NAME_API_BLUEZ5_ENUM_DBUS, NULL);
handle = pw_context_load_spa_handle(context, SPA_NAME_API_BLUEZ5_ENUM_DBUS, &session->props->dict);
if (handle == NULL) {
res = -errno;
pw_log_error("can't load %s: %m", SPA_NAME_API_BLUEZ5_ENUM_DBUS);