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

Pali Rohár pali.rohar at gmail.com
Mon Sep 19 14:44:00 UTC 2016


Hi! Please let me know what do you think about this v4 version of my patch.

On Sunday 11 September 2016 16:41:02 Pali Rohár wrote:
> 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);
>  

-- 
Pali Rohár
pali.rohar at gmail.com


More information about the pulseaudio-discuss mailing list