[pulseaudio-discuss] [PATCH v4] bluetooth: Add optional heuristic for switching between hsp and a2dp profiles

Pali Rohár pali.rohar at gmail.com
Sun Sep 11 14:41:02 UTC 2016


Not all VOIP applications (specially those which use alsa) set media.role to
phone. This means we need some heuristic to determinate if we want to switch
from a2dp to hsp profile based on number and types of source output (recording)
streams.

And also some people want to use their bluetooth headset (with microphone) as
their default recording device but some do not want to because of low quality.

This patch implements optional heuristic which is disabled by default. It is
disabled by default to not break experience of current pulseaudio users because
heuristic cannot be optimal. Heuristic is implemented in module-bluetooth-policy
module and decide if pulseaudio should switch to a hsp profile or not. It checks
if there is some source output with pass all these conditions:

* does not have set media.role
* does not use peak resample method (which is used by desktop volume programs)
* has assigned client/application (non virtual stream)
* does not record from monitor of sink

And if yes it switch to hsp profile.

By default this heuristic is disabled and can be enabled when loading module
module-bluetooth-policy with specifying parameter auto_switch=2

Because it is disabled by default nobody will be affected by this change unless
manually change auto_switch parameter.

Signed-off-by: Pali Rohár <pali.rohar at gmail.com>
---
 src/modules/bluetooth/module-bluetooth-policy.c |   43 ++++++++++++++++-------
 1 file changed, 30 insertions(+), 13 deletions(-)

diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c
index 68c8ab4..3f085b8 100644
--- a/src/modules/bluetooth/module-bluetooth-policy.c
+++ b/src/modules/bluetooth/module-bluetooth-policy.c
@@ -38,7 +38,7 @@ PA_MODULE_DESCRIPTION("Policy module to make using bluetooth devices out-of-the-
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
-        "auto_switch=<Switch between hsp and a2dp profile?> "
+        "auto_switch=<Switch between hsp and a2dp profile? (0 - never, 1 - media.role=phone, 2 - heuristic> "
         "a2dp_source=<Handle a2dp_source card profile (sink role)?> "
         "ag=<Handle headset_audio_gateway card profile (headset role)?> "
         "hfgw=<Handle hfgw card profile (headset role)?> DEPRECATED");
@@ -52,6 +52,7 @@ static const char* const valid_modargs[] = {
 };
 
 struct userdata {
+    uint32_t auto_switch;
     bool enable_a2dp_source;
     bool enable_ag;
     pa_hook_slot *source_put_slot;
@@ -210,7 +211,8 @@ static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) {
 }
 
 /* Return true if we should ignore this source output */
-static bool ignore_output(pa_source_output *source_output) {
+static bool ignore_output(pa_source_output *source_output, void *userdata) {
+    struct userdata *u = userdata;
     const char *s;
 
     /* New applications could set media.role for identifying streams */
@@ -219,16 +221,32 @@ static bool ignore_output(pa_source_output *source_output) {
     if (s)
         return !pa_streq(s, "phone");
 
-    return true;
+    /* If media.role is not set use some heuristic (if enabled) */
+    if (u->auto_switch != 2)
+        return true;
+
+    /* Ignore if resample method is peaks (used by desktop volume programs) */
+    if (pa_source_output_get_resample_method(source_output) == PA_RESAMPLER_PEAKS)
+        return true;
+
+    /* Ignore if there is no client/application assigned (used by virtual stream) */
+    if (!source_output->client)
+        return true;
+
+    /* Ignore if recording from monitor of sink */
+    if (source_output->direct_on_input)
+        return true;
+
+    return false;
 }
 
-static unsigned source_output_count(pa_core *c) {
+static unsigned source_output_count(pa_core *c, void *userdata) {
     pa_source_output *source_output;
     uint32_t idx;
     unsigned count = 0;
 
     PA_IDXSET_FOREACH(source_output, c->source_outputs, idx)
-        if (!ignore_output(source_output))
+        if (!ignore_output(source_output, userdata))
             ++count;
 
     return count;
@@ -248,7 +266,7 @@ static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_ou
     pa_assert(c);
     pa_assert(source_output);
 
-    if (ignore_output(source_output))
+    if (ignore_output(source_output, userdata))
         return PA_HOOK_OK;
 
     switch_profile_all(c->cards, false, userdata);
@@ -260,11 +278,11 @@ static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source
     pa_assert(c);
     pa_assert(source_output);
 
-    if (ignore_output(source_output))
+    if (ignore_output(source_output, userdata))
         return PA_HOOK_OK;
 
     /* If there are still some source outputs do nothing (count is with *this* source_output, so +1) */
-    if (source_output_count(c) > 1)
+    if (source_output_count(c, userdata) > 1)
         return PA_HOOK_OK;
 
     switch_profile_all(c->cards, true, userdata);
@@ -278,7 +296,7 @@ static pa_hook_result_t card_init_profile_hook_callback(pa_core *c, pa_card *car
     pa_assert(c);
     pa_assert(card);
 
-    if (source_output_count(c) == 0)
+    if (source_output_count(c, userdata) == 0)
         return PA_HOOK_OK;
 
     /* Only consider bluetooth cards */
@@ -388,7 +406,6 @@ static void handle_all_profiles(pa_core *core) {
 int pa__init(pa_module *m) {
     pa_modargs *ma;
     struct userdata *u;
-    bool auto_switch;
 
     pa_assert(m);
 
@@ -399,8 +416,8 @@ int pa__init(pa_module *m) {
 
     m->userdata = u = pa_xnew0(struct userdata, 1);
 
-    auto_switch = true;
-    if (pa_modargs_get_value_boolean(ma, "auto_switch", &auto_switch) < 0) {
+    u->auto_switch = 1;
+    if (pa_modargs_get_value_u32(ma, "auto_switch", &u->auto_switch) < 0) {
         pa_log("Failed to parse auto_switch argument.");
         goto fail;
     }
@@ -429,7 +446,7 @@ int pa__init(pa_module *m) {
     u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL,
                                        (pa_hook_cb_t) sink_put_hook_callback, u);
 
-    if (auto_switch) {
+    if (u->auto_switch) {
         u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL,
                                                     (pa_hook_cb_t) source_output_put_hook_callback, u);
 
-- 
1.7.9.5



More information about the pulseaudio-discuss mailing list