16 KiB
16 KiB
WebRTC 实现
**本文引用的文件** - [src/index.ts](file://src/index.ts) - [src/signaling.ts](file://src/signaling.ts) - [src/class/websockethandler.ts](file://src/class/websockethandler.ts) - [src/class/httphandler.ts](file://src/class/httphandler.ts) - [client/src/peer.js](file://client/src/peer.js) - [client/src/renderstreaming.js](file://client/src/renderstreaming.js) - [client/src/signaling.js](file://client/src/signaling.js) - [client/src/inputremoting.js](file://client/src/inputremoting.js) - [client/src/inputdevice.js](file://client/src/inputdevice.js) - [client/src/sender.js](file://client/src/sender.js) - [client/src/logger.js](file://client/src/logger.js) - [src/log.ts](file://src/log.ts) - [package.json](file://package.json) - [client/test/peerconnection.test.js](file://client/test/peerconnection.test.js) - [client/test/inputremoting.test.js](file://client/test/inputremoting.test.js)目录
简介
本项目是一个基于 WebRTC 的远程渲染与输入控制解决方案,涵盖以下能力:
- PeerConnection 管理与控制:连接建立、媒体协商、连接状态监控
- 输入设备远程控制:鼠标、键盘、触摸屏、手柄事件的采集、编码与转发
- 渲染流处理:媒体流接收、解码与显示
- 信令客户端:HTTP 轮询与 WebSocket 两种信令协议,消息路由与广播
- 性能优化与最佳实践:连接稳定性、资源管理、事件驱动架构
- 测试与调试:单元测试覆盖、日志与调试开关
项目结构
后端采用 TypeScript + Express + WebSocket,前端采用原生 JavaScript,核心模块分布如下:
- 后端入口与配置:启动参数解析、HTTPS/HTTP 选择、WebSocket 信令服务初始化
- 信令路由:HTTP 轮询接口与 WebSocket 通道
- 信令处理器:WebSocket 与 HTTP 两类处理器,负责消息分发、连接组管理、广播
- 前端渲染与信令:RenderStreaming 控制器、PeerConnection 管理、输入远程控制链路
- 输入系统:设备抽象、事件状态编码、消息打包与发送观察者
graph TB
subgraph "后端"
A["Express 应用<br/>src/index.ts"]
B["HTTP 信令路由<br/>src/signaling.ts"]
C["WebSocket 处理器<br/>src/class/websockethandler.ts"]
D["HTTP 处理器<br/>src/class/httphandler.ts"]
end
subgraph "前端"
E["RenderStreaming 控制器<br/>client/src/renderstreaming.js"]
F["PeerConnection 管理<br/>client/src/peer.js"]
G["信令客户端(HTTP/WebSocket)<br/>client/src/signaling.js"]
H["输入远程控制链路<br/>client/src/sender.js + inputremoting.js + inputdevice.js"]
end
A --> B
A --> C
A --> D
E --> F
E --> G
E --> H
图表来源
- src/index.ts:13-109
- src/signaling.ts:1-25
- src/class/websockethandler.ts:1-479
- src/class/httphandler.ts:1-800
- client/src/renderstreaming.js:11-317
- client/src/peer.js:3-188
- client/src/signaling.js:3-292
- client/src/sender.js:14-209
- client/src/inputremoting.js:9-300
- client/src/inputdevice.js:40-719
章节来源
- src/index.ts:13-109
- src/signaling.ts:1-25
- src/class/websockethandler.ts:1-479
- src/class/httphandler.ts:1-800
- client/src/renderstreaming.js:11-317
- client/src/peer.js:3-188
- client/src/signaling.js:3-292
- client/src/sender.js:14-209
- client/src/inputremoting.js:9-300
- client/src/inputdevice.js:40-719
核心组件
- 后端启动与配置:命令行参数解析、HTTPS/HTTP 选择、WebSocket 信令服务启动
- 信令路由与中间件:HTTP 路由定义、会话校验中间件、消息聚合接口
- WebSocket 与 HTTP 处理器:连接组管理、消息广播、参与者加入/离开、心跳与超时
- 前端渲染控制器:连接生命周期管理、Offer/Answer/Candidate 分发、统计与数据通道
- PeerConnection 管理:SDP 描述协商、ICE 候选收集与注入、事件派发
- 输入远程控制:设备事件采集、状态编码、消息打包与发送观察者
- 日志系统:前后端日志级别控制与格式化输出
章节来源
- src/index.ts:13-109
- src/signaling.ts:6-24
- src/class/websockethandler.ts:10-137
- src/class/httphandler.ts:128-145
- client/src/renderstreaming.js:11-317
- client/src/peer.js:3-188
- client/src/inputremoting.js:9-300
- client/src/inputdevice.js:40-719
- client/src/logger.js:1-30
- src/log.ts:1-51
架构总览
系统采用“前端渲染 + 后端信令 + 媒体直连”的架构:
- 前端通过 HTTP 或 WebSocket 与后端进行信令交互,完成连接建立与媒体协商
- 媒体流在浏览器间直接传输,信令仅传递 SDP 与 ICE 候选
- 输入事件在前端采集后编码并通过 RTCDataChannel 或自定义消息通道发送至远端
sequenceDiagram
participant Host as "主机(Host)"
participant Server as "信令服务器"
participant Participant as "参与者(Participant)"
Host->>Server : "创建会话/连接"
Note over Host,Server : "HTTP/WebSocket 信令握手"
Host->>Server : "发送 Offer"
Server-->>Participant : "转发 Offer"
Participant->>Server : "发送 Answer"
Server-->>Host : "转发 Answer"
Host->>Server : "发送 ICE Candidate"
Server-->>Participant : "转发 Candidate"
Participant->>Server : "发送 ICE Candidate"
Server-->>Host : "转发 Candidate"
Note over Host,Participant : "媒体流直连传输"
图表来源
- src/class/websockethandler.ts:145-338
- src/class/httphandler.ts:492-641
- client/src/renderstreaming.js:72-147
章节来源
- src/class/websockethandler.ts:145-338
- src/class/httphandler.ts:492-641
- client/src/renderstreaming.js:72-147
详细组件分析
PeerConnection 管理与控制
- 事件绑定:track、datachannel、icecandidate、negotiationneeded、signalingstatechange、iceconnectionstatechange、icegatheringstatechange
- 协商流程:在 negotiationneeded 触发时设置本地描述并派发 offer;收到远端 offer 后设置远端描述并生成 answer
- ICE 候选:收集到候选时派发候选事件,等待远端注入
- 状态监控:失败时派发 disconnect 事件,便于上层重连或降级
- 统计与控制:支持 getStats、addTrack/addTransceiver、createDataChannel 等
sequenceDiagram
participant Peer as "Peer(前端)"
participant Signaling as "信令客户端"
participant Remote as "远端Peer"
Peer->>Peer : "negotiationneeded 触发"
Peer->>Peer : "setLocalDescription()"
Peer-->>Signaling : "派发 sendoffer"
Signaling-->>Remote : "发送 Offer"
Remote->>Remote : "setRemoteDescription(Offer)"
Remote->>Remote : "setLocalDescription(Answer)"
Remote-->>Signaling : "派发 sendanswer"
Signaling-->>Peer : "发送 Answer"
Peer->>Peer : "setRemoteDescription(Answer)"
Peer-->>Peer : "触发 negotiated 事件"
图表来源
章节来源
- client/src/peer.js:3-188
- client/src/renderstreaming.js:72-130
- client/test/peerconnection.test.js:1-251
输入设备远程控制
- 设备抽象:Mouse、Keyboard、Touchscreen、Gamepad 四类设备,统一状态接口
- 事件采集:鼠标/键盘/触摸/手柄事件监听与状态更新
- 状态编码:各设备状态转为二进制缓冲区,包含格式标识、尺寸、数据
- 消息打包:StateEvent/TextEvent 包装基础事件与状态数据
- 发送链路:InputRemoting 订阅观察者,将消息序列化后通过 RTCDataChannel 或自定义通道发送
flowchart TD
Start(["用户输入事件"]) --> Queue["设备队列更新状态"]
Queue --> Encode["编码为 IInputState 缓冲区"]
Encode --> Wrap["包装为 StateEvent/TextEvent"]
Wrap --> Pack["封装为 Message(含 participant_id/type/length/data)"]
Pack --> Send["通过观察者发送(如 RTCDataChannel)"]
Send --> End(["远端接收并应用"])
图表来源
章节来源
- client/src/inputdevice.js:91-719
- client/src/inputremoting.js:63-300
- client/src/sender.js:14-209
- client/test/inputremoting.test.js:1-132
渲染流处理
- 渲染控制器:根据角色(host/participant)创建/复用 Peer,订阅 trackevent/adddatachannel/onmessage 等事件
- 媒体流接收:trackevent 中获取远端媒体轨道,绑定到视频元素进行播放
- 数据通道:adddatachannel 事件用于建立输入控制通道或其他应用通道
- 统计与诊断:getStats 接口用于网络质量与传输指标观测
sequenceDiagram
participant RS as "RenderStreaming"
participant Peer as "Peer"
participant Video as "视频元素"
RS->>Peer : "创建/复用 Peer 并绑定事件"
Peer-->>RS : "trackevent(媒体轨道)"
RS->>Video : "设置 srcObject 为媒体流"
Peer-->>RS : "adddatachannel(数据通道)"
RS->>RS : "getStats() 收集统计信息"
图表来源
章节来源
信令客户端实现
- HTTP 轮询:定时拉取消息,解析类型并派发事件
- WebSocket:长连接,消息类型丰富,支持广播与参与者路由
- 会话管理:创建/删除会话、连接管理、消息聚合
- 参与者模型:私有模式下 host 与多个 participants,支持按 participantId 路由
classDiagram
class Signaling {
+start()
+stop()
+createConnection()
+deleteConnection()
+sendOffer()
+sendAnswer()
+sendCandidate()
+getAll()
}
class WebSocketSignaling {
+start()
+stop()
+createConnection()
+deleteConnection()
+sendOffer()
+sendAnswer()
+sendCandidate()
+sendMessage()
}
Signaling <|-- WebSocketSignaling
图表来源
章节来源
- client/src/signaling.js:3-292
- src/class/httphandler.ts:492-641
- src/class/websockethandler.ts:145-338
依赖关系分析
- 后端依赖:Express、ws、uuid、swagger-ui-express 等
- 前端依赖:浏览器原生 WebRTC API、RTCDataChannel、EventTarget、Fetch/WebSocket
- 日志:前后端独立日志模块,支持级别控制与格式化输出
graph LR
Pkg["package.json 依赖声明"] --> Express["express"]
Pkg --> WS["ws"]
Pkg --> UUID["uuid"]
Pkg --> Swagger["swagger-ui-express"]
Front["前端模块"] --> WebRTC["WebRTC API"]
Front --> FetchWS["Fetch/WebSocket"]
LogB["后端日志(src/log.ts)"] --> ConsoleB["控制台输出"]
LogF["前端日志(client/src/logger.js)"] --> ConsoleF["控制台输出"]
图表来源
章节来源
性能考虑
- 连接稳定性
- 使用轮询重发 Offer 以应对网络抖动,避免长时间无响应
- ICE 候选收集完成后及时注入,减少连接建立延迟
- 媒体协商
- 优先使用匹配编解码器,减少转码开销
- 合理设置带宽限制与回退策略
- 输入事件
- 状态事件合并发送,降低消息频率
- 使用二进制消息格式,减少序列化成本
- 信令
- WebSocket 模式优于 HTTP 轮询,降低延迟与 CPU 开销
- 私有模式下按 participantId 路由,避免广播风暴
- 资源管理
- 及时关闭 PeerConnection 与数据通道
- 监控统计指标,动态调整分辨率与帧率
[本节为通用指导,无需具体文件分析]
故障排查指南
- 日志启用
- 前端:调用日志模块的 enable/disable 控制输出
- 后端:通过命令行参数设置日志级别
- 常见问题定位
- 连接失败:检查 signaling 事件派发与路由,确认 Offer/Answer/Candidate 是否正确传递
- ICE 失败:查看 iceconnectionstatechange/disconnect 事件,确认候选注入与防火墙/NAT 配置
- 输入无响应:确认 RTCDataChannel 已打开,消息格式与 participantId 正确
- 单元测试
- PeerConnection 行为验证:添加/移除轨道、发送 Offer/Answer、ICE 候选注入
- 输入系统:设备消息打包、事件编码、观察者发送
章节来源
- client/src/logger.js:1-30
- src/log.ts:1-51
- client/test/peerconnection.test.js:1-251
- client/test/inputremoting.test.js:1-132
结论
本实现提供了完整的 WebRTC 远程渲染与输入控制方案,具备清晰的模块划分与可扩展性。通过事件驱动与观察者模式,前端渲染控制器与输入系统能够高效协作;后端信令处理器支持私有与公共两种模式,满足不同场景需求。配合完善的日志与测试体系,便于维护与优化。
[本节为总结,无需具体文件分析]
附录
- 启动参数与脚本
- 开发模式:ts-node 启动,支持 HTTPS/HTTP 与私有/公共模式切换
- 生产模式:构建后运行,支持证书文件与端口配置
- API 与消息类型
- HTTP 轮询:/signaling/* 接口,支持会话管理与消息聚合
- WebSocket:connect/disconnect/offer/answer/candidate/on-message/participant-joined/participant-left/broadcast 等
章节来源