[pulseaudio-discuss] How to change profile of bluetooth headset for different applications?

Colin Guthrie gmane at colin.guthr.ie
Mon May 23 10:18:36 PDT 2011

'Twas brillig, and Lin, Mengdong at 23/05/11 16:58 did gyre and gimble:
> Is it possible to handle bluetooth profile change within
> “module-bluetooth-device”, instead of unload this module and reloading
> it with the new profile argument for the same device?

Yeah, this can be changed quite easily. It's already exposed in the API
to allow profile changes without reloading the module.

> I need my bluetooth headset working under A2DP profile for a music/video
> application and switch to HSP profile automatically for a incoming phone
> application. I wonder if the “module-bluetooth-device” can monitor
> application change by hooking sink input’s put and unlink event, change
> profile accordingly, and create/delete sink or source by itself. Then
> “module-switch-on-connect” can handle sink/source switch and
> “module-cork-music-on-phone” can handle music interrupted by a incoming
> call J

Yeah, this kind of support is already planned, so it's certainly not
beyond the realms of possibility!  The policy based configuration goes
beyond bluetooth, for example, if we play 5.1 input, perhaps we should
change to a 5.1 profile (this is a rather basic example, but when UCM
support comes in, this kind of thing will be somewhat commonplace)

> My current solution is to monitor application change in
> “module-bluetooth-discover”: when a phone’s input stream is put and
> current profile is not HSP, the discover module will unload
> “module-bluetooth-device” and reload it to HSP profile. I wonder if the
> bluetooth device module can handle this by itself so unloading and
> reloading can be saved.

Yes, you can just change the profile (pa_card_set_profile()). The sink
itself should ultimately disappear and reappear which may fire other
hooks etc, but hopefully that won't get in your way.

I'm not 100% sure where this code should go however... I'm not convinced
it should be in module-bluetooth-device....

How about this:

Define a new card property similar in concept to
PA_PROP_DEVICE_INTENDED_ROLES ("device.intended_roles") called
PA_PROP_DEVICE_INTENDED_PROFILE ("device.intended_profile").

In bluetooth device, when loading a headset that has both HSP and A2DP,
populate the device with the following code:

if (supports_hsp && supports_a2dp)
  char *k;

  k = pa_sprintf_malloc("%s.%s", PA_PROP_DEVICE_INTENDED_PROFILE, "music");
  pa_proplist_sets(card_data.proplist, k, "a2dp");

  k = pa_sprintf_malloc("%s.%s", PA_PROP_DEVICE_INTENDED_PROFILE, "video");
  pa_proplist_sets(card_data.proplist, k, "a2dp");

  k = pa_sprintf_malloc("%s.%s", PA_PROP_DEVICE_INTENDED_PROFILE, "phone");
  pa_proplist_sets(card_data.proplist, k, "hsp");

This should result in "pacmd list-cards" dumping a proplist that includes:

 device.intended_profile.music = a2dp
 device.intended_profile.video = a2dp
 device.intended_profile.phone = hsp

Then a separate module (module-intended-profiles?) could take care of
listening quite generically for streams landing on sink and do the
following logic:

if (!si->sink || !si->sink->card)
	return PA_HOOK_OK;

char *role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE);
if (!role)
	return PA_HOOK_OK;

char *k = pa_sprintf_malloc("%s.%s", PA_PROP_DEVICE_INTENDED_PROFILE, role);
pa_card *card = si->sink->card;
char *profile = pa_proplist_gets(card->proplist, k);
if (!profile)
	return PA_HOOK_OK;

if (!pa_streq(card->active_profile, profile)) {
   /* Save the current profile for later restoration */

   if (pa_card_set_profile(card, profile, FALSE) < 0)
   /* Find the sink of this card as the above call will likely have
changed it */
   sink = pa_idxset_first(card->sinks, NULL);

   pa_sink_input_move_to(si, sink, FALSE);

This code is then completely generic. I'm not 100% sure how well this
will work in practice as there may be some nasty races etc. but I think
this general approach could work... Perhaps others (i.e. Tanu :D) have
better suggestions?

> I have another question, I learned that the internal concept of a single
> "default" device can be replaced by priority lists of devices in future
> routing (from Colin’s blog:
> http://colin.guthr.ie/2010/02/this-is-the-route-to-hell/)
> Can the logic of “module-switch-on-connect” conflict with the new
> routing design?

Yes and in fact I've stated as such on
http://pulseaudio.org/ticket/706#comment:11 although I will obviously
ensure that any functionality it achieves is preserved when implementing
more advanced routing options :)

> If yes, this means a latest connected Bluetooth headset may not be the
> default device for a phone call and changing the profile is not needed.
> How can the Bluetooth device module know whether it’s the default device
> for an application when this application’s input stream is PUT?

Good question! I'm not 100% sure, but I think the approach I outlined
above (caveats with races not withstanding) could be the short term

We were recently talking about routing to sinks+ports in a discussion on
this ML (jack detection related from a week or so back).

Ports are sink-level config options and in many ways it may make sense
to change from "card profiles" to "sink ports" for the HSP<->A2DP change
(although the fact that the sample spec etc. may change may make this a
non-option - I don't know enough about BT to say at this stage).

We decided that we probably wanted the routing to work in such a way
that entries in the priority list would be keyed by the sink+port. This
would allow us to use "is jack plugged in" in our routing decisions.

e.g. a user may want to use in order of priority
 1) his headphones plugged into internal jack
 2) USB speakers
 3) Internal speakers.

Essentially 1 & 3 are the same sink but with different "ports" active.

In this case, if it were possible to put the HSP vs A2DP choice into
sink ports, then the following may be present in the "phone" priority list:
 1. BT+HSP
 2. BT+A2DP
 3. Speakers

If no bluetooth device is present, it'll use the speakers for a "phone"
stream. Then you turn you bt headset on and (for arguments sake) it
defaults to A2DP. The fact the device has shown up triggers a "reroute"
where the routing priority lists are applied to running streams.

Thus the phone call is moved to the BT+A2DP sink.

Now, the pseudo module I outlined above kicks in (but rather than
module-intended-profiles which acts on a card proplist metadata to
change a card profile, it becomes module-intended-ports and acts on the
sink proplist metadata to change a sink port). This module sees a phone
stream on the sink that an "intended port" for phone streams and thus it
changes the port for us. This cases BT+A2DP to disappear but BT+HSP to
appear. Again a reroute is triggered and now the stream is moved to the
BT+HSP sink.

So with this approach everything works nicely.... the question is, is it

In the short term, I think the card profile changing module is the way
to go!




Colin Guthrie

Day Job:
  Tribalogic Limited [http://www.tribalogic.net/]
Open Source:
  Mageia Contributor [http://www.mageia.org/]
  PulseAudio Hacker [http://www.pulseaudio.org/]
  Trac Hacker [http://trac.edgewall.org/]

More information about the pulseaudio-discuss mailing list