33

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

首先,我如何通过 RFCOMM 在我的 Android 手机上接收比特流?是否可以通过 RFCOMM 接收比特流作为 PCM 或 Mp3 流?

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

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

需要明确的是,我对使用 A2DP 配置文件不感兴趣!我想通过 RFCOMM(SPP 蓝牙配置文件)流式传输数据。接收到的数据流将是 PCM 或 Mp3。我想编写自己的应用程序,但如果有人知道可以解决此问题的应用程序,我会很高兴听到它!我正在使用 Android 2.3 姜饼。

/约翰尼

4

5 回答 5

43

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

事实是,正如您所提到的,Android 没有实现BlueZ关于功能的 API 调用(Android 使用的蓝牙堆栈直到 Jelly Bean 4.1)A2DP sink。你必须自己实现它们。我会尽力指导你,因为我在不久的过去也有兴趣自己做这件事。

默认情况下,您启用蓝牙的 Android 设备将自己宣传为A2DP source设备。您必须先更改此设置,以便附近的设备可能会将您的设备识别为接收器。为此,您必须修改audio.conf文件(通常位于/etc/bluetooth/中)并确保Enable密钥存在并且值Source已附加到此密钥,因此您将获得如下内容:

Enable=Source

重新启动,附近的设备现在应该将您的设备识别为A2DP sink.

现在,当 A2DP 源设备开始将音频流式传输到您的手机时,您必须与 BlueZ 进行交互以做出适当的反应。

Android 和 BlueZ 正在通过D-BUS. 实际上,Android 连接到 DBUS_SYSTEM 通道并监听每个 BlueZ 广告,例如事件、文件描述符......

我记得我使用本机应用程序成功地将自己绑定到这个 d-bus 通道,并访问了 BlueZ 发布的各种事件。使用 BlueZ API 作为参考,这是相对容易实现的,此处提供了 BlueZ API 。如果你这样做,你将不得不构建一个本地应用程序 (C/C++) 并为你的平台编译它。您必须能够使用Android NDK.

如果你觉得它很难使用D-BUS,你可以试试我刚刚发现的这个 Java 库,它可以为你处理与 D-BUS 的通信:http: //jbluez.sourceforge.net/。我从未使用过它,但我认为值得一试。

您真正需要做的是找出 A2DP 源设备何时与您的手机配对,以及他何时开始流式传输音乐。您可以通过 D-BUS 检索这些事件。一旦有人尝试流式传输音乐,您需要告诉 BlueZ 您的本机应用程序将处理它。有一个很好的文档解释了你应该处理的事件流。该文档可在此处访问。您感兴趣的部分在第 7 页。给定示例中的接收器应用程序是,PulseAudio但它也可能是您的应用程序。

当您调用该org.bluez.MediaTransport.Acquire方法时,BlueZ 将向您转发一个 UNIX 套接字。读取此套接字将为您提供当前由远程设备流式传输的数据。但我记得有一个在 BlueZ 堆栈上工作的人告诉我,在这个套接字上读取的数据不是 PCM 纯音频,而是编码的音频内容。数据通常以称为SBC (低复杂度子带编码)的格式编码。

解码 SBC 并不难,你可以在这里找到解码器。

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

为了防止您卡住并以更简单的方式测试您的应用程序,您可以使用d-bus应该在您的 Android 系统上可用的二进制文件。他位于/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

这将为您提供设备数组字段中的配对设备列表。

获得与蓝牙适配器配对的设备列表后,您就可以知道它是否已连接到 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

希望这可以帮助。

于 2013-03-21T23:11:54.097 回答
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();
于 2014-03-14T00:20:13.490 回答
1

[不相关但有效]这个 hack 仅通过 WIFI 热点提供 mp3 流(我在只有 AUX 输入的汽车中使用它):

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

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

于 2015-04-17T10:29:38.383 回答
0

audio.conf seems to be missing in Android 4.2.2?

于 2013-05-27T03:48:12.747 回答
0

要通过 rfcomm 接收 pcm 音频流,您可以使用代码流作为提示解释(在 C 中读取音频文件并通过蓝牙转发以在 Android 音轨中播放),并进行更改。将初始化时使用的频率从 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 播放。”

将工作。

于 2013-06-28T14:46:23.713 回答