在 Android 中通过蓝牙接收音频

Posted

技术标签:

【中文标题】在 Android 中通过蓝牙接收音频【英文标题】:Receive audio via Bluetooth in Android 【发布时间】:2013-03-11 13:51:06 【问题描述】:

我想创建一个能够接收音频流的 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 Gingerbread。

/强尼

【问题讨论】:

嗨,约翰尼,到目前为止有什么进展吗? ;) 嘿!有什么进展吗? 【参考方案1】:

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

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

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

【讨论】:

【参考方案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 中的免提吗?谢谢 @user8264 "半满" - developer.sony.com/develop/wearables/smarteyeglass-sdk/guides/…【参考方案3】:

要通过 rfcomm 接收 pcm 音频流,您可以使用代码流作为提示解释 (Reading Audio file in C and forwarding over bluetooth to play in Android Audio track) ,并进行更改。将初始化时使用的频率从 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 播放。”

会起作用的。

【讨论】:

【参考方案4】:

android 4.2.2 中似乎缺少audio.conf?

【讨论】:

从 Jellybean 开始,bluez 堆栈被 google 替换为 Broadcomm 蓝牙堆栈 在 4.1.2 中,我有上面提到的 audio.conf 文件。安装 AOKP 4.2.2 后,audio.config 不见了。 4.2.2 将包含来自 Broadcom 的蓝牙堆栈,audio.conf 是 bluez 堆栈的一部分 here【参考方案5】:

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

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

默认情况下,您支持蓝牙的 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 可用here。如果你这样做,你将不得不构建一个本地应用程序 (C/C++) 并为你的平台编译它。您必须能够使用Android NDK 执行此操作。

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

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

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

解码SBC并不难,可以找解码器 right here。

最后一步是将 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

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

一旦您获得了与您的蓝牙适配器配对的设备列表,您就可以知道它是否已连接到 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 音频流呢? 因为 A2DP 是一种标准,并且默认情况下在每部手机中都使用(即它不需要任何应用程序在客户端工作)。如果您决定使用 RFCOMM,我猜您还需要提供一个客户端应用程序,该应用程序将从远程设备流式传输音频。如果您决定这样做,您还可以使用 wifi 通过常规套接字流式传输音频。 Play Store 上的许多应用程序都提供了这样的功能。 话虽如此,如果您应用我上面提到的更改,您的接收设备将是独一无二的,因为这些更改不会在每部 Android 手机上都可用(除非您在您的应用中使用 JNI 封装另一个 BlueZ 堆栈,而且,这仍然非常困难)。但另一方面,每台设备都可以将音频流式传输到您的接收器。 我无法在我的 Android 手机中找到 audio.conf 文件!有一个蓝牙文件夹,但它是空的。 使用 Gingerbread 并不代表有问题。您需要 UNIX 权限才能修改此文件并列出包含/system 的分区的内容,是的。另外,您要应用的更改必须在root 中执行,因此需要对手机进行root。

以上是关于在 Android 中通过蓝牙接收音频的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ios 中通过蓝牙接收简单的整数值

怎么读取Android的蓝牙接收到的数据

捕获文件在 C# 中通过蓝牙发送

在android中通过蓝牙进行多连接

在外部蓝牙设备和 Android 手机之间传输音频

如何在 Android 中通过 FFmpeg 解码音频