随着电子产品的普及,越来越多的年轻人热衷于使用蓝牙技术来播放歌曲(相当多的手机品牌取消了耳机插孔),本篇文章就和大家聊聊蓝牙音乐SRC端在安卓系统中的实现原理。
安卓系统参考版本:Android-9
蓝牙SRC侧播放蓝牙音乐,音频数据都是从安卓音频系统发送过来的,蓝牙协议栈接收到音频数据处理再发送到SNK端进行播放,整体的流程大概如下图所示:
红色框图标注的部分就是我们今天探讨的内容,在对蓝牙音乐SRC介绍之前,我们得简单了解下安卓中音频系统。安卓的audio系统播放音频都是将数据发送到对应的输出设备进行播放,蓝牙自然也是其中一种音频输出设备,类似的输出设备还有如下这些,源码路径:system/media/audio/include/system/audio.h
这些音频输出设备通过HAL框架以一个个单独的模块存在于安卓系统中,音频系统服务层audioserver在初始化过程中通过HAL技术加载配置文件支持的模块。这里以蓝牙a2dp为例具体分析下加载模块的时序图:
这样音频服务就和蓝牙协议栈bluedroid建立联系,蓝牙音乐播放的音频数据就可以通过该路径源源不断的送入蓝牙协议栈中进一步处理。
蓝牙提供的音频系统的动态链接库为:audio.a2dp.default.so
源码路径:/system/bt/audio_a2dp_hw/
音频系统初始化阶段只会加载打开 audio.a2dp.default.so 获取到a2dp模块接口,至于打开输出流只有在A2DP协议连接成功后才会执行。
从以上时序图可以看到 audio.a2dp.default.so 运行在音频服务的进程中,那其又如何与蓝牙服务进程 com.android.bluetooth 交互呢?
跨进程通信有多种方式,audioserver 和 com.android.bluetooth 在蓝牙音乐数据传输场景下通过 socket 完成跨进程通信。
蓝牙协议栈创建如下两种socket与audioserver进行通信:
- /data/misc/bluedroid/.a2dp_ctrl :控制socket,传递A2DP的控制信号
- /data/misc/bluedroid/.a2dp_data :数据socket,传递A2DP的音频数据
Bluedroid 通过数据 socket 接收到 audio 发送过来的音频数据后,根据A2DP连接时双方协商确定的编码方式对数据进行编码操作,最终将编码后的数据通过l2cap链路发送到SNK端。具体时序图如下:
对音频数据进行编码的具体操作则在各编码方式对应的处理函数中完成,源码路径参考:system\bt\stack\a2dp\a2dp_xxx_encoder.cc\a2dp_xxx_encode_frames(),xxx对应A2DP连接使用的编码方式(SBC、AAC、aptX、LDAC等)。
数据最终在bta层 bta_av_data_path() 处理函数中通过 p_scb->p_cos->data() 回调从 btif_a2dp_source_cb.tx_audio_queue 队列中取出,数据随后经过进一步封装发送给SNK端,SNK再反向解码出音频流播放。
蓝牙音乐SRC侧的安卓实现大体上如此,感兴趣的小伙伴欢迎私信留言一起讨论,共同学习,一起进步!