Description
This page contains audio service usage examples. The API description is available at audio service.
Note that there are a few approaches for a2dp (alsa, gstreamer, pulse?), maybe two for sco (alsa, pulse?). Alsa is the simplest and should support the greater variety of audio clients directly.
Basic steps
(these are valid for bluez-utils-3.16 or newer)
Note: you will not need bluetooth-alsa, plugz, btsco or the like for audio to work. They are obsolete.
- Make sure the audio service is installed
- modify your ~/.asoundrc to contain
pcm.bluetooth { type bluetooth device 00:11:22:33:44:55 }where 00:11:22:33:44:55 is the Bluetooth address of your headset (use for example hcitool scan to find it) - configure your audio application to use the alsa device "bluetooth". See Supported Players for some examples.
- start playing :)
Tips
- There's a tool called dbus-viewer (apt-get install dbus-viewer; dbus-viewer --system) which is very helpful in using D-Bus interfaces as long as there are no convenient scripts or GUIs available for them. A newer similar tool for GTK+ is called D-Feet. In kubuntu try qdbusviewer from the qt4-dev-tools package.
Known issues
- You may get choppiness with a2dp. An hcid.conf with "lm accept,master;" and "lp hold,sniff,park;" will be more robust. For BlueZ 4.x, which has no hcid.conf, you will have to do something like 'hciconfig hci0 lm master; hciconfig hci0 lp hold,sniff,park' after bluetoothd starts up (or write a patch ;).
- If you get messages in the kernel log like "sco packet for unknown connection" then you may need to apply a kernel patch (several versions of the fix are circulating around bluez-dev but no one solution seems right)
- xmms with broadcom based headsets produces choppy sound at times (actually xmms in general seems to have a little bit buggy alsa usage). We recommend using some other player, e.g. audacious.
- e.g. the Logitech freepulse and Etymotic Research ety8 headphones are broadcom based
- vlc pause/unpause doesn't work (no sound after unpause)
- Skype at version 2.0.0.72 may not work out of the box with your headset. It seems like this may be due to Skype not handling the sampling rate your headset is fixed at. A workaround is to create a plug device so resampling can be done transparently. You can do this in your .asoundrc file, for example:
pcm.bluetoothraw {
type bluetooth
device 00:11:22:33:44:55
}
pcm.bluetooth {
type plug
slave {
pcm bluetoothraw
}
}
Alsa Plugin
Configuration
Append this lines to ~/.asoundrc:
pcm.bluetooth {
type bluetooth
device "XX:XX:XX:XX:XX:XX" #optional, connects to specific device instead the default one
profile "auto" #optional, supported profiles are: auto, hifi and voice
}
See also the workaround in the previous section.
Supported Players
aplay and arecord
arecord -D bluetooth -f S16_LE | aplay -D bluetooth -f S16_LE
gst-launch
gst-launch -v alsasrc device=bluetooth ! audioconvert ! audioresample ! alsasink device=bluetooth sync=false
audacious
options->preferences->Audio->Current Output Plugin->ALSA Output Plugin Preferences->Device Settings->audio device: "bluetooth"
if you can't see the audio device form there, you can force the bluetooth output by modifying your audacious property file
~/.config/audacious/config. Modify the "pcm_device" value by "pcm_device=bluetooth"
xmms
xmms is known to have some issues, please use its successor audacious instead
options->preferences->Output Plugin->Configure: "bluetooth"
beep-media-player
preferences->Plugins->Output->Select ALSA->Preferences->Audio device: "bluetooth"->Advanced->disable mmap mode
mplayer
mplayer -ao alsa:device=bluetooth
play
play --device=bluetooth file.mp3
amarok
Amarok configuration -> Engine -> Select: "alsa" then replace "default" with "bluetooth" in either "mono" or "stereo" field
banshee, rhythmbox and totem
to route the sound to bluetooth:
gconftool -t string -s /system/gstreamer/0.10/default/musicaudiosink "alsasink device=bluetooth"
to route the sound to normal speakers:
gconftool -t string -s /system/gstreamer/0.10/default/musicaudiosink "autoaudiosink"
Kaffeine
Menu Settings -> xine Engine Parameters -> audio -> Expert Options tab Change "device.alsa_default_device" (mono) and "device.alsa_front_device" (stereo) from "default" to "bluetooth"
GStreamer Plugin
Currently only available for a2dp.
Configuration
It is not necessary to create a virtual device as in alsa, configuration can be change via element properties:
a2dpsink
Element Properties:
name : The name of the object
flags: readable, writable
String. Default: null Current: "a2dpsink0"
preroll-queue-len : Number of buffers to queue during preroll
flags: readable, writable
Unsigned Integer. Range: 0 - 4294967295 Default: 0 Current: 0
sync : Sync on the clock
flags: readable, writable
Boolean. Default: true Current: true
max-lateness : Maximum number of nanoseconds that a buffer can be late before it is dropped (-1 unlimited)
flags: readable, writable
Integer64. Range: -1 - 9223372036854775807 Default: -1 Current: -1
qos : Generate Quality-of-Service events upstream
flags: readable, writable
Boolean. Default: false Current: false
device : Bluetooth remote device address
flags: readable, writable
String. Default: null Current: null
sbcenc
Element Properties:
name : The name of the object
flags: readable, writable
String. Default: null Current: "sbcenc0"
mode : Encoding mode
flags: readable, writable
Enum "GstSbcMode" Current: 0, "auto"
(0): auto - Auto
(1): mono - Mono
(2): dual - Dual Channel
(3): stereo - Stereo
(4): joint - Joint Stereo
allocation : Allocation mode
flags: readable, writable
Enum "GstSbcAllocation" Current: 0, "auto"
(0): auto - Auto
(1): loudness - Loudness
(2): snr - SNR
blocks : Blocks
flags: readable, writable
Integer. Range: 0 - 2147483647 Default: 0 Current: 0
subbands : Sub Bands
flags: readable, writable
Integer. Range: 0 - 2147483647 Default: 0 Current: 0
Supported Players
gst-launch
To transmit with SBC
gst-launch -v ... ! decodebin ! audioconvert ! audioresample ! sbcenc ! a2dpsink device=XX:XX:XX:XX:XX:XX
To send mp3 data directly
gst-launch filesrc location=<some mp3 file> ! mp3parse ! a2dpsink device=XX:XX:XX:XX:XX:XX
Sending mp3 data directly is preferable on low powered systems, such as embedded devices, rather than decoding from mp3 and re-encoding as sbc. (To diagnose problems, try passing --gst-debug a2dpsink:5,avdtpsink:5 to gst-launch. Sometimes the mp3parse element from the gst-plugins-ugly package is incompatible with bluez due to the way it reports channels. Different versions and/or manual patching may be required)
banshee, rhythmbox and totem
to route the sound to bluetooth:
gconftool -t string -s /system/gstreamer/0.10/default/musicaudiosink "sbcenc ! a2dpsink device=XX:XX:XX:XX:XX:XX"
to route the sound to normal speakers:
gconftool -t string -s /system/gstreamer/0.10/default/musicaudiosink "autoaudiosink"
Pulseaudio
Pulse using ALSA plugin
Older versions of pulse without the native bluetooth module can use the alsa plugin:
Make audio service autostart. (Autostart=true in /etc/bluetooth/audio.service) (not required for bluez 4.x)
.asoundrc:
pcm.headset {
type bluetooth
device "XX:XX:XX:XX:XX:XX"
profile "voice"
}
Manually run:
pactl load-module module-alsa-sink device=headset pactl load-module module-alsa-source device=headset
Then use pavucontrol to make the headset sink/source the default
Pulse native Bluetooth sink
Newer versions of pulse have a native Bluetooth sink which works *only* with Bluez 4.x. You need pulse 0.9.14 (FIXME: maybe 0.9.13 works too?) and later, and they have to have module-bluetooth-discover and module-bluetooth-device compiled, and they have to match the version of BlueZ you are running.
That last point is so important it's worth repeating: You *must* have matching bluez and pulseaudio versions, because the API for communicating between pulse and bluez is still under active development. If you fail to do this, everything will seem to work up to the point of making "beep" sounds as your headset is connected...except there will be error messages instead of audio.
Here's a compatibility table to get you started:
bluez 4.0-26: FIXME: not sure when pulse/bluez starts working...0.9.13? 4.18? bluez 4.27-33: pulse 0.9.14 bluez 4.34-38: pulse 0.9.15 bluez 4.39+: FIXME: if these versions are released, please test and update this page! Thanks in advance!
You'll also need a non-ancient kernel (2.6.24 works, and it's older than anything the BlueZ developers are willing to support), the kernel needs to be built with Bluetooth support, you'll need a compatible bluetooth adapter, and you need to pair your headset first...which you'll need for the ALSA and gstreamer plugins as well.
Also, it is a good idea to make sure pulse is working properly with a more conventional sound device on your setup, and strip down your Pulse configuration to the bare minimum set of modules that you actually use before attempting to use it with Bluetooth. Chances are good that if an obscure Pulse module bug causes your pulseaudio daemon to crash with an ordinary sound card, it'll continue to make your pulseaudio daemon crash with a bluetooth audio device.
There are two ways to get the bluetooth sink running in pulse: automatic detection with module-bluetooth-discover, or manually with module-bluetooth-device.
Manually using module-bluetooth-device
The manual method: from pacmd, the pulseaudio -C command line, or from .pulse/default.pa:
# $HOME/.pulse/default.pa # Load the sink module. We use the name "fred" here, but any valid Pulse sink name will work. # Also note we've set the default profile to 'a2dp'. Use 'hsp' for headset (telephone) audio. load-module module-bluetooth-device profile=a2dp sink_name=fred address=XX:XX:XX:XX:XX:XX # Make the module default (you can do this with any other pulse tool as well, e.g. pavucontrol) set-default-sink fred # Set the card profile (in case you left out profile= above - pulse 0.9.15 only) set-card-profile fred a2dp
Some important notes about the manual method:
- It sets up one connection to the headset. That means if the headset isn't powered on, isn't connectable, is connected but to a cell phone or other Bluetooth device, or is out of range when pulse starts, there will be no bluetooth sink in Pulse. Also, if the headset goes out of range, gets turned off, battery dies, or disconnects, the bluetooth sink will disappear from Pulse. You can repeat the load-module command to reconnect the headset.
- Depending on the pulse version and on the firmware in your headset, module-bluetooth-discover may interfere with your connection attempts. You may need to remove (or not load in the first place) module-bluetooth-discover.
Automatically with module-bluetooth-discover
The automatic method: Load module-bluetooth-discover, then connect with gnome-bluetooth or dbus commands.
# $HOME/.pulse/default.pa # Load module-bluetooth-discover load-module module-bluetooth-discover # Note we do NOT have to load-module module-bluetooth-device here.
Once the pulseaudio daemon is configured and running with module-bluetooth-device, you can (in theory) connect to the headset any way you like: use gnome-bluetooth, press the "connect" button on the headset, etc. Pulseaudio should detect the headset connection and automatically create a pulseaudio sink. You then use pavucontrol or similar to configure it as the default sink and/or move streams to it.
# Use dbus-send from the command line to connect to the headset: $ dbus-send --print-reply --system --dest=org.bluez / org.bluez.Manager.DefaultAdapter method return sender=:1.18141 -> dest=:1.18218 reply_serial=2 object path "/org/bluez/4189/hci0" $ dbus-send --print-reply --system --dest=org.bluez /org/bluez/4189/hci0 org.bluez.Adapter.FindDevice string:00:15:0E:A0:6C:C8 method return sender=:1.18141 -> dest=:1.18221 reply_serial=2 object path "/org/bluez/4189/hci0/dev_00_15_0E_A0_6C_C8" $ dbus-send --print-reply --system --dest=org.bluez /org/bluez/4189/hci0/dev_00_15_0E_A0_6C_C8 org.bluez.AudioSink.Connect method return sender=:1.18141 -> dest=:1.18226 reply_serial=2 # Use pacmd to set the default sink and enable a2dp $ pacmd "set-default-sink bluez_sink.00_15_0E_A0_6C_C8" $ pacmd "set-card-profile bluez_sink.00_15_0E_A0_6C_C8 a2dp"
Depending on what profile you want, the interface name in the last dbus-send command will be different:
org.bluez.AudioSink.Connect - connects via A2DP (stereo audio) org.bluez.Headset.Connect - connects via HSP/HFP (mono headset with mic) org.bluez.Audio.Connect - connects to both (select which to use via the pulse card profile)
Regardless of which method you use, after the Connect call, module-bluetooth-discover in the pulseaudio daemon should automatically load the module-bluetooth-device module. Pulse 0.9.13 and 0.9.14 used a name based on the "friendly name" of the device (which is a string like "TRI_BH102 v2.2"). Pulse 0.9.15 uses a name based on the Bluetooth address such as "bluez_sink.00_15_0E_A0_6C_C8".
Pulse 0.9.15 probably won't work until you set the card profile.
Advanced examples for programatically setting up audio devices
The examples from here to the end of the page are only for programmers who need to automate device creation, service startup, etc. Stop reading now if you're just trying to get the basics working.
Service Activation
Note this section does not apply to bluez 4.x, where the audio service is a plugin that runs in bluetoothd.
When a client wants to start the audio service or discover the audio service bus "id", the ActivateService method should be called.
import dbus bus = dbus.SystemBus() bmgr = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Manager') bus_id = bmgr.ActivateService('audio') # All serial service messages should be sent to this address print bus_id
Service activation from command line:
$dbus-send --system --type=method_call --print-reply --dest=org.bluez \ /org/bluez org.bluez.Manager.ActivateService string:audio
A2DP
Note that much of this section does not apply to bluez 4.x, which uses org.bluez.AudioSink?.Connect.
A2DP (Advanced Audio Distribution Profile) is intended for high quality stereo audio applications.
For A2DP there is no need to specifically request a "playing" state. The bluetooth alsa plugin will automatically do this when some application tries to use it. If no device is connected, the default device will be connected to, otherwise the first found connected device will be used.
python
import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Manager') bus_id = manager.ActivateService('audio') audio = dbus.Interface(bus.get_object(bus_id, '/org/bluez/audio'), 'org.bluez.audio.Manager') path = audio.CreateDevice('00:11:22:33:44:55') #audio.ChangeDefaultDevice(path) #change the device to be used by default sink = dbus.Interface (bus.get_object(bus_id, path), 'org.bluez.audio.Sink') sink.Connect()
Command line
The ":X.Y" in the following examples is the unique bus name of the audio service. You get it in the reply to the ActivateService method call:
$dbus-send --system --print-reply --dest=org.bluez /org/bluez org.bluez.Manager.ActivateService string:audio
* CreateDevice:
$dbus-send --system --type=method_call --print-reply --dest=":X.Y" \ /org/bluez/audio org.bluez.audio.Manager.CreateDevice string:00:11:22:33:44:55
* Connect (replace device_object_path with return value from last call):
$dbus-send --system --type=method_call --print-reply --dest=":X.Y" \ "device_object_path" org.bluez.audio.Sink.Connect
Headset Profile
Note this section does not apply to bluez 4.x, which uses org.bluez.Headset.Connect.
The Headset Profile is intended for two-way low-latency audio applications such as VoIP.
python
import dbus bus = dbus.SystemBus() manager = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Manager') bus_id = manager.ActivateService('audio') audio = dbus.Interface(bus.get_object(bus_id, '/org/bluez/audio'), 'org.bluez.audio.Manager') path = audio.CreateHeadset('00:11:22:33:44:55') #audio.ChangeDefaultHeadset(path) #change the device to be used by default headset = dbus.Interface (bus.get_object(bus_id, path), 'org.bluez.audio.Headset') #Connect and Play are not required in PCM mode headset.Connect() headset.Play()
Command line
* CreateHeadset:
$dbus-send --system --type=method_call --print-reply --dest=":X.Y" \ /org/bluez/audio org.bluez.audio.Manager.CreateHeadset string:00:11:22:33:44:55
* Connect:
$dbus-send --system --type=method_call --print-reply --dest=":X.Y" \ "device_object_path" org.bluez.audio.Headset.Connect
* Play:
$dbus-send --system --type=method_call --print-reply --dest=":X.Y" \ "device_object_path" org.bluez.audio.Headset.Play
