Commit eab18870 authored by Marijn Suijten's avatar Marijn Suijten 🦀

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 and
passthrough mode (in case of a2dp_sink) will be enabled.

For safety the setup is also performed if the property is already
available initially.
parent 57524159
......@@ -709,6 +709,67 @@ static void bluez5_transport_set_source_volume(pa_bluetooth_transport *t, uint16
bluez5_transport_set_volume(t, gain);
}
static void request_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_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);
pa_bluetooth_transport_remote_volume_changed(t, gain);
finish:
dbus_message_unref(r);
PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
pa_dbus_pending_free(p);
}
static void bluez5_transport_request_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(pa_bluetooth_profile_is_a2dp(t->profile));
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, request_volume_reply, t);
}
bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
unsigned i, bluetooth_profile_count;
......@@ -2412,7 +2473,7 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
/* AVRCP Absolute Volume is dynamically detected and enabled as soon as the
* Volume property becomes available */
t->rx_soft_volume = true;
t->tx_soft_volume = false;
t->tx_soft_volume = true;
t->max_rx_volume_gain = A2DP_MAX_GAIN;
t->max_tx_volume_gain = A2DP_MAX_GAIN;
t->acquire = bluez5_transport_acquire_cb;
......@@ -2423,6 +2484,9 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
/* A2DP Absolute Volume control (AVRCP 1.4) is optional */
bluez5_transport_request_volume(t);
return NULL;
fail:
......
......@@ -1001,6 +1001,7 @@ static void source_set_volume_cb(pa_source *s) {
if (u->transport->rx_soft_volume)
pa_cvolume_set(&s->soft_volume, u->decoder_sample_spec.channels, volume);
else
// TODO: Passthrough?
pa_cvolume_reset(&s->soft_volume, u->decoder_sample_spec.channels);
if (u->transport->set_rx_volume_gain)
......@@ -1164,6 +1165,7 @@ static void sink_set_volume_cb(pa_sink *s) {
if (u->transport->tx_soft_volume)
pa_cvolume_set(&s->soft_volume, u->encoder_sample_spec.channels, volume);
else
// TODO: Passthrough?
pa_cvolume_reset(&s->soft_volume, u->encoder_sample_spec.channels);
if (u->transport->set_tx_volume_gain)
......@@ -2413,6 +2415,23 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa
return PA_HOOK_OK;
}
static void sink_set_a2dp_remote_controlled(pa_sink *s) {
struct userdata *u;
pa_assert(s);
pa_assert(s->core);
u = s->userdata;
pa_assert(u);
pa_assert(u->sink == s);
// pa_sink_set_set_volume_callback(s, sink_set_volume_cb);
// s->n_volume_steps = A2DP_MAX_GAIN + 1;
/* Reset local attenuation */
pa_sink_enter_passthrough(s);
}
static pa_hook_result_t transport_tx_volume_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
pa_volume_t volume;
pa_cvolume v;
......@@ -2430,6 +2449,19 @@ static pa_hook_result_t transport_tx_volume_gain_changed_cb(pa_bluetooth_discove
if (volume > PA_VOLUME_NORM)
volume = PA_VOLUME_NORM;
if (pa_bluetooth_profile_is_a2dp_sink(t->profile)) {
if (!u->sink) {
pa_log_warn("Received a2dp gain change without connected sink");
return PA_HOOK_OK;
}
/* The first time this callback fires: peer supports Absolute Volume */
if (t->tx_soft_volume) {
sink_set_a2dp_remote_controlled(u->sink);
t->tx_soft_volume = false;
}
}
pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume);
if (!t->tx_soft_volume)
......@@ -2440,6 +2472,21 @@ static pa_hook_result_t transport_tx_volume_gain_changed_cb(pa_bluetooth_discove
return PA_HOOK_OK;
}
static void source_set_a2dp_remote_controlled(pa_source *s) {
struct userdata *u;
pa_assert(s);
pa_assert(s->core);
u = s->userdata;
pa_assert(u);
pa_assert(u->source == s);
pa_assert(u->transport);
// pa_source_set_set_volume_callback(s, source_set_volume_cb);
// s->n_volume_steps = A2DP_MAX_GAIN + 1;
}
static pa_hook_result_t transport_rx_volume_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
pa_volume_t volume;
pa_cvolume v;
......@@ -2458,6 +2505,19 @@ static pa_hook_result_t transport_rx_volume_gain_changed_cb(pa_bluetooth_discove
if (volume > PA_VOLUME_NORM)
volume = PA_VOLUME_NORM;
if (pa_bluetooth_profile_is_a2dp_source(t->profile)) {
if (!u->source) {
pa_log_warn("Received a2dp gain change without connected source");
return PA_HOOK_OK;
}
/* The first time this callback fires: peer supports Absolute Volume */
if (t->rx_soft_volume) {
source_set_a2dp_remote_controlled(u->source);
t->rx_soft_volume = false;
}
}
pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume);
if (!t->rx_soft_volume)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment