固件 WebSocket 音频传输协议规范
本文档描述了小智固件通过 WebSocket 向服务端发送音频数据的二进制协议格式。服务端可据此文档解析和处理固件上传的音频数据。
1. 概述
固件支持三种二进制协议版本,通过配置选择使用:
| 协议版本 | 说明 |
|---|---|
| 版本 1 | 直接发送 Opus 音频数据,无额外元数据 |
| 版本 2 | 包含完整的消息头,用于服务端 AEC 处理 |
| 版本 3 | 精简版消息头,减少带宽占用 |
默认使用 版本 1。
2. 音频参数
所有协议版本使用相同的音频参数:
| 参数 | 值 |
|---|---|
| 采样率 | 16000 Hz |
| 声道数 | 1 (单声道) |
| 帧时长 | 60 ms |
| 编码格式 | Opus |
3. 协议格式详解
3.1 版本 1 (BinaryProtocol1)
说明:直接发送 Opus 音频数据,无任何头部信息。
┌────────────────────────────┐
│ Payload │
│ (Opus 音频数据) │
│ N 字节 │
└────────────────────────────┘特点:
- 消息体即为纯 Opus 音频数据
- 服务端收到后直接进行 Opus 解码
- 适用于网络带宽充足的场景
3.2 版本 2 (BinaryProtocol2)
说明:包含完整的消息头,适用于需要服务端进行回声消除(AEC)的场景。
数据结构
struct BinaryProtocol2 {
uint16_t version; // 协议版本,值为 2
uint16_t type; // 消息类型:0 = Opus 音频, 1 = JSON 文本
uint32_t reserved; // 保留字段,用于未来扩展
uint32_t timestamp; // 时间戳(毫秒),用于服务端 AEC 对齐
uint32_t payload_size; // Payload 负载大小(字节)
uint8_t payload[]; // 负载数据(Opus 音频数据)
} __attribute__((packed));消息结构图
┌──────────┬──────┬──────────┬──────────┬──────────┬──────────┐
│ version │ type │ reserved │ timestamp│pay size │ payload │
│ (2B) │ (2B) │ (4B) │ (4B) │ (4B) │ (N B) │
└──────────┴──────┴──────────┴──────────┴──────────┴──────────┘
│ 头部 (16 字节) │
└────────────────────────────────────────────────────────────┘
↑ 真正的 Opus 音频数据字段说明
| 字段 | 字节序 | 大小 | 说明 |
|---|---|---|---|
| 字节序 (大端) | version | 网络 2 字节 | 协议版本号,固定为 2 |
| type | 网络字节序 (大端) | 2 字节 | 消息类型:0 = Opus 音频,1 = JSON 文本 |
| reserved | 网络字节序 (大端) | 4 字节 | 保留字段,当前设为 0 |
| timestamp | 网络字节序 (大端) | 4 字节 | 时间戳(毫秒),用于服务端 AEC 对齐 |
| payload_size | 网络字节序 (大端) | 4 字节 | payload 数据的长度(字节) |
| payload | - | N 字节 | 实际的 Opus 编码音频数据 |
服务端解析步骤
- 读取前 16 字节:读取消息头
- 解析头部字段:
- 将前 2 字节转换为整数得到
version(需使用ntohs()) - 下 2 字节转换为整数得到
type(需使用ntohs()) - 下 4 字节转换为整数得到
reserved - 下 4 字节转换为整数得到
timestamp(需使用ntohl()) - 下 4 字节转换为整数得到
payload_size(需使用ntohl())
- 将前 2 字节转换为整数得到
- 验证 version:确认 version == 2
- 验证 type:确认 type == 0(Opus 音频)
- 提取 payload:从偏移量 16 字节处开始,读取
payload_size字节的数据,即为 Opus 音频数据 - 解码 Opus:使用 Opus 解码器将 payload 解码为 PCM 原始音频
时间戳用途
timestamp 字段表示音频帧相对于会话开始的时间偏移(毫秒),服务端可利用此字段:
- 将服务端音频与麦克风输入进行时间对齐
- 用于回声消除(AEC)算法
- 检测音频帧是否连续或存在丢包
3.3 版本 3 (BinaryProtocol3)
说明:精简版消息头,减少带宽占用,适用于对带宽敏感的场景。
数据结构
struct BinaryProtocol3 {
uint8_t type; // 消息类型:0 = Opus 音频, 1 = JSON 文本
uint8_t reserved; // 保留字段
uint16_t payload_size; // Payload 负载大小(字节)
uint8_t payload[]; // 负载数据(Opus 音频数据)
} __attribute__((packed));消息结构图
┌────────┬──────────┬─────────────┬──────────┐
│ type │ reserved │ pay size │ payload │
│ (1 B) │ ved(1B) │ (2 B) │ (N B) │
└────────┴──────────┴─────────────┴──────────┘
│ 头部 (4 字节) │
└────────────────────────────────────────────┘
↑ 真正的 Opus 音频数据字段说明
| 字段 | 字节序 | 大小 | 说明 |
|---|---|---|---|
| type | - | 1 字节 | 消息类型:0 = Opus 音频,1 = JSON 文本 |
| reserved | - | 1 字节 | 保留字段,当前设为 0 |
| payload_size | 网络字节序 (大端) | 2 字节 | payload 数据的长度(字节) |
| payload | - | N 字节 | 实际的 Opus 编码音频数据 |
服务端解析步骤
- 读取前 4 字节:读取消息头
- 解析头部字段:
- 第 1 字节为
type - 第 2 字节为
reserved - 下 2 字节转换为整数得到
payload_size(需使用ntohs())
- 第 1 字节为
- 验证 type:确认 type == 0(Opus 音频)
- 提取 payload:从偏移量 4 字节处开始,读取
payload_size字节的数据,即为 Opus 音频数据 - 解码 Opus:使用 Opus 解码器将 payload 解码为 PCM 原始音频
4. JSON 文本消息
当 type 字段值为 1 时,payload 为 JSON 文本格式,用于传输控制消息。
唤醒词检测通知
当用户说出唤醒词后,固件会发送以下 JSON 消息:
{
"session_id": "xxx",
"type": "listen",
"state": "detect",
"text": "小智"
}字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| session_id | string | 会话 ID |
| type | string | 消息类型 |
| state | string | 状态:detect 表示检测到唤醒词 |
| text | string | 唤醒词内容 |
5. 连接建立流程
5.1 Hello 消息
设备连接 WebSocket 后,首先发送 Hello 消息,包含音频参数:
{
"type": "hello",
"version": 1,
"features": {
"mcp": true
},
"transport": "websocket",
"audio_params": {
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
}服务端可根据 audio_params 确认音频参数。
5.2 协议版本协商
服务端可在响应中指定使用的协议版本。固件默认使用版本 1,可通过配置切换为版本 2 或版本 3。
6. 音频数据处理流程
服务端处理固件上传音频数据的完整流程:
┌─────────────────┐
│ 接收 WebSocket │
│ 二进制数据 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 判断协议版本 │
│ (根据配置或解析) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 解析消息头部 │
│ 提取 payload │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 使用 Opus │
│ 解码器解码 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 得到 PCM │
│ 原始音频数据 │
└─────────────────┘7. 注意事项
-
字节序:所有多字节整数均使用网络字节序(大端序),服务端解析时需要使用
ntohs()和ntohl()进行转换 -
最小帧大小:每帧 Opus 数据最小约 10 字节,最大约 120 字节(取决于压缩率和音频内容)
-
帧间隔:每帧时长为 60ms,服务端可据此计算音频时长:
duration = payload_size_frames * 60ms -
版本兼容性:建议服务端支持所有三个版本,以便在不同配置下都能正常工作
-
错误处理:
- 若
version字段不是 1/2/3,应断开连接 - 若
type字段不是 0/1,应忽略该消息 - 若
payload_size超过合理范围(如 > 1000 字节),应视为异常
- 若
8. 参考资料
- 固件源码:
main/protocols/websocket_protocol.cc - 协议定义:
main/protocols/protocol.h - Opus 编码库:https://opus-codec.org/
Last updated on