bluetooth: Add avrcp_absolute_volume module flag for disablement
authorMarijn Suijten <marijns95@gmail.com>
Thu, 22 Apr 2021 19:59:37 +0000 (21:59 +0200)
committerPulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org>
Mon, 17 May 2021 14:50:03 +0000 (14:50 +0000)
Not all peers might work fine with Absolute Volume, provide the user
with an option to disable it without impairing other AVRCP-related
commands like media status and playback controls.

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>

src/modules/bluetooth/bluez5-util.c
src/modules/bluetooth/bluez5-util.h
src/modules/bluetooth/module-bluez5-device.c
src/modules/bluetooth/module-bluez5-discover.c

index f24e12e..067c797 100644 (file)
@@ -534,7 +534,7 @@ static pa_volume_t pa_bluetooth_transport_set_volume(pa_bluetooth_transport *t,
 
     pa_assert(t);
     pa_assert(t->device);
-    pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+    pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
     pa_assert(t->device->discovery);
 
     gain = volume_to_a2dp_gain(volume);
@@ -593,6 +593,10 @@ static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport
     char volume_str[PA_VOLUME_SNPRINT_MAX];
 
     pa_assert(t);
+    pa_assert(t->device);
+
+    if (!t->device->avrcp_absolute_volume)
+        return;
 
     is_source = t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
 
@@ -787,7 +791,7 @@ static void bluez5_transport_get_volume(pa_bluetooth_transport *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(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,
@@ -800,6 +804,10 @@ static void bluez5_transport_get_volume(pa_bluetooth_transport *t) {
 
 void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t) {
     pa_assert(t);
+    pa_assert(t->device);
+
+    if (!t->device->avrcp_absolute_volume)
+        return;
 
     if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
         /* A2DP Absolute Volume control (AVRCP 1.4) is optional */
@@ -2057,6 +2065,10 @@ bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_pr
     pa_assert_not_reached();
 }
 
+bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile) {
+    return profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+}
+
 static const pa_a2dp_endpoint_conf *a2dp_sep_to_a2dp_endpoint_conf(const char *endpoint) {
     const char *codec_name;
 
index 762ec50..e458086 100644 (file)
@@ -133,6 +133,7 @@ struct pa_bluetooth_device {
     bool valid;
     bool autodetect_mtu;
     bool codec_switching_in_progress;
+    bool avrcp_absolute_volume;
     uint32_t output_rate_refresh_interval_ms;
 
     /* Device information */
@@ -204,6 +205,7 @@ pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hoo
 
 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile);
 bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t profile);
+bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile);
 
 static inline bool pa_bluetooth_uuid_is_hsp_hs(const char *uuid) {
     return pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS_ALT);
index 5dfe576..993fac3 100644 (file)
@@ -60,6 +60,7 @@ PA_MODULE_USAGE(
     "path=<device object path>"
     "autodetect_mtu=<boolean>"
     "output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>"
+    "avrcp_absolute_volume=<synchronize volume with peer, true by default>"
 );
 
 #define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
@@ -71,6 +72,7 @@ static const char* const valid_modargs[] = {
     "path",
     "autodetect_mtu",
     "output_rate_refresh_interval_ms",
+    "avrcp_absolute_volume",
     NULL
 };
 
@@ -899,6 +901,9 @@ static void source_setup_volume_callback(pa_source *s) {
     pa_assert(u->source == s);
     pa_assert(u->transport);
 
+    if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
+        return;
+
     /* Remote volume control has to be supported for the callback to make sense,
      * otherwise this source should continue performing attenuation in software
      * without HW_VOLUME_CTL.
@@ -1124,6 +1129,9 @@ static void sink_setup_volume_callback(pa_sink *s) {
     pa_assert(u->sink == s);
     pa_assert(u->transport);
 
+    if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
+        return;
+
     /* Remote volume control has to be supported for the callback to make sense,
      * otherwise this sink should continue performing attenuation in software
      * without HW_VOLUME_CTL.
@@ -2404,7 +2412,7 @@ static char *list_codecs(struct userdata *u) {
 
     pa_json_encoder_begin_element_array(encoder);
 
-    if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
+    if (pa_bluetooth_profile_is_a2dp(u->profile)) {
         is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
 
         a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints;
@@ -2620,7 +2628,7 @@ int pa__init(pa_module* m) {
     struct userdata *u;
     const char *path;
     pa_modargs *ma;
-    bool autodetect_mtu;
+    bool autodetect_mtu, avrcp_absolute_volume;
     char *message_handler_path;
     uint32_t output_rate_refresh_interval_ms;
 
@@ -2669,6 +2677,14 @@ int pa__init(pa_module* m) {
 
     u->device->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms;
 
+    avrcp_absolute_volume = true;
+    if (pa_modargs_get_value_boolean(ma, "avrcp_absolute_volume", &avrcp_absolute_volume) < 0) {
+        pa_log("Invalid boolean value for avrcp_absolute_volume parameter");
+        goto fail_free_modargs;
+    }
+
+    u->device->avrcp_absolute_volume = avrcp_absolute_volume;
+
     pa_modargs_free(ma);
 
     u->device_connection_changed_slot =
index 6c6988b..b13517e 100644 (file)
@@ -41,6 +41,7 @@ PA_MODULE_USAGE(
     "output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>"
     "enable_native_hsp_hs=<boolean, enable HSP support in native backend>"
     "enable_native_hfp_hf=<boolean, enable HFP support in native backend>"
+    "avrcp_absolute_volume=<synchronize volume with peer, true by default>"
 );
 
 static const char* const valid_modargs[] = {
@@ -50,6 +51,7 @@ static const char* const valid_modargs[] = {
     "output_rate_refresh_interval_ms",
     "enable_native_hsp_hs",
     "enable_native_hfp_hf",
+    "avrcp_absolute_volume",
     NULL
 };
 
@@ -60,6 +62,7 @@ struct userdata {
     pa_hook_slot *device_connection_changed_slot;
     pa_bluetooth_discovery *discovery;
     bool autodetect_mtu;
+    bool avrcp_absolute_volume;
     uint32_t output_rate_refresh_interval_ms;
 };
 
@@ -83,8 +86,12 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
     if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
         /* a new device has been connected */
         pa_module *m;
-        char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i output_rate_refresh_interval_ms=%u",
-                                       d->path, (int)u->autodetect_mtu, u->output_rate_refresh_interval_ms);
+        char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i output_rate_refresh_interval_ms=%u"
+                                       " avrcp_absolute_volume=%i",
+                                       d->path,
+                                       (int)u->autodetect_mtu,
+                                       u->output_rate_refresh_interval_ms,
+                                       (int)u->avrcp_absolute_volume);
 
         pa_log_debug("Loading module-bluez5-device %s", args);
         pa_module_load(&m, u->module->core, "module-bluez5-device", args);
@@ -116,6 +123,7 @@ int pa__init(pa_module *m) {
     int headset_backend;
     bool autodetect_mtu;
     bool enable_msbc;
+    bool avrcp_absolute_volume;
     uint32_t output_rate_refresh_interval_ms;
     bool enable_native_hsp_hs;
     bool enable_native_hfp_hf;
@@ -161,6 +169,12 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
+    avrcp_absolute_volume = true;
+    if (pa_modargs_get_value_boolean(ma, "avrcp_absolute_volume", &avrcp_absolute_volume) < 0) {
+        pa_log("avrcp_absolute_volume must be true or false");
+        goto fail;
+    }
+
     output_rate_refresh_interval_ms = DEFAULT_OUTPUT_RATE_REFRESH_INTERVAL_MS;
     if (pa_modargs_get_value_u32(ma, "output_rate_refresh_interval_ms", &output_rate_refresh_interval_ms) < 0) {
         pa_log("Invalid value for output_rate_refresh_interval parameter.");
@@ -171,6 +185,7 @@ int pa__init(pa_module *m) {
     u->module = m;
     u->core = m->core;
     u->autodetect_mtu = autodetect_mtu;
+    u->avrcp_absolute_volume = avrcp_absolute_volume;
     u->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms;
     u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);