我想创建一个能够接收音频流的Android应用程序。我想使用A2DP配置文件,但似乎Android不支持A2DP接收器。看起来有很多人在寻找解决这个问题的方法。但是如果接收一个普通的比特流,然后在应用程序中将数据转换为音频呢?我正在考虑通过RFCOMM (SPP蓝牙配置文件)接收PCM或Mp3数据流,然后使用AudioTrack播放。
首先,我如何通过RFCOMM在我的Android手机上接收比特流?有可能通过RFCOMM接收位流作为PCM或Mp3流吗?
第二,如果不可能通过RFCOMM接收作为PCM或Mp3流的位流,我如何将接收的位流转换为音频?
第三,我如何将接收到的数据转换成音频并同时播放音频,在"实时"?我可以只使用onDataReceived吗?
澄清一下,我对使用A2DP配置文件不感兴趣!我想通过RFCOMM (SPP蓝牙配置文件)流式传输数据。接收到的数据流将是PCM或Mp3格式。我想写自己的应用程序,但如果有人知道一个应用程序来解决这个问题,我很高兴听到它!我用的是安卓2.3姜饼。
/约翰尼
不
尝试编写一个Android应用程序来处理这将不是解决方案。至少如果你想使用A2DP Sink
角色。
事实是,Android,正如你提到的,没有实现API调用BlueZ
(蓝牙堆栈Android使用直到果冻豆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作为参考,这相对容易实现。如果采用这种方法,您将不得不构建一个本机应用程序(C/c++),并为您的平台编译它。您必须能够使用Android NDK
.
如果你发现很难使用D-BUS
,你可以尝试这个Java库,我刚刚发现处理通信到D-BUS为您:http://jbluez.sourceforge.net/。我从来没用过,但我觉得值得一试。
你真正要做的是找出A2DP源设备何时与你的手机配对,以及他何时开始流媒体音乐。您可以通过D-BUS检索这些事件。一旦有人尝试流媒体音乐,你需要告诉BlueZ你的本机应用程序将会处理它。有一个非常好的文档解释了您应该处理的事件流。这份文件可以在这里找到。你感兴趣的部分在第七页。在给定的示例中的接收器应用程序是PulseAudio
,但它也可以是您的应用程序。
org.bluez.MediaTransport.Acquire
方法时,BlueZ将向您转发一个UNIX套接字。读取此套接字将为您提供远程设备当前流式传输的数据。但我记得有一个在BlueZ堆栈上工作的人告诉我,在这个套接字上读取的数据不是PCM纯音频,而是编码的音频内容。数据通常以SBC
(低复杂度子带编码)的格式编码。
解码SBC并不难,你可以在这里找到解码器。
最后一步是将PCM音频转发到您的扬声器。
为了防止你卡住,为了以一种更容易的方式测试你的应用程序,你可以使用d-bus
二进制文件,它应该在你的Android系统上可用。他位于/系统/bin 。
在执行上述任何操作之前,您可以做以下快速测试:
获取设备列表:
dbus-send——system——dest=org。Bluez -打印-回复/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_XXorg.bluez.AudioSource.GetPropertiesorg.bluez.Manager.GetProperties
希望这对你有帮助。
另一种方法是使用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();
[无关但有效]此hack仅通过WIFI热点提供mp3流(我在只有AUX输入的汽车中使用它):
- 安装AirSong应用程序,
- 打开wifi热点,
- 将其他设备连接到该热点,
- 从设备的浏览器访问192.168.43.1:8088,你就打开了。
(想知道为什么只有"192.168.43.1"?
audio.conf似乎在Android 4.2.2中丢失了?
要通过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播放。"
。