Bluez must be one of the best kept secrets in Linux
PrologueYou don't believe me? Quick - tell me, what does Socket option mean in /etc/bluetooth/audio.conf? Why does it have to be enabled for audio streaming (from computer to bluetooth speakers/headsts) to work? Along the way, what are other options (other than this "Socket") available, and what do they mean?
Not in your local manpages? Here, let me google it for you: http://bit.ly/11q1jdq
The first link (at least in my browser here, everyone knows Google does habit-tracking...) will bring you to the ArchLinux wiki. Arch Wiki is usually very explanatory, but for this case, if you scroll down and read the details about this "socket" stuff - well, it simply refers back to its source, Gentoo Wiki. Let me save your from googling http://wiki.gentoo.org/wiki/Bluetooth_Headset. Yup, no explanation there either.
And from there, it goes down the rabbit hole. And down there I went.
Down the rabbit hole
The latest release of Fatdog64 comes with partial bluetooth support using bluez 4.101 (the latest and probably final release of bluez 4.x series, now that is has been superseded by bluez 5.x). Bluez 4.x series comes with an ALSA PCM plugin. This plugin makes it possible for the computer to stream audio to bluetooth speakers (and headsets) - provided, of course, one has the correct settings in audio.conf with that Enable=Socket line inside (yes, Fatdog64 is pre-configured with this). All that is required after that is to setup the correct asoundrc file, and in Fatdog64 this is taken care of by the Set Default Soundcard applet in Fatdog64's control panel. In short, with this you can listen to youtube using your bluetooth speakers.
In technical terms, the bluez ALSA PCM plugin is a Source - source of the audio stream, to be sent over to the bluetooth speakers. The speakers in this case is called as the Sink - where the audio stream is received.
But there is no plugin for the reverse.
That is, how to to listen to the music in your phone from your computer? (the computer may be connected to a stereo set). That is, how to make the computer as the Sink with the phone as the "Source"?
Back to google. And google I did, nothing I found. All the references I can find when I googled for "bluetooth streaming audio to my computer" always refer to how to do it with PulseAudio (load pulseaudio module this and module that, connect them, and magic happens). Very helpful and very handy except that it is useless for me because I don't run PulseAudio and I'm not about to. (I don't have to run PulseAudio or any other audio server when I plug-in a USB audio soundcard - so why should bluetooth?!)
Remember what I said at the beginning? "Bluez must be one of the best kept secrets in Linux."
Bluez source tarball comes with a /doc directory that contains a handful of text files containing extremely concise DBus "API" descriptions. Now, I am not allergic to documentation in text format (in fact I'm very annoyed with packages whose documentation need to be "made" or "built" first before it can be read - especially if it requires huge or esoteric tools to do so - but that's another story); but did I just say "handful" and "extremely concise"? You can have a taste of this "documentation" here. No howtos. No tutorials. Not on the source tarball, not on bluez.org, not anywhere. In fact, later on I learnt that some of these "API" are not APIs at all - some of them are "function prototypes" ("DBus interfaces") for callbacks which our own application has to provide and will be called by bluez! (instead of us calling into bluez). (Note: There is actually a rather nice bluetooth/bluez tutorial here, but it focuses on data communication aspect of bluez using sockets and RFCOMM; nothing to with bluez DBus API and audio/media. Again, there is no documentation from bluez for these subjects too!).
Sure, hotshot - someone might say - why don't I just read pcm_bluetooth.c (the ALSA PCM plugin) from bluez package, understand the interface, and reverse it to create the "Sink" interface? I will have to admit, I'm not that well versed with ALSA plugin interfaces which is absolutely required if you want to separate the ALSA stuff from bluez stuff in pcm_bluetooth.c (I know ALSA good enough to output sound using its API, but that's it). And my original idea is once I have the "Sink" interface, I will extract the audio stream in a known format and send it to stdout where I can pipe it over to "aplay" or "ffmpeg" or "sox" or whatever to actually output the soud; this way I don't have to mess with ALSA libs at all.
So I didn't follow that path, and later on it turned out to be a good move for a reason even more obscure: The ALSA PCM plugin talks to bluez using what is called as the "audio socket API" (this, by the way, is the answer to the first question on the opening paragraph of this post) and it is already deprecated for sometime (which is why it isn't enabled by default).
OK. Lacking this first-hand information from bluez, I spent hours googling to find explanations or at least overview of what this bluez DBus audio/media API is all about.
Among hundreds of results from Google, I could only find one place that explains it, here. It is simply the simplest, clearest overview of bluez usage from userspace perspective. It was written in the context of Android but it easily applies to other situations too. Buried deep in that post, is a link to a PDF document which highlights bluez DBus API overview, http://download.tizen.org/misc/media/conference2012/wednesday/bayview/2012-05-09-0900-0940-bluez-_plugging_the_unpluggable.pdf.
One wonders why this very important document is hidden deep inside Tizen's website maze. Only after one realises that bluez is mainly driven by Intel (and Nokia in the past, when they were still supporting the Maemo platform), you will see the connection: Tizen is a joint Intel-Nokia initiative too (being the rightful descendant of Maemo/Meego/Moblin). There is nothing wrong with that, in fact I'm glad that they take the initiative to sponsor the development of this very important protocol stack in Linux kernel. My only question is this: why oh why isn't this information available from bluez.org instead; is that too much to ask?
Anyway. That PDF got me started, but that information is - as mathematicians call it - necessary but not sufficient.
One need to read this post (I need to enable the Source interface in audio.conf), this post (when I can request bluez for the file descriptor for actual reading/writing of audio data) and then lastly this post (I need to disable the Socket interface otherwise the bluez Media DBus API won't work!) to make it work.
Once done this enabled me to dump the audio data to stdout.
Nice! But how to actually listen to that?
As it turns out, the audio data is compressed with SBC codec. But I can't just use "sbcdec" tool from SBC package to decode it, as the audio data is encapsulated in A2DP packets, not naked SBC-compressed audio data. A2DP packets are RTP packets (referenced by A2DP specification, and detailed in this IETF draft) containing A2DP Media Payload. We need to extract the SBC audio data, pass it through SBC decompressor, and only then we get raw audio data that can be sent to ALSA.
I took a shortcut - I already know of a good implementation that can do this decoding: PulseAudio. So I cracked open PulseAudio source tarball. Fortunately the encoding/ decoding functions are obvious enough even for one who knows zilch about PulseAudio internals; I took the decoding function out and merge it to my code.
Minor details such as how to tell my phone to connect its audio source to my computer was found rather quickly by trial and error, like this:
dbus-send --system --dest=org.bluez /org/bluez/[bluetoothd-pid]/hci0/dev_XX_XX_XX_XX_XX_XX org.bluez.AudioSource.Connect
(it is one long line, not two lines).
And the first sound that came from my phone aptly came from a song called "Painting Raindows" :)
Notes: Bluez sinks and sources
Note to self: bluez' treatment of the terms "Source" and "Sink" are not consistent. Let me explain:
Bluez audio has two interfaces, like these:
"bluetooth device <--> (a) bluez (b) <--> application"
For example, in our case, viewed from (a), the interface is called as a "sink" because bluez receives data from external bluetooth device (the phone). Once processed, bluez will pass the data to an external application (view (b)); viewed from here bluez acts as a "source" of data to this external application.
The Bluetooth specification always uses viewpoint from (a). Bluez uses both view points, sometimes from (a) and sometimes from (b). That is also the very reason why to enable the computer to act as an audio "sink", we need to add "Source" to audio.conf.
Did I say that bluez must be one of the best kept secrets in Linux?
One more thing, honey ...
Once I got the audio sink working, I was about to wrap up and close my little experiment when I realised something.
Do you remember what I said about enabling Socket in audio.conf to enable ALSA PCM plugin, so that one can stream audio from the computer to the external bluetooth speaker? Good. Do you remember what I said about disabling Socket in audio.conf before the bluez Media DBus API can work, so that we can stream audio from phones to the computer? Good.
Not!
Obviously it means I can't do both at the same time!
Well, the solution, according to the bluez team, is simply to drop the Socket API (and the associated built-in ALSA PCM plugin) altogether because it is already deprecated and will be removed soon anyway! In another words, I will now also need to write the an A2DP "Source" interface. This time around, google didn't help me at all, but fortunately the bluez team did the correct thing and followed the same exact event flow with the "Sink" interface, as tests and trials confirmed. So I just need to modify the code a little to listen to different events (AudioSink instead of AudioSource). As before, the A2DP packet preparation and SBC compression is taken from PulseAudio.
To the future and beyond ...
And the happy ending is, all this work will be at least partially wasted in bluez 5. Firstly, bluez 5 removes the Audio Socket API (which also means the built-in ALSA PCM plugin is gone for good). Secondly, bluez has big API changes. Apps built for bluez 4 *will not work* without re-factoring and re-testing. Sure, using the DBus API instead of the socket API is a smart move, but still not good enough for bluez 5.
I am not the only one. It is telling that even PulseAudio team themselves have not released a version that supports bluez 5, six months after the first version of bluez 5 was released. Mind you, PulseAudio team works closely with bluez team; they got first hand knowledge of the impending changes and has already started to patch PulseAudio to accomodate bluez 5 API well before bluez 5 was officially released....
Apparently, despite being the official Linux bluetooth stack since 2001, bluez userspace API is not stable yet...
As for Fatdog64, I think I will stick with 4.101 for a while.
The code
I will follow this up with another post that documents all the findings in more details, as well as the corresponding source tarball.
The Sink works relatively well, the Source, while usable, isn't a comparable replacement for the ALSA PCM plugin. It sort of works; you can pipe raw sound data to it and you will hear it on your bluetooth speaker. Combined with ALSA PCM File plugin, it can be used as a poor man's ALSA PCM output plugin, but it doesn't always work (ffmpeg works, VLC stutters, youtube simply crashes). It also suffers from an odd problem of stuttering when I connect both the sink and source at the same time (the sink never stutters).
The code is meant as a proof-of-concept, as a learning tool, and as a working example of the bluez Audio/Media interfaces. It focuses on neither performance nor robustness. If I have time and inclination I may do one that do it properly - as a native ALSA PCM plugin.
Epilogue
I have not talked about DBus, bluez' IPC of choice. One can't avoid DBus at all because bluez API is exposed as DBus method calls (bluez has other kind of APIs - these are totally undocumented). Some jokingly said that one of the reason why DBus is so popular in embedded devices is because if people want to get bluetooth functionality using bluez, they'd better bring DBus in too ...
I'd talk about DBus, "whose reference library is not meant to be used", in another occasion.
As a parting note, here is the link to a suite of bluetooth userspace tools much better than the one you can find in bluez package itself. I wish this tool is more popular, I wish I had know this tool when I started to experiment with Fatdog64's bluetooth support. Isn't it telling that most of wikis and solution websites advocate the usage of "simple-agent" python script when one needs to do device pairing on command line - when this python script actually lives in "/test" directory inside bluez source tarball? Hmmmm.
Bluez must be one of the best kept secrets in Linux.
Edit - Delete
Comments:
Posted on 30 Oct 2013, 4:30 by Raybo"So, did you ever find?"
I appreciate your article. I'm not alone in my frustration.
Any luck finding documentation on audio.conf settings? I've been struggling with connecting to a logitech BT adapter for my stereo.
What I really want find is a guide that will list the options and explain what each option is for.
Delete
Posted on 1 Nov 2013, 15:42 by jamesbond
"RE: So, did you ever find?"
There isn't any. The only "documentation" of sort you can find is the comments in the audio.conf itself.
If this isn't clear enough (and it's not! - unless one happens to be a bluetooth protocol expert), you will have to read the code in the "audio" directory of bluez source tarball ...
Delete
Posted on 6 Dec 2013, 3:12 by ChrisA
"Bluez-Tools Coming to 5.0"
Note that bluez-tools doesn't work with the Bluez 5.0 API. However, it appears the team is going to fork the project to have 4.x and 5.x flavors:
https://code.google.com/p/bluez-tools/issues/detail?id=13
Delete
Posted on 7 Dec 2013, 5:20 by jamesbond
"RE: Bluez-Tools Coming to 5.0"
Thanks for the info. I'm not surprised since Bluez 5.0 API isn't backward compatible with 4.0 API; and it breaks things in many little ways; it isn't straightforward to maintain a codebase that works on both.
Delete
Posted on 9 Jan 2014, 1:13 by Matt
"alternative to AudioSource.Connect, maybe"
Thanks for this post. It kind of goes along what I found out in Ubuntu as well; the audio stuff isn't quite as straightforward as one might expect, though possibly because it's not meant to be interfaced with directly from the command line but rather by non-existant userspace, maybe GUI tools.
I wrote a blog post about this stuff, and specifically streaming A2DP from the phone to a Linux computer some time ago, after we had enabled the "right" Source/Sink/Gateway profiles:
http://blog.cyphermox.net/2012/03/call-for-testing-bluez-a2dp-and-hsphfp.html. It depends on having the loopback and bluetooth modules in pulseaudio, and IIRC used the Socket API... I don't think we could use the MediaEndpoint DBus magic at that point.
Anyway, great post. It's always good to have more information on Bluetooth on Linux. I really look forward to seeing if BlueZ 5.0 helps too.
Delete
Posted on 22 Mar 2014, 19:38 by Alessandro
"Bluez sink and Pulseaudio"
Thank you very much for your post, it helped a lot in my fatigues for having a bluetooth sink in my headless debian stereo.
I didn't succeed yet and I'm very stuck; I hope I may ask some more help, even an hint on what to try next.
I got to the point pulseaudio log shows the following line
org.bluez.Audio property 'State' changed
to value 'connecting'
but org.bluez.Audio never goes to connected (org.bluez.AudioSource and Sink are connected tho).
I asked for some help on the bluez newsletter, you may find further info and the log on their gmane
http://permalink.gmane.org/gmane.linux.bluez.kernel/46055
As you will easily understand I'm a real n00b, I'm surprised I even got to this point :) I'd appreciate any possible help
Thanks and cheers
Delete
Posted on 24 Mar 2014, 16:40 by jamesbond
"Re: Bluez sink and Pulseaudio"
Sorry Alessandro, I'm not the right person to ask about Pulseaudio. The entire point of this blog post is how to run use bluetooth audio without pulseaudio. If you don't get any response on bluez mailing list, you can try pulseaudio mailing list or the mailing list of the distro you're using.
That being said, I looked at your log file and everything seemed to work fine until the very last step of bluez handover (=creation of unix socket); but for one reason or another the "protocol-native" (from pulseaudio) lost the connection (=connection died). I guess that's where the investigation should start.
cheers!
Delete
Posted on 25 Mar 2014, 6:49 by Alessandro
"Re: Re: Bluez sink and Pulseaudio"
Once again, thank you for your help and patience (and excuse me for the off topic post). I'll try to investigate what you pointed out.
I succeeded to connect my Android phone, which is happily streaming audio now, by putting the line
Enable=Source,Sink,Media,Headset
in /etc/bluetooth/audio.conf. Nothing less, nothing more. It is probably some obvious stuff for you, still this is my experience maybe useful for other linux fans :) (once done I'll try to write a tutorial for dummies...)
cheers
Delete
Posted on 23 Nov 2014, 2:26 by Richard Melville
"Mr"
Good article, but I think you'll find that Tizen is an Intel/Samsung partnership since Nokia became (to all intents and purposes) Microsoft.
Delete
Posted on 25 Feb 2015, 18:17 by nayi
"Bluez sink: Audio plays fast"
This is a very good article. It is very much useful as we dont find this information elsewhere.
I followed the this link "http://www.lightofdawn.org/wiki/wiki.cgi/BluezA2DP", implemented using gdbus bindings of dbus and I was successful in streaming audio from phone to i386 laptop running ubuntu, bluez-4.98. So far so good.
Anyways, I am unsuccessful when laptop is replaced with cubietruck running lubuntu. I can stream the audio but the audio doesn't play evenly. It pauses for a while and plays in fast forward. In this case Bluez version is 4.99-2.
Can you please help me in this regard.
Delete