最近做蓝牙协议栈的过程中遇到一些客户偶尔提报音频卡顿的问题,所以引发了一些感想,跟大家分享下,非标比如AVDTP不走协议栈(ADSP+芯片offload)或者音频编解码不在主控内,在额外的ADSP中不在我们讨论的范围内,我们只是介绍常规的情况!
一. A2DP的架构
我们先来介绍下整个A2DP audio path的框架

A2DP source的发送流程
UL(Upper Layer)收集到PCM数据,然后发送到A2DP,A2DP经过codec算法(SBC/AAC/APTX/LDAC/LHDC..)压缩成特定的音频格式,然后交给AVDTP,AVDTP转交给L2CAP,L2CAP通过ACL格式转交给HCI,然后到达BT chip,通过RF射频出去。
A2DP sink的接收流程
BT chip通过RF接收进来数据,然后通过ACL交给HCI,然后交给L2CAP,L2CAP交给AVDTP,AVDTP交给A2DP,A2DP收到的是remote经过压缩的数据,此时通过codec算法(SBC/AAC/APTX/LDAC/LHDC..)解压成PCM数据,然后交于声卡播放
我们在拿一个Source设备(手机)跟一个Sink设备(车载/耳机/音箱)来举例子看下整个结构

整个流程跟上面的A2DP类似,所以我们就不介绍了
二. 卡顿原因分析
所以从这个框架中就可以总结归纳出来整个音频卡顿可能原因
NOTED: 在这里注意下,音频延迟跟音频卡顿分析思路完全不同,有的人可能会混淆
音频延时的链接如下:
https://blog.csdn.net/XiaoXiaoPengBo/article/details/148494385?spm=1001.2014.3001.5501
大体的可能得原因如下:
|------------|--------------------------|--------------------------------------------------|
| 问题端 | 原因 | 解决方案 |
| 射频问题 | 芯片射频性能差 | 需要在整板验证射频是否满足BR/EDR性能参数 |
| 射频问题 | 天线问题 | 修改天线参数 |
| 射频问题 | 隔离度不好 | 需要实测隔离度 |
| 射频问题 | 射频干扰 | 2.4G设备过多,影响蓝牙功能,需要干净环境测试 |
| 共存问题 | BT/Wi-Fi共存 | 1. 调整共存参数,一般是通过Wi-Fi测调整,但是部分参数有蓝牙命令 2. 蓝牙独立天线导入 |
| 共存问题 | BT/BLE共存 | 1. 调整BLE scan占空比 2. 调整connection interval,满足客户场景 |
| 性能 | 芯片性能满足不了 | 目前要根据客户需求,提前介入最复杂场景实测,不能通过理论计算带宽数据 |
| 性能 | CPU loading高 | 查找平台CPU loading高的原因 |
| 性能 | CPU异常 | 比如手机异常原因导致的过热,卡顿,导致蓝牙音乐顿卡 |
| Sink | 协议栈问题 | 不同的协议栈问题需要分别查看问题 |
| Sink | 平台Audio (Framwork/BSP)问题 | 平台查找,非蓝牙协议栈团队强项 |
| Source | 协议栈问题 | 不同的协议栈问题需要分别查看问题 |
| Source | 网络缓冲音乐 | 听在线音乐,网络问题导致没有音频缓冲 |
| Source | UL送数据过慢 | 上层给协议栈送数据过慢 |
1. 射频问题
射频问题可能集中在芯片/天线/环境等,排查这类问题有固定的思路,这种一般在红框这个阶段出问题,一般在空口中遇到大量重传导致的音频顿卡,建议优先先优先考虑射频问题!

a. 芯片射频性能差
这个没啥好解释的,正常一个射频芯片肯定要根据BQB的RF test suite做射频测试,BQB有常规的case,下载链接如下:
https://files.bluetooth.com/wp-content/uploads/dlm_uploads/2025/11/RF.TS.p36.pdf
这个一般让芯片进DUT模式下进行传导非信令测试,很多仪器都默认支持的,比如罗德施瓦茨的仪器,IQ等
这个性能不好可能会体现在各个方面呢·,比如连线不稳定,异常断线,音乐顿卡等。
如果出现以上现象,或者在蓝牙空口中有大量的重传,建议优先排查这个
b. 天线问题
这个也很好理解,天线阻抗匹配不良、布局不合理、增益不足,影响信号传输效率,同样会遇到芯片射频性能不合格的现象,比如比如连线不稳定,异常断线,音乐顿卡等。
最搞笑的是我经验遇到客户不插天线就进行蓝牙功能测试,还经常报蓝牙音乐顿卡
如果出现以上现象,或者在蓝牙空口中有大量的重传,建议优先排查这个
c. 隔离度不好
里面的问题展开其实有很多种原因
- 蓝牙独立天线,跟Wi-Fi 2.4G走FDD(频分)的隔离度不好,导致互相干扰,造成音乐顿卡
- 同一个设备有多个蓝牙芯片,芯片跟芯片的隔离度不好,造成音乐顿卡
排查这个的思路也比较简单,单点设备工作,比如跟wifi的隔离度可以通过关闭wifi功能,比如多个蓝牙芯片的隔离度,可以关闭其他设备的蓝牙功能,来着个排除
d. 射频干扰
正常蓝牙有跳频技术 (Frequency-Hopping Spread Spectrum,FHSS),甚至有自适应跳频(Adaptive Frequency Hopping,AFH),但是有的时候奈何周围设备实在太多,无法避免的也会收到干扰
这个时候可以拿到屏蔽房或者相对干净的环境测试,排除下是否是射频干扰!
2. 共存问题
共存问题也是造成蓝牙音乐顿卡的比较常见的问题,此问题也是有相对的固定排查思路,这种一般在空口阶段出问题

a. 蓝牙/Wi-Fi共存
蓝牙/Wi-Fi共存造成的蓝牙音乐卡顿在combo模组中非常常见,通常从空口来看也跟射频不好现象一样,都有大量的重传封包,排查方式也相对简单,关闭Wi-Fi功能,查看蓝牙音乐是否还有卡顿,如果卡顿消失,那么基本确定是蓝牙/Wi-Fi问题,那么剩下的就是解决思路了,每家芯片的共存都有自己的配置的方式:
- 修改蓝牙/Wi-Fi的策略,基本每个芯片原厂都会提供,此思路具有通用性,但是配置方法不具有通用性
- 提升蓝牙的ACL优先级,部分芯片有蓝牙的vendor command来提升蓝牙音乐的优先级
b. BT/BLE共存
大部分的人只是知道蓝牙/Wi-Fi的共存,其实还有BT/BLE的共存,这个通过air log或者btsnoop都会直观的看到是否有BLE的相关的操作,如果有那么关闭BLE相关的动作看看,是否现象会消失,如果消失,那么可以判定为BT/BLE共存,那么剩下的就是解决思路了
- 调整BLE scan占空比,有的协议栈,为了BLE scan响应快,把scan interval/windows拉的占空比很高,从而造成蓝牙音乐顿卡,另外扩展下,占空比很高有很多问题,比如bt inquiry非常慢,连接变慢等等吧·
- 调整connection interval,BLE有connection interval的概念,说白了就是ble多久醒一次达到低功耗的作用,但是如果有很多个BLE的设备连接,那么就非常占用芯片的射频资源,导致音频顿卡,所以并不是connection interval越小越好,要根据需求,调整适合的connection interval
3. 性能
这里说的性能,不一定是蓝牙芯片的性能,也包括主控的性能
a. 蓝牙芯片性能
这种情况比较极端,但是在特定的领域也确实会遇到哈·,比如车载的后排现在基本都是要求双路A2DP音频流+双路手柄,这种就非常考验蓝牙芯片性能是否能够满足了,而且这种情况不能通过理论的计算方式来判断是否满足,需要实测。
如果按照芯片的理论的计算方式:A2DP SBC HQ音频大概是328kbps,双路就是328*2=656,然后HID一般在12k~36kbps之间,那么2路A2DP+2路HID才700kbps左右,而EDR最低2M呢,是不是肯定满足呢?不是这样的哈·,因为还要牵扯到master/slave clock,比如你链接的4个设备中,假设有既做master又做slave的情况,那么作为slave,要跟着对端master跳频,那么又要做master来协调对端slave跳频,如果这时候调度冲突呢?肯定要做射频裁决调度的动作,肯定会丢失一部分资源,而这块会大大影响了性能,每家芯片的调度有差异(这块不会在spec规定),就会造成以上A2DP*2+HID*2,有的芯片厂家可以,但是有的厂家不可以的情况!所以这里就要实测了·,要根据需求实测,用数据说话,提前要有这种风险意识!
b. CPU性能问题
这个没啥好解释的哈·,比如cpu loading高或者异常都会造成音频卡顿,
甚至有的性能低的芯片不支持硬浮点运算,A2DP音频编解码速率跟不上,或者CPU异常了调度也会出现问题,造成音频顿卡,也会存在哈·
4. Sink端问题
Sink一般在红框阶段出问题,包括协议中的问题,平台audio的问题等

a. 协议栈的问题
协议栈的问题可能就是比较宽泛的概念了,大概可以通过几个角度来判断
协议栈调度异常
这个没有啥好介绍的,就是协议栈的调度问题,协议栈的接收或者playback倍其他task或者thread抢占,拿不到资源,这个可能会引起音频卡顿。
没有缓冲buffer或者缓冲buffer过小
为什么会有缓冲buffer呢,在介绍这个之前,我们就要介绍下jitter的概念了,什么是jitter呢?就是比如上面我们介绍了一包数据是28ms的延迟,也可以理解为每包duration如果所有都恒定,那么duration都应该是28ms,但是由于是无线,所以肯定会存在干扰等因素,导致包肯定有重传等,所以在空口的duration肯定不是那么恒定的,比如来了28ms的数据,你就直接播放,那么由于空口有干扰,导致下个包是50ms来(这个是假定制),那么你本应该在29ms播放的时候发现没有数据了,所以这时候会存在22ms的空档没有数据期,所以就会卡存,比如这种空口包

可以看到只要有duration增大的情况,那么后面一定会有duration小的数据过来来补充,我们就成为这种现象为jitter.
为了避免这种情况,所以sink端一定会有缓冲buffer来降低这个卡顿风险,但是谈到这里学问就大了·,你增大缓冲buffer是解决了卡顿的风险,但是就带来延迟多了问题,比如你缓冲buffer缓冲100ms,那么就额外多了100ms的延迟。这时候我们就要在特定的平台来权衡这个数值了,这个方面在不同的平台调整的值策略不同,要实测
b. 平台audio的方式
可以见到下图,Sink端在用codec decoder A2DP协议的数据,得到PCM raw data,这时候就应该丢给喇叭播放了,也就是红框部分!

那么这个就是平台依赖了,不同的平台主控肯定方式天差地别,比如Linux可以用alsa/pulse audio播放,Android可以用AAudio/Audio Track播放,MCU可能要写自己的IIS或者SAI播放等等,而不同的方式可能会导致音频卡顿呢,但是这种问题往往太耦合了,需要用切割的方法来定位问题所在,也是需要排除空口,host的问题!排查方式基本思路进行切割下·

ⅰ. 空口确认
首先按照①来确定空口卡不卡·这里排查方式有好多种方法,可以看空口的bitrate

看看真实的bitrate跟实际的bitrate是否大提相同,如果差异过大,就回到对端Src或者射频层面的问题了·
另外,这里不一定完全具有参考性,也不能看到差异过大,就判断为空口卡顿,可以在空口听下音乐卡不卡

但是有的编解码不支持在ellisys播放,此时还有一个方法,就是看每包AVDTP media packet的delta

总之就是确定空口没有问题,就进入下个Host阶段了·
ⅱ. Host确认
在确定了空口(也就是①)没有问题后,那么可以进入阶段②的排查,也就是Host层面的排查,空口层面没有问题后,不代表所有的数据就能到Host层面,所以Host层面也有几个方法,类似于空口的,如果有条件抓取btsnoop(Linux/Android平台),那么也用bitrate,直接听,Time delta三板斧来排查,就是空口的三个步骤,我们就不重复了,如果没有条件抓取btsnoop(RTOS/裸机),那么可以自己写一个bitrate计算公式(1s decoder后的pcm raw data size),来计算下解码后的数据大小,也能做一个常规的判断· 如果这里也没有问题基本就定义到平台的audio问题了·
ⅲ. 平台audio问题
如果你已经确定空口跟host decoder完毕后的这个流程已经没有问题,那么就剩下平台audio了,还是那句话,不同的平台实现差异有点大,具体你的平台代码只有你自己熟悉,这部分就不具有通用性了,比如DMA有问题,playback的task/thread优先级过低,格式设置错误等等吧,但是我认为既然已经定位到这里了,已经离着解决问题很快了!!!
5. Source端问题
a. 在线网络缓冲问题
尤其是source播放网络歌曲的时候,由于网络并不好,所以有的时候有的时候在网络缓冲,导致source送给sink的数据不联系,造成了音频卡顿,我们做sink的时候,连接手机,竟然会遇到软测说音频卡顿,结果我们发现是网络缓冲的问题,这种乌龙有的时候也挺无语的哈,但是这种通过空口/btsnoop也会判断出来,我们通过空口来说吧!
注意哈:这种问题的卡顿跟射频/共存的空口现象不同哈,射频/共存的空口可能是有大量的重传data,而这种由于网络问题,重传很少,基本就是不发送,所以驱动还是很大哈·
b. 协议栈的问题
确定了编解码格式(编解码类型/采样率/声道等)以及AVDTP stream channel基于L2CAP的MTU后,基本就能确定了packet duration了·,比如每包之间间隔 20ms,那么source就要严格按照这个来执行,如果source出现调度不及时,比如每包之间间隔20ms,而你30ms才给sink一包,肯定会造成音频卡顿,这个也没啥技术难度不能理解的哈
c. UL层送数据过慢
上面说了能通过一些参数计算出来packet duration了,所以协议栈应该严格按照这个来给sink数据,但是数据从哪里来呢?肯定是应用层播放器来吧?那么UL层数据给的不及时,那么同样会造成sink卡顿,这个也非常容易理解哈· 我们就不过多展开了
三. 蓝牙卡顿示例分析
这个btsnoop是我们做为source给对端耳机发送音频流的,对端音频卡顿的问题·

可以看到,实际给我给对端的数据本身就少了· ,然后为了谨慎起见,我们再通过time delte来确定下·

可以看到里面有很多delta过高的问题,然后我们再通过音频能量图来看下·
