}
}
-static pa_volume_t pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
+static pa_volume_t pa_bluetooth_transport_set_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
static const char *volume_str = "Volume";
static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
DBusMessage *m;
/* Propagate rounding and bound checks */
volume = a2dp_gain_to_volume(gain);
- pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
-
- if (t->sink_volume == volume)
+ if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE && t->source_volume == volume)
+ return volume;
+ else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && t->sink_volume == volume)
return volume;
- t->sink_volume = volume;
+ if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+ t->source_volume = volume;
+ else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+ t->sink_volume = volume;
pa_log_debug("Sending A2DP volume %d/127 to peer", gain);
return volume;
}
+static pa_volume_t pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
+ pa_assert(t);
+ pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ return pa_bluetooth_transport_set_volume(t, volume);
+}
+
+static pa_volume_t pa_bluetooth_transport_set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
+ pa_assert(t);
+ pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+ return pa_bluetooth_transport_set_volume(t, volume);
+}
+
static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport *t, pa_volume_t volume) {
pa_bluetooth_hook_t hook;
bool is_source;
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
+ * the source role as it is the audio rendering device.
+ */
+ t->set_source_volume = pa_bluetooth_transport_set_source_volume;
pa_bluetooth_transport_reconfigure(t, &endpoint_conf->bt_codec, a2dp_transport_write, NULL);
pa_bluetooth_transport_put(t);
* If the peer is an AG however backend-native unconditionally provides this
* function, PA in the role of HS/HF is responsible for signalling support
* by emitting an initial volume command.
+ * For A2DP bluez-util also unconditionally provides this function to keep
+ * the peer informed about volume changes.
*/
if (!u->transport->set_source_volume)
return;
/* Send initial volume to peer, signalling support for volume control */
u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume));
} else {
+ /* It is yet unknown how (if at all) volume is synchronized for bidirectional
+ * A2DP codecs. Disallow attaching callbacks (and using HFP n_volume_steps)
+ * below to a pa_source if the peer is in A2DP_SINK role. This assert should
+ * be replaced with the proper logic when bidirectional codecs are implemented.
+ */
+ pa_assert(u->profile != PA_BLUETOOTH_PROFILE_A2DP_SINK);
+
if (s->set_volume == source_set_volume_cb)
return;