321 lines
13 KiB
Markdown
321 lines
13 KiB
Markdown
# IM 声网音视频通话接入文档
|
||
|
||
更新时间:2026-09-01
|
||
|
||
## 1. 接入目标
|
||
|
||
本次接入目标是把 IM 的一对一语音/视频通话媒体能力从原 WebRTC 直连方案切到声网 RTC。IM Server 继续负责账号、租户、好友/黑名单、通话状态、WebSocket 信令和通话记录;声网只负责音视频媒体频道、音视频流传输和后续可选的媒体质量能力。
|
||
|
||
首期按声网调试账号接入,只使用 `app_id`,不生成 RTC Token。正式上线时再启用 App 证书,由 IM Server 按频道和用户生成 RTC Token。
|
||
|
||
## 2. 系统边界
|
||
|
||
| 模块 | 责任 |
|
||
| --- | --- |
|
||
| IM Server | 校验登录态、租户、好友/黑名单和通话权限;生成 `call_id`、声网频道名、声网 UID;维护通话状态和通话记录;通过 WebSocket 下发邀请、接听、挂断等信令。 |
|
||
| 前端客户端 | 集成声网 RTC SDK;从 IM Server 获取声网入会配置;使用 `app_id/channel_name/uid/token` 加入声网频道;通过 IM WebSocket 发送通话业务事件。 |
|
||
| HotGo/Admin | 保存声网第三方配置;后续可提供项目、证书、开关等管理入口。 |
|
||
| 声网 RTC | 提供音视频实时媒体通道。 |
|
||
| 声网控制台 REST API | 可选,用于服务端创建项目、查询项目、启停项目、启停或重置主要 App 证书。首期不强依赖。 |
|
||
|
||
## 3. 声网账号与配置准备
|
||
|
||
### 3.1 调试模式必须准备
|
||
|
||
| 配置 | 说明 |
|
||
| --- | --- |
|
||
| `agora_app_id` | 声网项目 App ID,客户端初始化 RTC SDK 使用。 |
|
||
| `agora_auth_mode` | 当前建议填 `token`,由服务端生成 RTC Token。 |
|
||
| `agora_enabled` | 是否启用声网通话,建议测试环境先设为 `true`。 |
|
||
| `agora_uid_mode` | 建议用 `string`,客户端加入频道时使用 `usr...` 作为声网 user account,避免 BIGINT 用户 ID 超过声网数字 UID 范围。 |
|
||
| `agora_channel_prefix` | 建议 `call_`,最终频道名为 `call_{call_id}`。 |
|
||
|
||
### 3.2 正式模式后续准备
|
||
|
||
| 配置 | 说明 |
|
||
| --- | --- |
|
||
| `agora_app_certificate` | 声网 App 证书,服务端生成 RTC Token 使用,禁止下发客户端。 |
|
||
| `agora_token_ttl_seconds` | RTC Token 有效期,建议 3600 秒。 |
|
||
| `agora_rest_customer_id` | 声网 RESTful API 客户 ID,用于控制台 API。 |
|
||
| `agora_rest_customer_secret` | 声网 RESTful API 客户密钥,用于控制台 API,必须脱敏保存和展示。 |
|
||
| `agora_rest_primary_domain` | 国内建议 `https://api.sd-rtn.com`。 |
|
||
| `agora_rest_backup_domain` | 备用建议 `https://api.agora.io`。 |
|
||
|
||
配置建议存放在 HotGo `hg_sys_config` 的 `im_shengwang` 分组,IM Server 通过 `store.ThirdPartyConfig.Get("im_shengwang", ...)` 读取,并受 Kafka `third_party.updated` 或 `cache.third_party.invalidate` 事件清理进程内缓存。
|
||
|
||
## 4. 后端与声网 API 对接
|
||
|
||
### 4.1 首期不调用声网 REST API
|
||
|
||
只使用 `app_id` 的调试模式下,IM Server 不需要向声网发起服务端请求。后端只需要读取后台配置,把 `app_id`、频道名、UID 和空 Token 返回给客户端。
|
||
|
||
首期后端返回:
|
||
|
||
```json
|
||
{
|
||
"provider": "agora",
|
||
"auth_mode": "app_id",
|
||
"app_id": "声网 App ID",
|
||
"channel_name": "call_call_01K...",
|
||
"uid": 1565659909440147456,
|
||
"token": "",
|
||
"token_expires_at": null
|
||
}
|
||
```
|
||
|
||
### 4.2 正式 Token 模式
|
||
|
||
正式模式启用 App 证书后,IM Server 使用 `agora_app_id + agora_app_certificate + channel_name + uid + ttl` 在本地生成 RTC Token。Token 只返回给当前登录用户,不能存日志,不能返回给非通话参与人。
|
||
|
||
正式后端返回:
|
||
|
||
```json
|
||
{
|
||
"provider": "agora",
|
||
"auth_mode": "token",
|
||
"app_id": "声网 App ID",
|
||
"channel_name": "call_call_01K...",
|
||
"uid": 1565659909440147456,
|
||
"token": "007eJx...",
|
||
"token_expires_at": "2026-09-01T17:00:00+08:00"
|
||
}
|
||
```
|
||
|
||
客户端收到声网 SDK 的 Token 即将过期回调后,重新调用 IM Server 获取新 Token,再调用声网 SDK 的 `renewToken` 或等价接口更新。
|
||
|
||
### 4.3 声网控制台 REST API
|
||
|
||
控制台 REST API 是后台运维能力,不是每通电话的实时链路。需要自动化管理声网项目时再接入。
|
||
|
||
声网 REST API 鉴权:
|
||
|
||
```text
|
||
Authorization: Basic base64(customer_id + ":" + customer_secret)
|
||
Content-Type: application/json
|
||
```
|
||
|
||
常用接口:
|
||
|
||
| 能力 | 方法与路径 | 说明 |
|
||
| --- | --- | --- |
|
||
| 创建项目 | `POST /dev/v1/project` | Body `name, enable_sign_key`;返回 `vendor_key`,即 App ID。 |
|
||
| 查询指定项目 | `GET /dev/v1/project?id=&name=` | 按项目 ID 和名称查询。 |
|
||
| 查询全部项目 | `GET /dev/v1/projects` | 获取当前声网账号下项目列表。 |
|
||
| 启用/禁用项目 | `POST /dev/v1/project_status` | Body `id, status`,`1` 启用,`0` 禁用。 |
|
||
| 启用/禁用主要 App 证书 | `POST /dev/v1/signkey` | Body `id, enable`。 |
|
||
| 重置主要 App 证书 | `POST /dev/v1/reset_signkey` | Body `id`;证书未启用时会启用主要证书。 |
|
||
|
||
调用约束:
|
||
|
||
- 成功 HTTP 状态码为 `200` 或 `201`。
|
||
- 失败响应通常包含 `statusCode/error/message`。
|
||
- 每个声网账号 REST API QPS 默认 10 次/秒。
|
||
- 请求失败时日志记录声网响应 Header `X-Request-ID`、`X-Resource-ID`,但不能记录客户密钥、App 证书或 RTC Token。
|
||
- 国内服务优先 `api.sd-rtn.com`,失败时按退避策略切换 `api.agora.io` 或区域域名。
|
||
|
||
## 5. 前端与后端对接
|
||
|
||
### 5.1 WebSocket 通话信令
|
||
|
||
现有 IM WebSocket 仍是通话业务信令主链路。客户端发起通话时发布一条 `x-im-type=call` 的 Topic 消息:
|
||
|
||
```json
|
||
{
|
||
"pub": {
|
||
"id": "call-pub-1",
|
||
"topic": "p2p...",
|
||
"head": {
|
||
"mime": "application/json",
|
||
"webrtc": "started",
|
||
"x-im-type": "call",
|
||
"x-im-client-mid": "mid_call_1"
|
||
},
|
||
"content": {
|
||
"callId": "call_01K...",
|
||
"callType": "audio",
|
||
"audio": true,
|
||
"video": false,
|
||
"provider": "agora"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
服务端返回:
|
||
|
||
```json
|
||
{
|
||
"ctrl": {
|
||
"id": "call-pub-1",
|
||
"code": 202,
|
||
"text": "accepted",
|
||
"params": {
|
||
"seq": 30
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
后续 `ringing/accept/hang-up` 继续走 `{note what="call"}`。切到声网后,`offer/answer/ice-candidate` 不再需要由客户端发送给 IM Server;媒体协商由声网 SDK 自己完成。为了兼容旧客户端,服务端可以继续接受这些事件,但新客户端应忽略旧 WebRTC SDP/ICE 流程。
|
||
|
||
### 5.2 获取声网入会配置
|
||
|
||
建议新增产品接口:
|
||
|
||
```http
|
||
POST /v1/im/calls/rtc-join
|
||
Authorization: Bearer <IM token>
|
||
X-MeetingIM-APIKey: <api_key>
|
||
X-IM-Schema: 1
|
||
X-Request-ID: <request_id>
|
||
Idempotency-Key: <idempotency_key>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
请求 Body:
|
||
|
||
```json
|
||
{
|
||
"topic": "p2p...",
|
||
"call_id": "call_01K...",
|
||
"seq": 30,
|
||
"call_type": "audio",
|
||
"role": "caller"
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
| --- | --- | --- |
|
||
| `topic` | 是 | P2P Topic。首期只支持一对一通话。 |
|
||
| `call_id` | 是 | 通话 ID,必须和 WebSocket call 消息一致。 |
|
||
| `seq` | 建议 | 通话消息 seq。发起方可在 `{pub}` 返回后传入;接听方从收到的 call 消息中取得。 |
|
||
| `call_type` | 是 | `audio` 或 `video`。 |
|
||
| `role` | 是 | `caller` 或 `callee`,用于审计和风控。 |
|
||
|
||
成功响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"data": {
|
||
"provider": "agora",
|
||
"auth_mode": "app_id",
|
||
"app_id": "声网 App ID",
|
||
"channel_name": "call_call_01K...",
|
||
"uid": 1565659909440147456,
|
||
"token": "",
|
||
"token_expires_at": null,
|
||
"call_id": "call_01K...",
|
||
"topic": "p2p...",
|
||
"seq": 30
|
||
},
|
||
"request_id": "req_01K...",
|
||
"server_time": "2026-09-01T16:00:00+08:00"
|
||
}
|
||
```
|
||
|
||
失败原因建议:
|
||
|
||
| reason | 场景 |
|
||
| --- | --- |
|
||
| `AGORA_DISABLED` | 后台未开启声网。 |
|
||
| `AGORA_CONFIG_MISSING` | 缺少 App ID,或正式模式缺少 App 证书。 |
|
||
| `CALL_NOT_FOUND` | `call_id/topic/seq` 不存在或不匹配。 |
|
||
| `CALL_NOT_ALLOWED` | 用户不是通话参与人,或被好友/黑名单/租户策略限制。 |
|
||
| `CALL_STATE_INVALID` | 通话已结束、已超时或状态不允许入会。 |
|
||
| `VALIDATION_FAILED` | 请求字段非法。 |
|
||
|
||
### 5.3 客户端发起通话流程
|
||
|
||
```text
|
||
用户点击语音/视频通话
|
||
-> 客户端生成 call_id
|
||
-> WS pub call started
|
||
-> 收到 ctrl 202 和 seq
|
||
-> 调 POST /v1/im/calls/rtc-join 获取声网入会参数
|
||
-> 使用声网 SDK joinChannel
|
||
-> 等待对方 ringing/accept
|
||
-> 通话中通过 IM note hang-up 结束业务状态
|
||
```
|
||
|
||
### 5.4 客户端接听通话流程
|
||
|
||
```text
|
||
收到 Topic call started data
|
||
-> 展示来电 UI
|
||
-> 用户点击接听
|
||
-> 调 POST /v1/im/calls/rtc-join 获取声网入会参数
|
||
-> 使用声网 SDK joinChannel
|
||
-> WS note call accept
|
||
-> 通话中通过 IM note hang-up 结束业务状态
|
||
```
|
||
|
||
### 5.5 客户端挂断/取消/拒接
|
||
|
||
客户端通过 IM WebSocket 发送:
|
||
|
||
```json
|
||
{
|
||
"note": {
|
||
"topic": "p2p...",
|
||
"what": "call",
|
||
"event": "hang-up",
|
||
"seq": 30,
|
||
"payload": {
|
||
"callId": "call_01K...",
|
||
"reason": "cancelled"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
服务端按当前状态把原通话消息替换为 `finished/missed/declined/disconnected`,并更新 `im_call_records`。客户端以服务端下发的替换消息为准,不相信本地计算的最终时长。
|
||
|
||
## 6. 数据与安全要求
|
||
|
||
1. `agora_app_certificate`、`agora_rest_customer_secret`、RTC Token 都是敏感信息,不写普通日志、不下发给无关用户、不进入埋点。
|
||
2. 调试模式可以只用 App ID,但生产环境建议启用 App 证书和 RTC Token。
|
||
3. `channel_name` 必须由服务端规则生成,建议 `call_{call_id}`,客户端不能自定义任意声网频道。
|
||
4. `uid` 必须稳定,首期建议使用当前 IM 用户数据库 BIGINT ID;如果客户端 SDK 使用字符串 UID,后端和客户端必须同时切到 string 模式。
|
||
5. 入会接口必须校验当前用户属于该 P2P 通话双方之一。
|
||
6. 被拉黑、非好友、账号禁用、租户禁用、青少年模式限制等场景必须拒绝发起或接听。
|
||
|
||
## 7. 联调清单
|
||
|
||
| 编号 | 场景 | 预期 |
|
||
| --- | --- | --- |
|
||
| AGORA-01 | 后台配置 `agora_app_id`、`agora_app_certificate` 和 `agora_auth_mode=token` | `/v1/im/calls/rtc-join` 返回 App ID、频道名、UID 和 RTC Token。 |
|
||
| AGORA-02 | A 发起语音通话,B 接听 | 双方使用同一 `channel_name` 加入声网频道,IM 通话状态变为 `accepted`。 |
|
||
| AGORA-03 | A 发起视频通话,B 接听 | 双方可看到/听到媒体流,IM 通话记录为 `video`。 |
|
||
| AGORA-04 | B 拒接 | IM 替换原通话消息为 `declined`,双方退出声网频道。 |
|
||
| AGORA-05 | A 取消 | IM 替换原通话消息为 `missed` 或取消态,B 来电 UI 关闭。 |
|
||
| AGORA-06 | 通话中任一方挂断 | IM 以服务端计时生成 `finished` 和 `duration_ms`。 |
|
||
| AGORA-07 | 非参与人请求入会配置 | 返回 `CALL_NOT_ALLOWED`。 |
|
||
| AGORA-08 | 关闭 `agora_enabled` | 入会接口返回 `AGORA_DISABLED`。 |
|
||
| AGORA-09 | 正式 Token 模式缺少 App 证书 | 入会接口返回 `AGORA_CONFIG_MISSING`。 |
|
||
| AGORA-10 | Token 即将过期刷新 | 正式模式下客户端重新获取 Token 并成功 `renewToken`。 |
|
||
|
||
## 8. 后续增强
|
||
|
||
1. 接入 RTC Token 生成器,支持 App 证书正式模式。
|
||
2. 后台增加声网配置页和敏感字段脱敏。
|
||
3. 接入声网控制台 REST API,实现项目查询、证书启停和重置。
|
||
4. 记录声网频道、UID、入会时间、离会时间和错误码,便于排障。
|
||
5. 采集通话质量指标:建连耗时、首帧耗时、丢包、延迟、卡顿、断线重连。
|
||
6. 后续如需多人通话,单独设计会议房间和 SFU/RTC 房间状态,不复用 P2P `call_id` 状态机。
|
||
|
||
## 9. 参考文档
|
||
|
||
- 声网创建项目:https://doc.shengwang.cn/doc/console/general/console/operations/post-dev-v1-project
|
||
- 声网获取指定项目信息:https://doc.shengwang.cn/doc/console/general/console/operations/get-dev-v1-project
|
||
- 声网获取所有项目信息:https://doc.shengwang.cn/doc/console/general/console/operations/get-dev-v1-projects
|
||
- 声网启用或禁用项目:https://doc.shengwang.cn/doc/console/general/console/operations/post-dev-v1-project_status
|
||
- 声网启用或禁用主要 App 证书:https://doc.shengwang.cn/doc/console/general/console/operations/post-dev-v1-signkey
|
||
- 声网重置主要 App 证书:https://doc.shengwang.cn/doc/console/general/console/operations/post-dev-v1-reset_signkey
|
||
- 声网响应状态码:https://doc.shengwang.cn/doc/console/general/api/response-code
|
||
- 声网配额限制:https://doc.shengwang.cn/doc/console/general/api/quota
|
||
- 声网 REST 高可用:https://doc.shengwang.cn/doc/console/general/api/guarantee-rest
|
||
- 声网 RTC Token 鉴权:https://doc.shengwang.cn/doc/rtc/android/basic-features/token-authentication
|