蓝牙音乐静音
大家想必对静音功能不陌生,相关场景下该功能很实用。现在的终端设备上有整个音频系统的静音,当然也有局部功能的静音,其中的蓝牙音乐也是提供静音操作的,本期和大家简单分享下蓝牙音乐静音在安卓系统中的实现。
蓝牙音乐的音频数据基本按照如下流程从 SRC 传送到 SNK:
蓝牙音乐静音的实现方案从上图也就非常明了,当前的安卓系统主要是在音频数据上报到 SNK 端的蓝牙协议栈时不进行保存,从而没有解码后的音频数据送入安卓多媒体系统 AudioTrack 来实现的。
上述方案的实现主要依赖协议栈 btif_a2dp_sink.cc 中定义的全局变量 btif_a2dp_sink_cb 中的两个基本变量:
rx_focus_state 变量受蓝牙服务层控制,变量含义顾名思义表示当前的蓝牙音乐是否具有音频焦点,而布尔值的 rx_flush 表示接收到的音频数据是否保存,其和 rx_focus_state 是一 一对应的关系:
这样蓝牙服务也就间接通过 rx_flush 在函数btif_a2dp_sink_enqueue_buf()中实现了蓝牙音乐静音。
蓝牙服务层中的接口为A2dpSinkStateMachine.informAudioFocusStateNative(),还需在framework层中添加接口函数才能打通应用层到蓝牙服务的调用,相信大家都可以轻松实现。
蓝牙音乐静音功能遇到的问题:
静音成功后再解除静音,重新播放蓝牙音乐往往会伴随着一声pop音
针对该问题首先需要分析下蓝牙协议栈将解码后的音频数据送入AudioTrack是否异常,打开宏变量 DUMP_PCM_DATA 记录蓝牙音乐音频数据PCM流,使用PCM流查看工具打开发现送给系统的PCM流很正常啊。
最后请教系统音频的同事后才了解其中的缘由,蓝牙协议栈在静音时是停止往系统AudioFlinger写数据,AudioFlinger会检测通道内是否有数据,这样超过一定时间没有数据后,AudioFlinger会关闭蓝牙协议栈对应的音频通道。然后再解除静音系统会重新打开音频通道,由于打开通道需要时间,但蓝牙协议栈是在解除静音后就开始往送AudioTrack中写数据了,由于存在这样的时序问题才会伴随着一声pop音。
基于AudioFlinger会检测是否有数据这一特性,理想的修复方案就是蓝牙协议栈在静音后往给AudioFlinger 写入空数据,从而避免 AudioFlinger 检测通道一定时间没有送数据后关闭通道。
解决方案:SNK 端的蓝牙协议栈还是正常接收音频数据加入 btif_a2dp_sink_cb.rx_audio_queue 队列保存,只需在音频数据解码完成的处理函数 btif_a2dp_sink_on_decode_complete()中加以判断,如果当前处于静音状态,则将解码后的数据直接memset重置为0的空数据即可。
蓝牙音乐静音的分享到这里就结束了,感兴趣的小伙伴欢迎私信留言一起讨论,共同学习,一起进步!