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

11 KiB
Raw Permalink Blame History

声网视频接入说明 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 已包含依赖(无需额外安装):

    "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 使用示例

// ① 机器人上位机视频(设备 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 组件、参数协议、重连机制与安全建议