跳转到主要内容
开始创作
开发文档

音频通道

WebSocket 与 MQTT+UDP 音频帧格式、心跳。

WebSocket 音频

直接通过 WebSocket 二进制帧收发 Opus 音频。上行用户语音 16kHz,下行 TTS 24kHz,帧长均 60ms。

MQTT + UDP 音频(加密)

握手响应的 udp 字段下发 AES-128 密钥与 nonce。每个 UDP 包 = 16 字节包头 + AES-CTR 加密的 Opus 负载:

16 字节包头(同时作为 AES-CTR nonce):
  [0]      type   = 0x01 (audio) | 0x02 (heartbeat)
  [1]      flags  = 0x00
  [2:4]    payload_len   (big-endian uint16)
  [4:8]    ssrc          (会话标识,取自 hello.udp.nonce 字节 [4:8])
  [8:12]   timestamp     (设备自定义,如 unix 秒 / 毫秒)
  [12:16]  sequence      (设备自定义序号)
后接:
  type=0x01 → AES-128-CTR(Opus 负载)
  type=0x02 → 可选不透明负载(原样回显,可不加密)

建立 UDP 通道后,设备先发一个空包做 NAT 穿透,让服务端获知设备的 UDP 地址。之后即可双向收发音频。空包(仅 16 字节、非心跳 type)只用于地址学习,不会回包。

UDP 心跳(平台通用能力)

MQTT KeepAlive 只能证明控制通道仍在;海外 NAT / 防火墙常会在闲置数十秒后回收 UDP 映射,导致「MQTT 文字正常、说话无语音」。平台提供 UDP 心跳 echo:设备发送 type=0x02 包,Gateway 校验 ssrc 属于当前会话后,将完整 UDP 数据报原样返回,并刷新设备地址。发送频率、超时判定、断开重连由设备端自行实现。

字段要求说明
type固定 0x02与音频 0x01 区分;不会进入 ASR
ssrc必须匹配会话取自 hello 下发的 udp.nonce 字节 [4:8]
timestamp / sequence设备自定义可用于算 RTT、去重;平台不解释
payload可选0~若干字节,原样回显;可不加密
设备 → Gateway(Ping)
  [0]=0x02 | flags | payload_len | ssrc | timestamp | sequence | [optional payload]

Gateway → 设备(Pong,字节级原样回显)
  同一数据报完整返回

推荐(设备侧策略,非平台强制):
  · 会话存活期间每 15~20s 发一次(有真实音频上行时可跳过当次)
  · 连续 2~3 次无 Pong(如单次等待 2~3s)→ CloseAudioChannel + 重新 hello
  · 收到 Pong 只更新「UDP 健康」时间戳,勿与 MQTT 超时混用

下行 TTS 音频仍为 type=0x01。设备收包时若收到 type=0x02,应走心跳回调,不要当 Opus 解码。