bluetooth: Delay A2DP Absolute Volume setup until property is available

The Volume property on org.bluez.MediaTransport1 is required to utilize
Absolute Volume, but it will only become availabe if the peer device
supports the feature.  This happens asynchronously somewhere after the
transport itself has been acquired, after which the callbacks are
attached and software volume is reset.

To prevent race conditions availability of the property is also checked
on startup through a "Get" call.

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
This commit is contained in:
Marijn Suijten 2021-03-27 14:05:00 +01:00 committed by PulseAudio Marge Bot
parent c6b771537e
commit 25426bc029
3 changed files with 99 additions and 1 deletions

View file

@ -606,6 +606,16 @@ static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport
return;
t->sink_volume = volume;
hook = PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED;
/* A2DP Absolute Volume is optional. This callback is only
* attached when the peer supports it, and the hook handler
* further attaches the necessary hardware callback to the
* pa_sink and disables software attenuation.
*/
if (!t->set_sink_volume) {
pa_log_debug("A2DP sink supports volume control");
t->set_sink_volume = pa_bluetooth_transport_set_sink_volume;
}
} else {
pa_assert_not_reached();
}
@ -724,6 +734,78 @@ static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
pa_log_info("Transport %s released", t->path);
}
static void get_volume_reply(DBusPendingCall *pending, void *userdata) {
DBusMessage *r;
DBusMessageIter iter, variant;
pa_dbus_pending *p;
pa_bluetooth_discovery *y;
pa_bluetooth_transport *t;
uint16_t gain;
pa_volume_t volume;
pa_assert(pending);
pa_assert_se(p = userdata);
pa_assert_se(y = p->context_data);
pa_assert_se(t = p->call_data);
pa_assert_se(r = dbus_pending_call_steal_reply(pending));
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log_error(DBUS_INTERFACE_PROPERTIES ".Get %s Volume failed: %s: %s",
dbus_message_get_path(p->message),
dbus_message_get_error_name(r),
pa_dbus_get_error_message(r));
goto finish;
}
dbus_message_iter_init(r, &iter);
pa_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT);
dbus_message_iter_recurse(&iter, &variant);
pa_assert(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_UINT16);
dbus_message_iter_get_basic(&variant, &gain);
if (gain > A2DP_MAX_GAIN)
gain = A2DP_MAX_GAIN;
pa_log_debug("Received A2DP Absolute Volume %d", gain);
volume = a2dp_gain_to_volume(gain);
pa_bluetooth_transport_remote_volume_changed(t, volume);
finish:
dbus_message_unref(r);
PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
pa_dbus_pending_free(p);
}
static void bluez5_transport_get_volume(pa_bluetooth_transport *t) {
static const char *volume_str = "Volume";
static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
DBusMessage *m;
pa_assert(t);
pa_assert(t->device);
pa_assert(t->device->discovery);
pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Get"));
pa_assert_se(dbus_message_append_args(m,
DBUS_TYPE_STRING, &mediatransport_str,
DBUS_TYPE_STRING, &volume_str,
DBUS_TYPE_INVALID));
send_and_add_to_pending(t->device->discovery, m, get_volume_reply, t);
}
void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t) {
pa_assert(t);
if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
/* A2DP Absolute Volume control (AVRCP 1.4) is optional */
bluez5_transport_get_volume(t);
}
static ssize_t a2dp_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) {
ssize_t l = 0;
size_t written = 0;
@ -2114,7 +2196,6 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
t->acquire = bluez5_transport_acquire_cb;
t->release = bluez5_transport_release_cb;
t->set_sink_volume = pa_bluetooth_transport_set_sink_volume;
/* A2DP Absolute Volume is optional but BlueZ unconditionally reports
* feature category 2, meaning supporting it is mandatory.
* PulseAudio can and should perform the attenuation anyway in