在Android中通过蓝牙接收音频

35

我想创建一个Android应用程序,能够接收音频流。我考虑使用A2DP配置文件,但是似乎Android不支持A2DP sink。看起来有很多人正在寻找解决这个问题的方法。但是,如果接收一个普通的位流,然后在应用程序中将数据转换为音频呢?我考虑通过RFCOMM(SPP蓝牙配置文件)接收PCM或Mp3数据流,然后使用AudioTrack播放它。

首先,如何通过RFCOMM在我的Android手机上接收位流?并且是否可能通过RFCOMM接收PCM或Mp3流的位流?

其次,如果无法通过RFCOMM接收PCM或Mp3流的位流,那么如何将接收到的位流转换为音频?

第三,如何将接收到的数据转换为音频并同时播放音频,以“实时”方式进行?我可以只使用onDataReceived吗?

明确一点,我不感兴趣使用A2DP配置文件!我想通过RFCOMM(SPP蓝牙配置文件)流式传输数据。接收到的数据流将为PCM或Mp3。我考虑编写自己的应用程序,但如果有人知道解决此问题的应用程序,我会很高兴听到!我使用Android 2.3 Gingerbread。

/Johnny


嗨Johnny,到目前为止有没有进展啊?;) - Nippey
嘿!有进展了吗? - dpaksoni
5个回答

47

编号 尝试编写一个处理此问题的 Android 应用程序不是解决方案。至少如果您想使用 A2DP Sink 角色。

事实是,Android,正如您所提到的,没有实现有关 A2DP sink 功能的 BlueZ API 调用(Android 使用 Jelly Bean 4.1 之前的蓝牙堆栈)。您必须自己实现它们。我会尝试指导您,因为我也对在不久的将来自己做这件事感兴趣。

您的启用蓝牙的 Android 设备默认情况下会将自己广告为 A2DP source 设备。您必须首先更改此设置,以便附近的设备可以将您的设备识别为 sink。要执行此操作,您必须修改audio.conf文件(通常位于/etc/bluetooth/),并确保存在 Enable 键,并且将值 Source 附加到此键,以便您将获得以下内容:

Enable=Source

重新启动后,附近的设备现在应该将您的设备识别为 A2DP 接收器。
现在,您需要与 BlueZ 交互,以在 A2DP 源设备开始向您的手机流式传输音频时做出适当反应。
Android 和 BlueZ 通过 D-BUS 进行通信。实际上,Android 连接到 DBUS_SYSTEM 通道并侦听每个 BlueZ 广告,例如事件、文件描述符等等。
我记得曾经成功地使用本机应用程序将自己绑定到这个 d-bus 通道,并获得了许多 BlueZ 发布的事件。使用 BlueZ API 可以相对容易地实现此目标,此处提供了参考。如果您选择这种方法,您需要构建一个本机应用程序(C/C++)并将其编译为您的平台。您可以使用 Android NDK 完成此操作。
如果您发现使用D-BUS很困难,您可以尝试我刚刚发现的这个Java库,它可以为您处理与D-BUS的通信:http://jbluez.sourceforge.net/。我从未使用过它,但在我看来值得一试。
您真正需要做的是找出何时将A2DP源设备配对到您的手机以及何时开始流式传输音乐。您可以通过D-BUS检索这些事件。一旦有人尝试流式传输音乐,您需要告诉BlueZ您的本机应用程序将处理它。有一份非常好的文件解释了您应该处理的事件流程。此文件可在here中访问。您感兴趣的部分位于第7页。在给定示例中,接收器应用程序是PulseAudio,但也可以是您的应用程序。
BlueZ会在调用org.bluez.MediaTransport.Acquire方法时向您转发一个UNIX套接字。在此套接字上读取将提供当前由远程设备流式传输的数据。但我记得有个人在BlueZ堆栈上工作告诉我,在此套接字上读取的数据不是纯PCM音频,而是编码的音频内容。这些数据通常以称为SBC(低复杂度子带编码)的格式进行编码。

解码SBC并不是很困难,您可以在这里找到解码器。

最终步骤是将PCM音频转发到扬声器。

为了防止您陷入困境并以更轻松的方式测试应用程序,您可以使用Android系统上应该可用的d-bus二进制文件。它位于/system/bin中。

在执行以上任何操作之前,您可以进行以下快速测试:

获取设备列表:

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

这将返回带有其路径的适配器数组。一旦您拥有这些路径,就可以检索与您的适配器配对的所有蓝牙设备的列表。
获取已配对设备:
dbus-send --system --print-reply --dest=org.bluez /org/bluez/{pid}/hci0 org.bluez.Adapter.GetProperties
这将在Devices数组字段中给出已配对设备的列表。
一旦您拥有了与蓝牙适配器配对的设备列表,您可以知道它是否连接到AudioSource接口。
获取连接到AudioSource接口的设备:
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
希望这能帮到您。

太棒了!我很快就会尝试它。 但我认为应该可以忽略A2DP,只使用RFCOMM。我意识到最终可能只会得到单声道音频,但事实上,音频流是一个比特流,不是吗?那么,为什么不使用RFCOMM接收它,然后在我的应用程序中将比特流转换为PCM音频流呢? - Johnny
2
因为A2DP是一种标准,并且默认在每个手机上使用(即不需要任何应用程序在客户端上工作)。如果您决定使用RFCOMM,我想您也必须提供一个客户端应用程序,以从远程设备流式传输音频。如果您决定这样做,您还可以使用WiFi通过常规套接字流式传输音频。Play商店上的许多应用程序都提供此功能。 - Halim Qarroum
2
处于Gingerbread系统版本并不是问题。您需要UNIX权限来修改此文件并列出包含“/system”的分区的内容。此外,您即将应用的更改必须在“root”中执行,因此需要对手机进行root处理。 - Halim Qarroum
Android一次只支持一个已连接的蓝牙A2DP设备。(来源:http://developer.android.com/reference/android/bluetooth/BluetoothA2dp.html) - Halim Qarroum
...并尝试在Android的DBus上启用TCP。现在它处于引导循环状态,需要刷机... - alanjds
显示剩余11条评论

2

另一个解决方法是使用HandsFreeProfile。

在Android中,可以使用BluetoothHeadset来实现。 等待状态改变为BluetoothHeadset.STATE_AUDIO_CONNECTED。

那么你就可以从蓝牙耳机录制音频了。

    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();

你有连接并设置Android中Handsfree的完整代码吗? - John
@user8264 "半满" - https://developer.sony.com/develop/wearables/smarteyeglass-sdk/guides/use-bluetooth-for-audio-io/ - Nativ

1

[无关但有效] 这个技巧仅通过WIFI热点提供mp3流媒体(我在我的车上使用它,只有AUX输入):

  1. 安装应用AirSong
  2. 打开wifi热点,
  3. 将其他设备连接到该热点,
  4. 从设备的浏览器访问192.168.43.1: 8088即可。

(想知道为什么只有“192.168.43.1”吗?因为这是连接到Android Hotspot的任何设备的默认网关)


0

如果想通过rfcomm接收pcm音频流,可以使用代码流作为提示(详见在C中读取音频文件并通过蓝牙转发以在Android音轨中播放),并进行更改。在初始化时将freq从44100更改为22050。

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

注:此流仍包含一些噪音,但你

“通过RFCOMM(SPP蓝牙配置文件)接收PCM数据流,然后使用AudioTrack播放它。”

会起作用。


0

在Android 4.2.2中似乎缺少了audio.conf文件?


1
从Jellybean开始,蓝牙协议栈由Google更换为Broadcomm蓝牙协议栈。 - ashish
在4.1.2版本中,我有上述的audio.conf文件。安装AOKP 4.2.2后,audio.config文件不见了。 - WoodsLink
4.2.2 将包含 Broadcom 的蓝牙栈,audio.conf 是 bluez 栈的一部分。点击此处 - ashish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接