Receive audio via Bluetooth in Android
Asked Answered
S

5

35

I want to create an Android application that is capable of receiving an audio stream. I thought of using the A2DP profile, but is seems as if Android doesn't support A2DP sink. Looks like there are a lot of people that's searching for a solution for this problem. But what about receiving an ordinary bit stream, and then convert the data into audio in the application? I was thinking of receiving an PCM or Mp3 data stream via the RFCOMM (SPP Bluetooth profile), and then play it using AudioTrack.

First, how do I receive a bit stream on my Android phone via the RFCOMM? And is it possible to receive a bit stream via RFCOMM as a PCM or Mp3 stream?

Second, if it isn't possible to receive a bit stream via RFCOMM as a PCM or Mp3 stream, how do I convert the received bit stream into audio?

Third, how do I convert the received data into audio AND play the audio simultaneously, in "real time"? Can I just use onDataReceived?

To be clear, I'm not interested of using the A2DP profile! I want to stream the data via the RFCOMM (SPP Bluetooth profile). The received data stream will be in PCM or Mp3. I thought of writing my own app, but if anyone knows of an app to solve this I'd be glad to hear about it! I'm using Android 2.3 Gingerbread.

/Johnny

Sepsis answered 21/3, 2013 at 20:56 Comment(0)
V
47

No. Trying to write an Android application that handles this will not be the solution. At least if you want to use A2DP Sink role.

The fact is that Android, as you mentioned it, does not implement the API calls to BlueZ (the bluetooth stack Android uses till Jelly Bean 4.1) regarding A2DP sink capabilities. You have to implement them yourself. I will try to guide you, as I was also interested in doing this my self in the near past.

Your bluetooth-enabled Android device is advertising itself as an A2DP source device by default. You have to change this first, so nearby devices may recognize your device as a sink. To do this, you must modify the audio.conf file (usally located in /etc/bluetooth/) and make sure the Enable key exists and the value Source is attached to this key, so you will get something like :

Enable=Source

Reboot, nearby devices should now recognize your device as an A2DP sink.

Now you will have to interact with BlueZ to react appropriately when an A2DP source device will start to stream audio to your phone.

Android and BlueZ are talking to each other via D-BUS. In fact, Android connects to the DBUS_SYSTEM channel and listens to every BlueZ advertisement, such as events, file descriptors ...

I remember having successfully bound my self using a native application to this d-bus channel and got access to the various events BlueZ was posting. This is relatively easy to achieve using as reference, the BlueZ API available here. If you go this way, you will have to build a native application (C/C++) and compile it for your platform. You must be able to do this using the Android NDK.

If you find it difficult to use D-BUS, you can try this Java library I just found that handles the communication to D-BUS for you : http://jbluez.sourceforge.net/. I have never used it but it is worth a try in my opinion.

What you really have to do is find out when an A2DP source device is paired to your phone and when he starts to stream music. You can retrieve these events through D-BUS. Once somebody will try to stream music, you need to tell BlueZ that your native application is going to handle it. There is a pretty good document that explains the flow of events that you should handle to do this. This document is accessible here. The part you're interested in comes on page 7. The sink application in the given example is PulseAudio but it could be your application as well.

BlueZ will forward you a UNIX socket when you will call the org.bluez.MediaTransport.Acquire method. Reading on this socket will give you the data that are currently streamed by the remote device. But I remember having been told by a guy working on the BlueZ stack that the data read on this socket are not PCM pure audio, but encoded audio content instead. The data are generally encoded in a format called SBC (Low Complexity Subband Coding).

Decoding SBC is not very difficult, you can find a decoder right here.

The ultimate step would be to forward the PCM audio to your speakers.

To prevent you from getting stuck and in order to test your application in an easier manner, you can use the d-bus binary that should be available on your Android system. He is located in /system/bin.

Quick tests you can make before doing anything of the above might be :

Get Devices list :

dbus-send --system --dest=org.bluez --print-reply / org.bluez.Manager.GetProperties

This returns an array of adapters with their paths. Once you have these path(s) you can retrieve the list of all the bluetooth devices paired with your adapter(s).

Get paired devices :

dbus-send --system --print-reply --dest=org.bluez /org/bluez/{pid}/hci0 org.bluez.Adapter.GetProperties

This gives you the list of paired devices whithin the Devices array field.

Once you have the list of devices paired to your Bluetooth Adapter, you can know if it is connected to the AudioSource interface.

Get the devices connected to the AudioSource interface :

dbus-send --system --print-reply --dest=org.bluez /org/bluez/{pid}/hci0/dev_XX_XX_XX_XX_XX_XX org.bluez.AudioSource.GetProperties org.bluez.Manager.GetProperties

Hope this helps.

Vasti answered 21/3, 2013 at 23:11 Comment(16)
Awesome! I will try it out soon. But I think it should be possible to ignore the A2DP and just use the RFCOMM. I realize that I'll probably end up with just mono audio, but an audio stream is in fact a bitstream, isn't it? So why not receive it using the RFCOMM and then convert the bitstream into a PCM audio stream in my app?Sepsis
Because A2DP is a standard and is being used by default in every phones (i.e it does not require any application to work, on the client side). If you decide to go using RFCOMM, you'll have, I guess, to provide a client app as well that will stream audio from the remote device. If you decide to do so, you can also use wifi to stream audio through a regular socket. Many applications on the Play Store provides such a feature.Vasti
That being said, if you apply the changes I mentioned above, your sink device will be unique as the changes will not be available on every Android phones (unless you encapsulate in your app another BlueZ stack using JNI, and still, this would be very difficult). But on the other hand, every single device would be able to stream audio to your sink.Vasti
I have trouble finding the audio.conf file in my Android phone! There is a bluetooth folder, but it's empty.Sepsis
Try with a find / -iname "audio.conf" and see if there is a result. Sometimes these files can be located in another folder depending on the manufacturer.Vasti
Nope, there's no such file on my Android phone. Just to be clear, I'm using 2.3 Gingerbread. Do you think that may be a problem?Sepsis
Ah, I guess I have to root my phone?! I'm not sure I want to do that.Sepsis
Being on Gingerbread does not represent a problem. You need the UNIX permissions to modify this file and to list the content of the partition containing /system, yes. Also, the changes you're about to apply must be executed in root, so rooting the phone is required.Vasti
Do you have any idea how to do this without root my phone? You mentioned that ti might be possible to encapsulate a BlueZ stack in my app using JNI. Now how do I do that? Do I also have to make sure my app is using my own stack instead of the existing BT stack?Sepsis
Forget about it, honestly, it is not worth trying as I am not even sure if there is a chance it could work. Why not trying to build an Airplay application or an Audio/Video streaming one over Wifi ?Vasti
I'll stick with my initial idea. But I still wonder how to write an app that receives an audio stream using RFCOMM, and at the same time play the received audio?Sepsis
@HalimQarroum, is it possible to have two concurrent a2dp connection on android JB?Octogenarian
Android only supports one connected Bluetooth A2dp device at a time. (source : developer.android.com/reference/android/bluetooth/…).Vasti
In my phone (Defy+ stock), there is no dbus-send. I managed to chmod 666 /dev/socket/dbus but cannot connect. Before I had permission errors, but now no error, just hangs waiting to connect the socket. Any thought?Culpepper
...and tried to enable TCP on android's dbus. It now bootloops and needs to flash...Culpepper
Dbus-send might not be available on most devices, yes. The idea here is to give general guidelines to what could make you on the road of making this happen by building your own ROM. Regarding the issue with dbus, the client should be working. How did you compile it, and what Android version do you run ?Vasti
A
2

Another work around is using HandsFreeProfile.

in Android, BluetoothHeadset is working on that. Wait until status changed to BluetoothHeadset.STATE_AUDIO_CONNECTED.

then you can record audio from bluetooth headset.

    mMediaRecorder = new MediaRecorder();
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mMediaRecorder.setOutputFile(mFilename);
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    try {
        mMediaRecorder.prepare();
    } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    mMediaRecorder.start();
Agathaagathe answered 14/3, 2014 at 0:20 Comment(2)
Do you have full code to connect and setup Handsfree in Android? ThankFirebrat
@user8264 "half full" - developer.sony.com/develop/wearables/smarteyeglass-sdk/guides/…Sindysine
V
1

[Irrelevant but works] This hack serves only mp3 streaming via WIFI hotspot (I use it in my car which has only AUX input):

  1. Install the app AirSong,
  2. Turn on wifi hotspot,
  3. Connect the other device to that hotspot,
  4. Access 192.168.43.1:8088 from the device's browser and you are on.

(wondering why "192.168.43.1" only? because thats the default gateway of any device connected to Android Hotspot)

Vitus answered 17/4, 2015 at 10:29 Comment(0)
N
0

audio.conf seems to be missing in Android 4.2.2?

Narceine answered 27/5, 2013 at 3:48 Comment(3)
From Jellybean onwards, bluez stack is replaced by google to broadcomm bluetooth stackKellam
In 4.1.2 I had the audio.conf file mention above. After installing AOKP 4.2.2, the audio.config is missing.Narceine
4.2.2 would contain bluetooth stack from Broadcom, audio.conf is a part of bluez stack hereKellam
L
0

To receive pcm audio stream via rfcomm , you can use code flow as a hint explained (Reading Audio file in C and forwarding over bluetooth to play in Android Audio track) , with a change . change freq used while initializing from 44100 to 22050

AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,22050,AudioFormat.CHANNEL_OUT_MONO,AudioFormat.ENCODING_PCM_8BIT,10000, AudioTrack.MODE_STREAM);

note:This streaming still consists some noise but your

"receiving an PCM data stream via the RFCOMM (SPP Bluetooth profile), and then play it using AudioTrack."

will work.

Laurilaurianne answered 28/6, 2013 at 14:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.