# 传输协议
IVS 通过 HTTP/2 长连接向客户端下发由云端发起的指令,比如闹钟、媒体播放控制等。本章将介绍如何创建和维护与 IVS 之间的 HTTP/2 连接。
# 关键概念
- 帧: HTTP/2 协议的基本单位。每一帧都代表了不同的用途,比如 HEADERS 和 DATA 帧组成了基本的 HTTP 请求和返回。
- 流: 在同一个 HTTP/2 连接内,设备与服务器交换的一系列独立、双向的帧序列。详情参阅 RFC 7540 中的流和多路复用 (opens new window)。
- 接口: IVS 向你的产品提供的访问 IVS 内建技能的接口(比如 SpeechRecognizer、AudioPlayer 等)。
- 下行通道: 你在你的 HTTP/2 连接中建立的一个用于从云端向你的设备推送指令的流。下行通道由设备建立,保持半开状态。主要用于接收从云端发出的指令。
- 云端指令: 由云端直接向你的设备发起的指令。比如当用户通过手机 App 调整设备的音量时,云端将直接通过下行通道向设备推送调整音量的指令(而不是通过语音请求)。
注意
你的设备在同一个连接内应当只建立一条下行通道。
# 要求
要和 IVS 建立 HTTP/2 连接,你的产品需要对接 IVS 的授权接口以取得 Access Token。
所有请求头都应当包含 Access Token,类似于:
:method = POST
:scheme = https
:path = /v20180810/events
authorization = Bearer {YOUR_ACCESS_TOKEN}
content-type = multipart/form-data; boundary={BOUNDARY_TERM}
其中,v20180810
为当前受支持的 IVS API 版本。IVS 保证在同一个 API 版本下所使用的事件和指令遵循一致的格式。
# 接口地址
https://ivs.iflyos.cn
# 创建 HTTP/2 连接
你的产品开机后应当和 IVS 建立 单条 HTTP/2 连接。该连接用于处理所有指令和事件,包括通过下行通道流向你的设备推送的所有指令。
与 IVS 维持连接有两点要求:
注意
仅当你的设备需要使用云端唤醒词验证时才需要 recognizer_state
- 创建下行通道流,你的设备需要在连接开启后的 10 秒内向
/v20180810/directives
发出一个GET
请求,类似这样:
:method = GET
:scheme = https
:path = /v20180810/directives
authorization = Bearer {YOUR_ACCESS_TOKEN}
如果请求成功,这个连接会维持在半开状态。
- 下行通道建立成功之后,你的设备应当向 IVS 同步自己的组件状态。做法是在现有的连接中( 注意: 不要开新连接)发起一个新的事件流向
/v20180810/events
发出一个POST
请求。该事件流应当在收到响应之后关闭。下面是一个SynchronizeState
事件的例子:
:method = POST
:scheme = https
:path = /v20180810/events
authorization = Bearer {YOUR_ACCESS_TOKEN}
content-type = multipart/form-data; boundary={BOUNDARY_TERM_HERE}
--{BOUNDARY_TERM_HERE}
Content-Disposition: form-data; name="metadata"
Content-Type: application/json; charset=UTF-8
{
"context": [
// 上下文对象的数组,用于告知 IVS 设备端所有组件的状态。详见上下文章节。
],
"event": {
"header": {
"namespace": "System",
"name": "SynchronizeState",
"messageId": "{STRING}"
},
"payload": {
}
}
}
--{BOUNDARY_TERM_HERE}--
状态同步之后,你的设备即可利用这个连接来:
- 向 IVS 发送事件并接收指令响应
- 从下行通道接收云端发起的指令
注意
每个事件和它的响应都在自己独立的事件流中发送;响应接收完毕之后应该将流关闭。
# 维持 HTTP/2 连接
一旦连接建立,设备需要知道如何管理事件流、下行通道流、心跳、超时和云端发起的断开。
# 需要注意的点
我们建议使用 10ms、320 字节分片(每次发送 320 字节的 DATA 帧)、流式地向 IVS 传输捕获到的音频。过大的分片可能会导致不必要的缓冲,导致延迟增加。
向 IVS 发送的音频编码:
- 16bit 线性 PCM(LPCM16)
- 16kHz 采样率
- 单声道
- Little endian 字节序
详见语音识别接口。
单个 HTTP/2 连接仅支持 10 个并发的流,包括事件流、下行通道和心跳。再次提醒,事件流收到响应之后应该关闭。
大多数请求库对读取时间可能会有超时机制。由于 IVS 的下行通道流是一个长连接,在相当长一段时间内即便没有指令下发也应当维持开启状态。因此需要将读取超时设置为至少 60 分钟。
# 事件流生命周期
每一个事件都使用自己独立的流发送。通常情况下一条流会在当 IVS 返回指令和对应的音频附件之后关闭。
请求应该是顺序处理的。也就是说,应该在 IVS 开始响应你的上一个请求(设备收到了响应头)之后才发出新的请求。
- 你的产品打开一个流并发出一个 Multipart 消息,包含一个 JSON 格式的事件体和最多一个二进制音频附件。详情请看消息格式。
- IVS 在同一个流里返回一个 Multipart 消息,包含一个或多个 JSON 格式的指令和对应的音频附件。其中
Play
或Speak
指令里cid:
开头的 url 对应音频附件里的头。 - 接收响应之后,关闭该事件流。
注意
你的设备可能会收到多个指令(JSON)之后才收到对应的音频附件。因此,你的设备应当能够关联指令和它对应的音频附件。
# 下行通道生命周期
一些指令也可能会并行于事件流地从下行通道发送到你的设备。下行通道通常用于接收云端发起的指令。
- 连接到 IVS 之后的 10 秒内应该向 directives 路径发起一个
GET
请求。 - 下行通道流用于向你的设备发送云端发起的指令和附件,比如说计时器、闹钟,以及通过 App 远程发起的操作。和事件流不同,下行通道流不应立刻关闭,而应该维持长时间的半开状态。
- 当下行通道被关闭,你的设备应该立刻重新建立新的下行通道,以确保及时接收云端指令。
# 心跳和超时
你的设备应该采取下列其中一个措施来维持连接。遇到失败后需要关闭连接并重试。
- 连接空闲时每 5 分钟 向 IVS 发送一个
PING
帧 - 连接空闲时每 5 分钟 向
/ping
接口发出一个GET
请求
# 示例
:method = GET
:scheme = https
:path = /ping
authorization = Bearer {YOUR_ACCESS_TOKEN}
一旦 PING 失败,设备应当关闭当前连接,并立刻重新创建一个新的连接。
注意
如果使用 libcurl,你的设备需要每 5 分钟向 /ping
发一次 GET
来维持连接。
# 服务端主动断开连接
当服务器主动断开了连接,你的设备应该:
- 开启一个新的连接,并将所有新的请求移到这个连接上进行
- 在所有已开启的请求(以及他们对应的流)都处理完毕之后关闭旧的连接。
- 维持连接关闭前已经打开的流播 URL(比如音乐播放),直到播放结束
如果重新连接失败,设备应该采取适当的间隔重试机制。