在上一篇的内容里【LE Audio】PACS精讲[1]: 吃透基础规则,解锁音频能力发布核心逻辑,我们吃透了PACS的基础规则,从一致性要求、协议兼容、GATT交互约定到术语体系,搭建起了PACS的知识地基。而PACS的服务层,正是这些基础规则落地的核心载体,是蓝牙音频设备作为服务器对外发布自身音频能力的核心操作框架。如果说基础规则是PACS的"语法规范",那服务层就是基于语法写出的核心篇章,定义了PACS服务该如何声明、哪些设备能部署、音频能力该如何发布与交互,甚至明确了多参数组合的硬性规则------这些内容也是实际开发中设备兼容、能力解析的核心考点,很多开发中的问题都源于对服务层规则的理解偏差。
目录
[1.1 实例唯一性:一个服务器只能有一个PACS服务](#1.1 实例唯一性:一个服务器只能有一个PACS服务)
[1.2 UUID固定+推荐主服务:让客户端一眼找到PACS服务](#1.2 UUID固定+推荐主服务:让客户端一眼找到PACS服务)
[3.1 音频能力:通过PAC记录发布,设备的固有硬件配置表](#3.1 音频能力:通过PAC记录发布,设备的固有硬件配置表)
[3.2 音频可用性:通过Available Audio Contexts发布,设备的实时营业状态](#3.2 音频可用性:通过Available Audio Contexts发布,设备的实时营业状态)
[3.3 音频能力与可用性的核心区分:一个不变,一个实时变](#3.3 音频能力与可用性的核心区分:一个不变,一个实时变)
[4.1 多参数的两种表示形式:位域与范围](#4.1 多参数的两种表示形式:位域与范围)
[4.2 全组合规则:不允许选择性支持](#4.2 全组合规则:不允许选择性支持)
[4.3 离散值需求:拆分PAC记录,而非在一个记录中留空](#4.3 离散值需求:拆分PAC记录,而非在一个记录中留空)
[4.4 全组合规则的设计初衷:杜绝声明与实际不符](#4.4 全组合规则的设计初衷:杜绝声明与实际不符)
本文聚焦PACS服务层的核心,从服务声明的唯一性准则,到设备的部署范围,再到PAC记录的多参数组合规则、音频能力与可用性的核心区分,把服务层的规则讲透,真正理解蓝牙音频设备是如何对外公示自己的音频能力的。
一、服务声明:PACS服务器的唯一身份准则
任何一个蓝牙GATT服务的实现,第一步都是明确服务声明规则,PACS也不例外。服务声明为PACS服务器定下了两个核心硬性规则,一个是实例唯一性 ,一个是UUID 与 服务类型 规范,这两个规则是客户端能正常发现、识别PACS服务的前提,相当于给PACS这个音频能力公示牌定下了摆放规则。
1.1 实例唯一性:一个服务器只能有一个PACS服务
协议中明确规定There shall be no more than one instance of the Published Audio Capabilities Service (PACS) on a server,这里的shall是强制要求,意味着所有PACS服务器都必须遵守,不存在任何例外情况。
之所以做这样的强制要求,核心原因是为了避免客户端的发现与解析混乱。蓝牙客户端的GATT服务发现流程,是按UUID扫描设备上的服务实例,若一个设备上存在多个PACS服务实例,客户端会无法判断哪个实例是有效的音频能力信息,甚至会出现多个实例的能力参数冲突的情况------比如一个实例声明支持16k采样频率,另一个声明不支持,客户端就会陷入解析困境。
可以把PACS服务类比成一个商家的官方资质公示牌,一个商家只能有一个官方公示牌,若挂多个内容不同的公示牌,消费者根本无法判断哪个是真实有效的,PACS的实例唯一性规则,本质上就是为了杜绝这种"多牌混乱"的情况。
1.2 UUID固定+推荐主服务:让客户端一眼找到PACS服务
协议对PACS的服务标识和服务类型也做了明确规定:
PACS should be a <<Primary Service>> and the service universally unique identifier (UUID) shall be set to <<Published Audio Capabilities>> as defined in [1]。
这句话里有两个关键要求,一个是强制的,一个是推荐的,我们分开解析:
-
UUID 强制固定:UUID是蓝牙服务的唯一标识,PACS的UUID必须使用蓝牙SIG在《Bluetooth Assigned Numbers》中定义的「Published Audio Capabilities」专属UUID,不允许厂商自定义。这一规则让所有厂商的PACS服务都有了统一的身份码,客户端只需扫描这个固定UUID,就能找到设备上的PACS服务,无需为不同厂商做适配,这也是蓝牙设备互联互通的核心基础。
-
推荐作为主服务 :这里的should是推荐要求,并非强制,但从实际开发和兼容性角度,几乎所有厂商都会将PACS实现为主服务。原因在于蓝牙GATT的服务发现逻辑:客户端会优先扫描设备上的主服务( Primary Service) ,而次服务( Secondary Service) 必须关联到某个主服务才能被发现,若将PACS实现为次服务,很可能因主服务关联问题导致客户端无法扫描到,进而影响音频能力的发现。这就像商家的公示牌要挂在门店的显眼位置,而不是藏在角落,让顾客能一眼看到。
二、部署范围:哪些设备能搭载PACS服务?
PACS并非所有蓝牙设备都能部署,其部署范围有明确的界定,核心是能参与蓝牙音频流交互的设备,具体来说,是能接收单播/广播音频流,或能发送单播/广播音频流的蓝牙音频设备。协议中明确
PACS can be instantiated on devices that can accept the establishment of unicast Audio Streams or devices that can receive broadcast Audio Streams
同时给出了典型的设备例子:speakers、headsets、hearing aids、microphones。
我们可以将这些设备按音频交互的角色,分为三大类,覆盖所有PACS的部署场景,且这三类设备都能同时支持单播和广播音频流的交互,因为PACS的音频能力是设备固有属性,与音频流的传输方式无关:
1. 纯音频接收设备
这类设备的核心功能是接收蓝牙音频流,比如蓝牙音箱、纯听音乐的蓝牙耳机、助听器。这类设备会通过PACS的Sink PAC特征发布自身的音频接收能力,比如支持的编解码器、采样频率、编码帧字节数等,让音频发送设备(如手机)知道"我能接收什么样的音频数据"。
2. 纯音频发送设备
这类设备的核心功能是发送蓝牙音频流,比如蓝牙麦克风、音频采集器。这类设备会通过PACS的Source PAC特征发布自身的音频发送能力,让音频接收设备知道"我能发送什么样的音频数据"。
3. 音频收发一体设备
这类设备同时支持音频的接收和发送,是最常见的蓝牙音频设备,比如带麦克风的蓝牙耳机、手机、平板。这类设备会同时部署Sink PAC和Source PAC特征,分别发布音频接收和发送能力,比如手机既可以向耳机发送音乐音频流,也可以接收耳机麦克风的语音音频流,双方通过彼此的PACS服务识别对方的能力,实现音频流的适配。
简单来说,只要设备是蓝牙音频生态的参与者,需要与其他设备进行音频数据的收发,就必须部署PACS服务。反之,非音频类蓝牙设备(如蓝牙鼠标、蓝牙手环)则无需部署,因为这类设备不涉及音频流交互,没有音频能力需要发布。
三、服务核心行为:三大能力发布逻辑,区分固有能力与实时状态
PACS服务层的核心行为,就是对外发布设备的音频相关信息,并支持客户端的读取与服务器的主动通知。这一行为可以拆解为三大核心逻辑,分别是音频能力发布、音频可用性发布,以及二者的核心区分,这也是PACS服务层的核心设计思路,理解这一点,就能抓住PACS服务的本质。
3.1 音频能力:通过PAC记录发布,设备的固有硬件配置表
音频能力是PACS服务发布的核心信息,通过Sink PAC (接收能力)和Source PAC(发送能力)两个特征对外暴露,客户端可以读取这些特征的数值,服务器则可以在能力发生变化时主动向客户端发送通知。
协议中对音频能力的定义是
Audio capabilities, exposed in PAC records, represent the server audio capabilities independent of available resources at any given time
这句话是理解音频能力的关键,核心有两层含义:
-
音频能力由 PAC 记录承载:PAC记录(Published Audio Capability record)是一组参数的集合,每个参数对应一个音频能力项,比如Codec_ID(编解码器ID)、Supported_Sampling_Frequencies(支持的采样频率)、Supported_Octets_Per_Codec_Frame(支持的编码帧字节数)等,一个PAC记录对应一套完整的音频能力配置,设备可以根据自身情况发布一个或多个PAC记录。
-
音频能力是设备的固有属性:与设备的实时资源状态无关,不管设备是否在工作、是否被其他客户端连接,其音频能力都是固定的。比如一个蓝牙耳机支持AAC和SBC两种编解码器,这个能力是硬件和固件决定的,不管耳机是空闲还是正在连手机听歌,这个能力都不会改变。
类比来说,PAC记录就像设备的硬件配置表,比如一台电脑的配置表会写清CPU、显卡、内存的参数,这些参数是电脑的固有属性,不会因为电脑是否开机、是否在运行程序而改变,PACS的PAC记录也是如此。
另外,协议还明确Audio capabilities do not distinguish between unicast Audio Streams or broadcast Audio Streams,即音频能力不区分单播和广播音频流。因为单播和广播只是音频数据的传输方式,而音频能力是设备对音频数据的编解码、处理能力,传输方式的不同不会改变设备的处理能力。比如一个蓝牙音箱支持24k采样频率的音频处理,不管是手机单播给它的24k音频流,还是其他设备广播的24k音频流,它都能处理。
3.2 音频可用性:通过Available Audio Contexts发布,设备的实时营业状态
音频可用性是PACS服务发布的另一核心信息,通过Available Audio Contexts 特征对外暴露,同样支持客户端读取和服务器的主动通知。它与音频能力是完全不同的概念,核心是设备基于特定音频使用场景的实时可用状态。
协议中定义,音频可用性代表
the server's determination of whether it considers itself available to accept the establishment of a unicast Audio Stream based on classifications of the use cases intended for such Audio Streams。
简单来说,就是设备判断自己当前是否有资源、是否能为某个音频场景(Context Type) 提供音频收发服务。
这里的音频场景(Context Type) 是蓝牙SIG定义的音频使用场景,比如音乐播放、语音通话、导航播报、媒体录音等,每个场景对应一个位域值,设备通过置1或置0来表示自身对该场景的可用状态。比如蓝牙耳机正在为手机的音乐播放场景提供服务,此时其导航播报场景的可用位就会置0,代表当前无法接收导航音频流;当音乐播放停止后,导航播报场景的可用位会置1,代表恢复可用。
可以把音频可用性类比成商家的实时营业状态,比如一家餐厅有正餐、下午茶、夜宵三个场景,若正餐时段满座,那么正餐场景的可用状态就是不可用,而下午茶和夜宵场景仍为可用,餐厅会根据自身的座位资源实时更新这个状态,PACS的音频可用性也是如此,设备会根据自身的硬件资源(如蓝牙连接数、音频处理资源)实时更新各音频场景的可用状态。
需要注意的是,音频可用性仅针对单播音频流,不涉及广播音频流。原因在于广播音频流是一对多的传输方式,广播端无需判断接收端的资源状态,接收端也无需向广播端反馈自身的可用状态,而单播音频流是一对一的传输,需要双方确认资源状态,保证音频流的稳定传输。
3.3 音频能力与可用性的核心区分:一个不变,一个实时变
很多人会混淆音频能力和音频可用性,这也是开发中的常见误区,我们用一张表把二者的核心区别讲透,这也是理解PACS服务行为的关键:
|----------|----------------------|-------------------------------------|
| 对比维度 | 音频能力(PAC记录) | 音频可用性(Available Audio Contexts) |
| 本质属性 | 设备固有硬件/固件属性 | 设备实时资源状态属性 |
| 变化性 | 仅在固件更新/安装新应用时改变,平时不变 | 随设备资源使用情况实时变化 |
| 关联对象 | 与音频场景、传输方式均无关 | 仅与单播音频流的音频场景相关 |
| 发布特征 | Sink PAC/Source PAC | Available Audio Contexts |
| 核心作用 | 让客户端知道"设备能处理什么音频" | 让客户端知道"设备现在能为哪个场景提供服务" |
简单总结:音频能力回答的是"能不能"的问题,音频可用性回答的是"现在能不能"的问题。客户端与服务器建立音频流连接时,会先通过PAC记录判断设备是否支持所需的音频能力,再通过Available Audio Contexts判断设备当前在该场景下是否可用,二者缺一不可。
四、PAC记录核心规则:多参数组合的全兼容硬性要求
PAC记录是PACS服务的核心载体,协议对PAC记录的实现制定了一个最核心、最易出错的硬性规则 :当PAC记录中某个参数支持多个值时,服务器必须支持该参数的所有值与其他参数的所有值的全组合。这一规则是保证客户端与服务器音频能力适配的关键,也是实际开发中最容易踩坑的点,协议甚至专门用多个表格和例子来解释这一规则,足见其重要性。
4.1 多参数的两种表示形式:位域与范围
在PAC记录中,一个参数支持多个值的表示形式主要有两种,这两种形式也是蓝牙音频协议中通用的多值表示方式:
-
位域(Bitfield):每个位代表一个值,置1表示支持该值,多个位置1表示支持多个值,典型的如Supported_Sampling_Frequencies(支持的采样频率)。比如0x0006是二进制的0000 0000 0000 0110,代表第1位和第2位置1,对应支持16000Hz和24000Hz两种采样频率。
-
范围(Range) :用最小值+最大值表示支持该区间内的所有值,典型的如Supported_Octets_Per_Codec_Frame(支持的编码帧字节数)。比如0x0032001E,其中0x001E是最小值30,0x0032是最大值50,代表支持30到50之间的所有编码帧字节数。
这两种表示形式的核心特点是一次性声明多个支持的值,无需逐个列出,能有效减少PAC记录的数据量,提升传输效率。
4.2 全组合规则:不允许选择性支持
协议中明确
When exposing support for multiple values in a parameter within a PAC record, the server shall support all possible combinations of that parameter value with the other parameter values contained in the same PAC record。
这里的shall是强制要求,意味着服务器不能选择性支持参数组合------只要在PAC记录中声明了某个参数的多个值,就必须支持这些值与其他所有参数的所有组合,不存在任何例外。

我们结合协议中的经典例子来理解这一规则,这个例子是一个支持音频接收的服务器,其PAC记录的核心参数如下:
采样频率(位域):0x0006 → 支持16000Hz、24000Hz
编码帧字节数(范围):0x0032001E → 支持30~50(共21个值)
根据全组合规则,该服务器必须支持16000Hz与30~ ++50的所有组合,以及24000Hz与30~++ 50的所有组合 ,总计42种参数组合。客户端在请求音频配置时,只要选择的是这42种组合中的一种,服务器就必须能正常处理,不能以不支持该组合为由拒绝。

4.3 离散值需求:拆分PAC记录,而非在一个记录中留空
很多时候,设备厂商会有离散值支持的需求,即不想支持某个参数范围中的所有值,只支持其中的几个离散值。比如上述例子中,厂商只想支持30和50两个编码帧字节数,不支持中间的31~49,这种情况下该如何实现?

协议给出了明确的解决方案:不能在一个PAC记录中用范围表示,而是要将PAC记录拆分为多个独立的PAC记录 ,每个PAC记录中该参数的范围设为**"最小值=最大值"**,即固定值。
还是以上述例子为例,要实现仅支持30和50两个编码帧字节数,需要将原来的1个PAC记录拆分为2个PAC记录:
PAC记录i:采样频率0x0006,编码帧字节数0x001E001E(固定30)
PAC记录j:采样频率0x0006,编码帧字节数0x00320032(固定50)
拆分后,每个PAC记录都遵循全组合规则,客户端可以分别读取这两个记录,知道服务器仅支持30和50两个离散的编码帧字节数。

4.4 全组合规则的设计初衷:杜绝声明与实际不符
协议制定这一规则的核心初衷,是为了杜绝设备声明支持,实际不支持的情况,保证客户端的配置请求能被正常处理。如果允许服务器在一个PAC记录中选择性支持参数组合,客户端就需要逐个验证组合的有效性,这会大幅增加交互流程的复杂度,降低音频连接的效率,甚至会出现连接失败的情况。
而全组合规则+PAC记录拆分的方式,既保证了多值声明的传输效率,又保证了参数组合的明确性,客户端只需根据PAC记录的声明发起配置请求,无需额外验证,这也是蓝牙协议互联互通设计理念的体现。
五、通用约定:数组参数的标准排列规则
当服务器发布多个PAC记录时,PAC记录的参数会以数组形式 存在,比如Codec_ID[i]、Codec_Specific_Capabilities_Length[i],其中i代表第i个PAC记录。为了避免客户端解析数组参数时出现顺序错误,协议制定了PACS服务的通用数组参数排列规则,这一规则适用于PACS的所有特征,是设备实现和客户端解析的通用约定。
协议中明确:
If more than one set of arrayed parameters is specified (e.g., ParameterA[i], ParameterB[i]), then the order of the parameters is as follows (unless noted otherwise): ParameterA[0], ParameterB[0], ParameterA[1], ParameterB[1], ParameterA[2], ParameterB[2], ... ParameterA[n], ParameterB[n]。
简单来说,数组参数的排列遵循按PAC记录分组的原则,即先排列第一个PAC记录的所有参数,再排列第二个PAC记录的所有参数,以此类推,而不是将同类型的参数全部排列在一起。
我们用一个例子来理解,假设服务器有2个PAC记录,涉及两个数组参数Codec_ID[i]和Codec_Spec_Len[i],其参数值如下:
Codec_ID[0] = 0x000000000D,Codec_Spec_Len[0] = 0x0A
Codec_ID[1] = 0x000000000E,Codec_Spec_Len[1] = 0x0B
正确的排列顺序:0x000000000D → 0x0A → 0x000000000E → 0x0B(按记录分组,先0号记录的两个参数,再1号记录的两个参数)
错误的排列顺序:0x000000000D → 0x000000000E → 0x0A → 0x0B(按参数类型分组,先所有Codec_ID,再所有Codec_Spec_Len)
如果客户端按正确的规则解析,而服务器按错误的顺序传输,就会导致参数匹配错误,比如将0x000000000D与0x0B匹配,0x000000000E与0x0A匹配,最终解析出错误的PAC记录,影响音频能力的识别。
这一规则的设计,与PAC记录的整体性密不可分,因为每个PAC记录是一套完整的音频能力配置,其参数之间是强关联的,必须保证同一记录的参数排列在一起,才能保证解析的正确性。
六、服务行为的补充:通知机制的触发与执行
在上一篇的基础规则中,我们讲过PACS服务器必须支持GATT的Notifications子过程,而在服务层,协议对通知机制的触发场景 和执行要求做了更具体的规定,这是保证PACS实时性的核心。
1. PAC记录变化的通知要求
如果服务器的PAC记录发生变化,比如固件更新后新增了对某类编解码器的支持、安装新应用后修改了采样频率的支持范围,服务器必须为包含该PAC记录的Sink PAC/Source PAC特征开启通知,且满足两个执行要求:
若设备处于蓝牙连接状态,且客户端已配置该特征的通知开关,服务器需立即将新的PAC记录值通知给客户端;
若设备未处于连接状态,且客户端已配置通知开关,服务器需在重新连接到绑定的客户端时,立即通知新的PAC记录值。
2. 音频可用性变化的通知要求
如果服务器的音频可用性发生变化,比如某个音频场景从可用变为不可用,或从不可用变为可用,服务器需立即更新Available Audio Contexts特征的数值,并按上述连接状态的不同,将新的可用性值通知给客户端。
3. 通知机制的核心价值:实时同步,无需轮询
通知机制的核心价值,是让客户端能实时同步服务器的音频能力和可用性变化,无需通过定时轮询的方式获取最新值。轮询会增加蓝牙链路的通信负担,降低连接效率,而通知机制是按需推送,只有当数据发生变化时才会传输,能最大程度减少通信开销,保证蓝牙音频连接的稳定性。
七、测试
问题:PACS服务在服务器上的实例部署有何强制要求?其UUID和服务类型有何规定?
答案:
-
强制要求:一个PACS服务器上只能存在一个PACS服务实例,不允许多个实例并存;
-
UUID规定:必须使用蓝牙SIG在《Bluetooth Assigned Numbers》中定义的「Published Audio Capabilities」专属UUID,不允许自定义;
-
服务类型规定:推荐将PACS实现为主服务,便于客户端的GATT服务发现。
问题:简述PAC记录的多参数全组合规则,若需支持离散值该如何实现?
答案:
-
全组合规则:当PAC记录中某个参数以位域/范围形式支持多个值时,服务器必须支持该参数的所有值与其他参数的所有值的所有可能组合;
-
离散值实现方式:不能在一个PAC记录中用范围表示,需将PAC记录拆分为多个独立的PAC记录,每个记录中该参数的范围设为最小值等于最大值的固定值。
问题:PACS中音频能力(PAC记录)与音频可用性(Available Audio Contexts)的核心区别是什么?
答案:
-
本质属性不同:音频能力是设备的固有硬件/固件属性,与实时资源无关;音频可用性是设备的实时资源状态属性,随资源使用情况变化;
-
关联对象不同:音频能力与音频场景、传输方式均无关;音频可用性仅与单播音频流的音频场景相关,不涉及广播流;
-
变化性不同:音频能力仅在固件更新/安装新应用时改变,平时固定;音频可用性随设备资源状态实时变化;
-
发布特征不同:音频能力通过Sink PAC/Source PAC发布;音频可用性通过Available Audio Contexts发布。