Files
web-Iot/docs/DOC-10-声网视频接入说明-V1.0.md
2026-09-14 09:27:15 +08:00

199 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 声网视频接入说明 V1.0
| 项目 | 内容 |
|------|------|
| 文档编号 | DOC-10 |
| 文档名称 | 声网视频接入说明 |
| 版本 | V1.0 |
| 状态 | 草案(Draft) |
| 适用系统 | 机器人智慧服务系统(Web 前端) |
| 编写日期 | 2026-09-14 |
| 关联文档 | DOC-08 设备数据字典、DOC-09 控制命令字典、机器人能力模型(`cap.video`) |
---
## 1. 概述
本系统使用声网(Agora)WebRTC SDK 实现设备(机器人上位机 / 无人机)视频画面在 Web 端的实时观看。
- **SDK**:`agora-rtc-sdk-ng`(WebRTC NG SDK),版本 `^4.24.3`(见 `package.json`)
- **角色模型**:设备端(上位机 / 无人机)为推流方(Host/Broadcaster),Web 前端为**纯观看方(Audience)**,只订阅远端流,不本地推流
- **与火山 RTC 的关系**:系统同时接入了火山引擎 RTC(`@volcengine/rtc`)作为主视频通道,声网为第二通道。前端根据后端返回的 `url_type` 字段自动选择播放器(见 §5.2)
### 1.1 涉及代码
| 文件 | 组件 | 用途 |
|------|------|------|
| `src/components/AgoraRtc.tsx` | `AgoraRtcRoom` | 通用观看组件,从后端返回的 URL 中解析 RTC 参数加入频道 |
| `src/components/AgrtcRoomRobot.tsx` | `AgrtcRoomRobot` | 机器人上位机专用观看组件,含自动重连与状态显示 |
| `src/components/devices/DroneVideoPlayer.tsx` | — | 无人机/机场视频组件,按 `url_type` 分流到火山或声网播放器 |
| `src/components/devices/DeviceOverviewPage.tsx` | — | 设备总览页,`hasHost` 设备使用 `AgrtcRoomRobot` |
> 注:`services/` 后端目录中**没有**声网 Token 生成逻辑,当前使用的是声网控制台生成的**临时 Token**(硬编码在前端),详见 §7 安全建议。
## 2. 接入架构
```
┌──────────────┐ 推流(RTC/H264) ┌──────────────┐ 订阅/播放 ┌──────────────────┐
│ 设备端 │ ─────────────────> │ 声网 SD-RTN │ ───────────> │ Web 前端(浏览器) │
│ 上位机/无人机 │ (Host) │ 云网络 │ (Audience) │ AgoraRtc 组件 │
└──────────────┘ └──────────────┘ └──────────────────┘
```
- 设备端按 `App ID + Channel + Token + UID` 加入频道并推流(设备侧实现,不在本仓库内)
- Web 端以 Audience 角色加入**同一频道**,通过 `user-published` 事件自动订阅远端视频/音频
### 2.1 关键参数
| 参数 | 说明 | 来源 |
|------|------|------|
| `appId` | 声网项目 App ID,当前 `7887b4db6033410d9ac3264b40ca9c1a` | 声网控制台 |
| `channel` | 频道名。机器人场景 = 设备 `serialNumber`;无人机场景由后端下发 | 双方约定 |
| `token` | 加入频道的鉴权 Token,有效期有限 | 声网控制台(临时)/ 后端生成(正式) |
| `uid` | 用户 ID。前端随机生成 `Math.floor(Math.random() * 100000)` | 前端 |
## 3. 前置条件
1. 声网账号已注册,并创建项目(鉴权机制建议:**App ID + Token(安全模式)**)
2. `package.json` 已包含依赖(无需额外安装):
```json
"agora-rtc-sdk-ng": "^4.24.3"
```
3. 设备端(上位机)已实现推流,频道名与平台约定一致
4. 浏览器环境要求:Chrome/Edge 等现代浏览器,允许摄像头/自动播放策略(纯观看场景一般无需授权弹窗,但需允许自动播放)
## 4. 组件说明
### 4.1 `AgoraRtcRoom`(通用观看组件)
**文件**:`src/components/AgoraRtc.tsx`
| Props | 类型 | 说明 |
|-------|------|------|
| `url` | `string` | 后端返回的 RTC 播放地址,Query 参数中携带 `app_id` / `channel` / `token` / `uid` |
**URL 参数协议**(由后端拼装返回):
```
https://<任意域名>/?app_id=<AppID>&channel=<频道名>&token=<Token>&uid=<UID>
```
- 四个参数缺任一则组件不发起连接(仅打印日志 `RTC参数不全,无法播放`)
- `uid` 缺省时前端随机生成
**内部行为**:
- `mode: "rtc"`、`codec: "vp8"` 创建客户端
- 监听 `user-published` 自动订阅并播放远端视频(渲染到内部容器),音频直接 `play()`
- 监听 `user-left` 清空画面
- **不推流**(纯观看,未创建本地轨道)
- URL 任一参数变化时**销毁旧实例并整体重连**(调用侧配合 `key={url}` 强制重建组件,避免复用旧 client 导致黑屏)
- 组件卸载时 `removeAllListeners()` + `leave()` 彻底清理
### 4.2 `AgrtcRoomRobot`(机器人上位机专用组件)
**文件**:`src/components/AgrtcRoomRobot.tsx`
| Props | 类型 | 说明 |
|-------|------|------|
| `CHANNEL` | `string` | 频道名,调用处传入 `selectedDevice.serialNumber`(设备序列号) |
**内部行为**:
- `mode: "live"`、`codec: "h264"`,`setClientRole("audience")`
- App ID / Token **硬编码**在组件内(当前为控制台临时 Token)
- **加入频道后主动补订**:遍历 `client.remoteUsers`,对已推流的远端用户手动 `subscribe`(覆盖"观看端后于设备端入会"的场景)
- **自动重连**:监听 `connection-state-change`,状态变为 `DISCONNECTED` 后清空画面并延迟 2 秒重连
- **Token 过期预警**:监听 `token-privilege-will-expire`,弹 `message.warning` 提示(正式环境应调用 `client.renewToken(newToken)`,见 §7)
- 顶部状态栏显示连接状态(RTC 已连接/未连接、已加入/未加入频道、当前频道名)
- 无远端流时显示"视频加载中,等待远端推流..."占位
- 卸载时逐个 `off()` 解绑事件 + `leave()`
- 使用 `joiningRef` 防止并发 join,`OPERATION_ABORTED` 错误(React StrictMode / 快速卸载)静默忽略
### 4.3 使用示例
```tsx
// ① 机器人上位机视频(设备 hasHost 时)
<AgrtcRoomRobot CHANNEL={selectedDevice?.serialNumber || ''} />
// ② 无人机/机场视频(后端返回 streamData.url,按 url_type 分流)
streamData?.url_type === 'volc' ? (
<DroneLivePlayer key={streamData.url} streamData={streamData} />
) : (
<AgoraRtcRoom key={streamData.url} url={streamData.url} />
)
```
## 5. 关键流程
### 5.1 机器人观看时序
```
Web前端 声网SD-RTN 机器人上位机
│ createClient(live/h264) │ │
│ setClientRole(audience) │ │
│──── join(appId,channel,token,uid) ────────┐ │
│ │<────────────┤ │
│ │ (上位机此前已 join + publish)
│──── 遍历 remoteUsers ─────>│ │
│──── subscribe(remoteUser, "video") ───────┤ │
│<─── user-published 事件 ───│ │
│ videoTrack.play(container) │
│ (画面渲染完成) │
```
### 5.2 无人机/机场视频流程(含分流)
1. 前端调用设备接口切换镜头/摄像头:
- `changeCameraPlane({ sn, lensType, cameraIndex, qualityType: "adaptive", videoExpire })` —— 无人机镜头(wide/zoom/ir)
- `changeCameragateway({ sn, cameraIndex, cameraPosition, qualityType, videoExpire })` —— 机场摄像头(indoor/outdoor)
2. 后端返回 `res.data`(`streamData`),包含 `url`、`url_type`
3. `url_type === "volc"` → 火山播放器 `DroneLivePlayer`;否则 → 声网 `AgoraRtcRoom`
4. `AgoraRtcRoom` 解析 `url` 中的 Query 参数,走 §5.1 流程加入并订阅
### 5.3 断线重连(AgrtcRoomRobot)
```
connection-state-change: CONNECTED → DISCONNECTED
│
▼
清空远端画面(setHasRemoteVideo=false) → 2s 延迟 → 重新 join → 补订 remoteUsers
```
## 6. 事件与状态参考
| 事件 | 触发时机 | 前端处理 |
|------|----------|----------|
| `connection-state-change` | 连接状态变化(DISCONNECTED/CONNECTING/CONNECTED/RECONNECTING) | 更新状态徽标;DISCONNECTED 触发重连 |
| `user-published` | 远端用户发布媒体流 | `subscribe` 后 `videoTrack.play()` / `audioTrack.play()` |
| `user-unpublished` | 远端停止推流(如上位机停止推流) | 停止轨道并清空画面 |
| `user-left` | 远端用户离开频道 | 清空画面,显示占位 |
| `user-joined` | 远端用户进入频道 | 仅日志 |
| `token-privilege-will-expire` | Token 即将过期(提前约 30s) | 警告提示;正式环境应 `client.renewToken()` |
## 7. 安全建议(重要)
当前实现存在以下与生产环境的差距,**上线前必须整改**:
1. **Token 硬编码在前端**(`AgrtcRoomRobot.tsx`)。声网临时 Token 有效期短(默认 24 小时内),过期后视频不可用且密钥暴露在浏览器端。应在后端(`services/`)集成声网服务端 SDK,按频道动态生成 Token 并通过接口下发(`AgoraRtcRoom` 的 URL 传参模式即为此设计,建议统一)。
2. **App 证书**:确认声网项目已开启 App Certificate(Token 鉴权),关闭"App ID 鉴权"裸奔模式。
3. **Token 续期**:在 `token-privilege-will-expire` 回调中请求后端换发新 Token 并调用 `client.renewToken(newToken)`,而非仅弹窗提示。
4. **UID 碰撞**:前端 `Math.random()` 生成 UID 有小概率与设备端 UID 或多开页面冲突,建议由后端分配唯一 UID。
## 8. 常见问题排查
| 现象 | 可能原因 | 处理建议 |
|------|----------|----------|
| 黑屏 / 无画面 | client 复用旧实例 | 已通过"每次新建 client + `key` 强制重建组件"规避,新增调用处务必沿用该模式 |
| 加入频道失败 | appId/token/channel 不匹配,或 Token 过期 | 核对三元组;临时 Token 需在声网控制台重新生成 |
| `OPERATION_ABORTED` 报错 | React StrictMode 双执行 / 组件快速卸载 | 代码已静默忽略,属预期行为 |
| 加入成功但无视频 | 设备端未推流,或观看端先于设备端入会 | 已实现 `remoteUsers` 补订;确认设备端 publish 正常 |
| 频繁重连 | 网络波动或 Token 过期 | 查看控制台 `连接状态变更` 日志;Token 过期会周期性 DISCONNECTED |
| 视频卡顿 | 编码/网络质量 | 检查 `qualityType: "adaptive"` 自适应档位;确认上行带宽 |
## 9. 版本记录
| 版本 | 日期 | 说明 |
|------|------|------|
| V1.0 | 2026-09-14 | 初稿:梳理现有 AgoraRtc / AgrtcRoomRobot 组件、参数协议、重连机制与安全建议 |