Openmoko media player and direct mp3 to bluetooth headphones

Daniel Benoy daniel at benoy.name
Thu Feb 26 15:33:42 CET 2009


It's only possible to do through gstreamer, unfortunately.  ALSA doesn't support it.  (I don't even know if the ALSA API could even be used for that purpose.. I'm sure someone with more knowledge than me can answer that)

I use debian, so I don't know if step by step instructions would be useful to you or most people here.  (I wouldn't really want to come up with them anyway..) so I'll just give general instructions.

Also FYI, I'm using bluez-3.36 .. if someone wants to try it on bluez 4 and tell me if anything is different please let me know what you discover.

Step 1:
Here's how you 'pair' them: (bluez calls it bonding)
http://wiki.bluez.org/wiki/HOWTO/Bonding

The way I did the pairing is by running bluetooth-applet from the bluez-gnome package, and then going ahead with step 2, and it automatically attempted to pair along with a spiffy GUI pin request as soon as it detected that I was trying to make an audio connection while the headset was in pairing mode.

Step 2:
Run the following python code.  (I think I tried to do this from bluetooth-properties from the bluez-gnome package, but I don't think it worked.  You might have better luck)

----
#!/usr/bin/python
import sys
import getopt
import dbus

bus = dbus.SystemBus()

bmgr = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Manager')
bus_id = bmgr.ActivateService('audio')

imgr = dbus.Interface(bus.get_object(bus_id, '/org/bluez/audio'), 'org.bluez.audio.Manager')

optlist, args = getopt.getopt(sys.argv[1:], '', ['list','connect=','disconnect=','help'])

for opt,val in optlist:
        if opt == '--list':
                for path in imgr.ListDevices():
                        idev = dbus.Interface (bus.get_object(bus_id, path), 'org.bluez.audio.Device')
                        print "%s Name: \"%s\" Address: \"%s\"" % (path, idev.GetName(), idev.GetAddress())
        elif opt == '--connect':
                path = ''
                if (val[0] == '/'):
                        path = val
                else:
                        path = imgr.CreateDevice(val)
                idev = dbus.Interface (bus.get_object(bus_id, path), 'org.bluez.audio.Sink')
                idev.Connect()
        elif opt == '--disconnect':
                imgr.RemoveDevice(val)
        else:
                print 'usage: ', sys.argv[0], '[ --list, --connect=[XX:XX:XX:XX:XX, /org/bluez/input/device0], --disconnect=/org/bluez/input/device0, --help ]'
                break
----

Put the headset in pairing mode and do something like ./audtool.py --connect=XX:XX:XX:XX:XX  and you can verify it later with ./audtool.py --list  (If you haven't bonded yet, this is when the UI will ask you for the PIN)

If you want to use AVRCP (remote control headset buttons) you have to set the headset to trusted so it can use the 'input' bluez service.  I'm not really sure how to do this except through the bluetooth-applet API.  That API will simply ask you every time if you want to allow input, and/or establish trust.  If your bluez still uses hidd, I think you're SOL for that entire feature, and/or it will be complicated and manual.

Step Pi*r^2
gstreamer and bluez under debian don't talk to eachother properly.  gstreamer (Specifically the mp3parse element in the mpegaudioparse plugin from the gst-plugins-ugly package) announces its channels as 'joint-stereo' and 'dual-channel' and bluez expects to see either 'joint' or 'dual' .. I've done some bug reporting to gstreamer and debian.. and hopefully one of the upstream maintainers decides to change it so that they're in sync.

I extracted the source of gstreamer and made the change there.

Step 3:
At this point, you should be able to play any gstreamer supported codec to your bluetooth headphones like this:

gst-launch filesrc location=<ogg, mp3, whatever> ! decodebin ! audioconvert ! audioresample ! sbcenc ! a2dpsink device=XX:XX:XX:XX:XX:XX

And you can play mp3s directly with:

gst-launch filesrc location=<some mp3 file> ! mp3parse ! a2dpsink device=XX:XX:XX:XX:XX:XX

In the second case, you may want to also pass --gst-debug a2dpsink:5,avdtpsink:5 in case you experience any problems.

Unfortunately, there is no trivial way to convince a gstreamer application to output mp3s the second way.  The source has to be patched to use a completely different pipeline :(  Also it's up to the software to detect that it's an mp3 file (ostensibly by file extension), and use the different pipeline accordingly.  Which leads me to my problem.  gst-launch doesn't support pausing or any other rudimentary features, so I'm stuck with SBC in the meantime... and I'm hoping that someone out there is making a gstreamer player that I can patch up for this purpose.

I'm currently using rhythmbox, and I'm considering patching that.. but I figure if I'm going to go through all the effort, I might as well do it on a player that is designed for palmtops.

On Wednesday 25 February 2009 21:19:18 you wrote:
> 
> Hi,
> 
> Daniel Benoy wrote:
> > 
> > Apparently bluez supports sending mp3 data directly to bluetooth stereo
> > headphones, rather than using CPU taxing SBC compression encoding.  
> > 
> > (FYI: The way to test direct mp3 is like this: gst-launch filesrc
> > location=<some mp3 file> ! mp3parse ! a2dpsink device=XX:XX:XX:XX:XX:XX 
> > ...
> > 
>   I'm in the process of making a new media player (OK well - 2 formats as of
> now mp3 and ogg - with maybe aac later if I can get a GPL codec) called
> intone. It is essentially aimed at reducing CPU usage by using integer based
> codecs (libmpg123 and tremor). I'm currently playing 44100 hz ogg files at
> about 17-19% CPU usage.
>   Anyhow, your finding is very interesting. If you could post some more
> details - the version of bluez, your audio.conf, hcid.conf etc and a step by
> step method of sending data to the bluetooth headset (including headset
> bonding) that would be helpful.



-- 
Daniel Benoy
http://daniel.benoy.name




More information about the community mailing list