# 传输协议

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

  1. 创建下行通道流,你的设备需要在连接开启后的 10 秒内/v20180810/directives 发出一个 GET 请求,类似这样:
:method = GET
:scheme = https
:path = /v20180810/directives
authorization = Bearer {YOUR_ACCESS_TOKEN}

如果请求成功,这个连接会维持在半开状态。

  1. 下行通道建立成功之后,你的设备应当向 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 开始响应你的上一个请求(设备收到了响应头)之后才发出新的请求。

  1. 你的产品打开一个流并发出一个 Multipart 消息,包含一个 JSON 格式的事件体和最多一个二进制音频附件。详情请看消息格式
  2. IVS 在同一个流里返回一个 Multipart 消息,包含一个或多个 JSON 格式的指令和对应的音频附件。其中 PlaySpeak 指令里 cid: 开头的 url 对应音频附件里的头。
  3. 接收响应之后,关闭该事件流。

注意

你的设备可能会收到多个指令(JSON)之后才收到对应的音频附件。因此,你的设备应当能够关联指令和它对应的音频附件。

# 下行通道生命周期

一些指令也可能会并行于事件流地从下行通道发送到你的设备。下行通道通常用于接收云端发起的指令。

  1. 连接到 IVS 之后的 10 秒内应该向 directives 路径发起一个 GET 请求。
  2. 下行通道流用于向你的设备发送云端发起的指令和附件,比如说计时器、闹钟,以及通过 App 远程发起的操作。和事件流不同,下行通道流不应立刻关闭,而应该维持长时间的半开状态。
  3. 当下行通道被关闭,你的设备应该立刻重新建立新的下行通道,以确保及时接收云端指令。

# 心跳和超时

你的设备应该采取下列其中一个措施来维持连接。遇到失败后需要关闭连接并重试。

  • 连接空闲时每 5 分钟 向 IVS 发送一个 PING
  • 连接空闲时每 5 分钟/ping 接口发出一个 GET 请求

# 示例

:method = GET
:scheme = https
:path = /ping
authorization = Bearer {YOUR_ACCESS_TOKEN}

一旦 PING 失败,设备应当关闭当前连接,并立刻重新创建一个新的连接。

注意

如果使用 libcurl,你的设备需要每 5 分钟向 /ping 发一次 GET 来维持连接。

# 服务端主动断开连接

当服务器主动断开了连接,你的设备应该:

  1. 开启一个新的连接,并将所有新的请求移到这个连接上进行
  2. 在所有已开启的请求(以及他们对应的流)都处理完毕之后关闭旧的连接。
  3. 维持连接关闭前已经打开的流播 URL(比如音乐播放),直到播放结束

如果重新连接失败,设备应该采取适当的间隔重试机制。