11 KiB
11 KiB
声网视频接入说明 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. 前置条件
-
声网账号已注册,并创建项目(鉴权机制建议:App ID + Token(安全模式))
-
package.json已包含依赖(无需额外安装):"agora-rtc-sdk-ng": "^4.24.3" -
设备端(上位机)已实现推流,频道名与平台约定一致
-
浏览器环境要求: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 使用示例
// ① 机器人上位机视频(设备 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 无人机/机场视频流程(含分流)
- 前端调用设备接口切换镜头/摄像头:
changeCameraPlane({ sn, lensType, cameraIndex, qualityType: "adaptive", videoExpire })—— 无人机镜头(wide/zoom/ir)changeCameragateway({ sn, cameraIndex, cameraPosition, qualityType, videoExpire })—— 机场摄像头(indoor/outdoor)
- 后端返回
res.data(streamData),包含url、url_type url_type === "volc"→ 火山播放器DroneLivePlayer;否则 → 声网AgoraRtcRoomAgoraRtcRoom解析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. 安全建议(重要)
当前实现存在以下与生产环境的差距,上线前必须整改:
- Token 硬编码在前端(
AgrtcRoomRobot.tsx)。声网临时 Token 有效期短(默认 24 小时内),过期后视频不可用且密钥暴露在浏览器端。应在后端(services/)集成声网服务端 SDK,按频道动态生成 Token 并通过接口下发(AgoraRtcRoom的 URL 传参模式即为此设计,建议统一)。 - App 证书:确认声网项目已开启 App Certificate(Token 鉴权),关闭"App ID 鉴权"裸奔模式。
- Token 续期:在
token-privilege-will-expire回调中请求后端换发新 Token 并调用client.renewToken(newToken),而非仅弹窗提示。 - 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 组件、参数协议、重连机制与安全建议 |