Files
2026-05-16 13:24:02 +08:00

367 lines
15 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.
# 渲染流处理
<cite>
**本文引用的文件**
- [client/src/renderstreaming.js](file://client/src/renderstreaming.js)
- [client/src/peer.js](file://client/src/peer.js)
- [client/src/signaling.js](file://client/src/signaling.js)
- [client/src/logger.js](file://client/src/logger.js)
- [client/src/inputdevice.js](file://client/src/inputdevice.js)
- [client/src/inputremoting.js](file://client/src/inputremoting.js)
- [client/src/pointercorrect.js](file://client/src/pointercorrect.js)
- [client/src/gamepadhandler.js](file://client/src/gamepadhandler.js)
- [client/src/sender.js](file://client/src/sender.js)
- [client/test/renderstreaming.test.js](file://client/test/renderstreaming.test.js)
- [client/test/peerconnection.test.js](file://client/test/peerconnection.test.js)
- [client/public/onebyone/renderer.js](file://client/public/onebyone/renderer.js)
- [src/index.ts](file://src/index.ts)
- [package.json](file://package.json)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 引言
本文件聚焦“渲染流处理”模块,系统性阐述媒体流的接收、解码与显示全链路,涵盖 WebRTC 媒体流的信令与协商、轨道管理、渲染管线、性能优化与调试方法。文档面向不同技术背景读者,既提供高层架构视图,也包含代码级细节与可视化图表,帮助快速定位问题并进行针对性优化。
## 项目结构
该项目由前端渲染与后端服务两部分组成:
- 前端 client负责渲染、输入遥测、WebRTC 信令与协商、媒体轨道管理与显示。
- 后端 src基于 Express 的服务端入口,启动 HTTP/HTTPS 服务器并按需启用 WebSocket 信令。
```mermaid
graph TB
subgraph "前端 client"
RS["RenderStreaming<br/>渲染流编排"]
PEER["Peer<br/>RTCPeerConnection 封装"]
SIG["Signaling<br/>HTTP/WebSocket 信令"]
RENDER["Renderer<br/>UI 渲染器"]
INPUT["Input Remoting<br/>输入事件打包"]
end
subgraph "后端 src"
APP["Express 应用"]
WS["WebSocket 信令服务"]
end
RS --> PEER
RS --> SIG
RENDER --> RS
INPUT --> SIG
SIG --> WS
APP --> WS
```
图表来源
- [client/src/renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317)
- [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188)
- [client/src/signaling.js:3-292](file://client/src/signaling.js#L3-L292)
- [client/public/onebyone/renderer.js:28-800](file://client/public/onebyone/renderer.js#L28-L800)
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
章节来源
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
- [package.json:1-60](file://package.json#L1-L60)
## 核心组件
- 渲染流编排RenderStreaming
- 负责连接生命周期管理、主机/参与方模式、多 peer 管理、事件转发与统计查询。
- RTCPeerConnection 封装Peer
- 提供 SDP 协商、ICE 候选、轨道增删、统计数据查询与断连检测。
- 信令Signaling
- 支持 HTTP 轮询与 WebSocket 两种模式,封装 offer/answer/candidate/on-message 等事件。
- 输入遥测Input Remoting
- 将鼠标、键盘、触摸、手柄等输入事件序列化并通过数据通道传输。
- 渲染器Renderer
- 将状态映射到 DOM负责视频流显示、占位符切换、分辨率适配与网络质量指示。
章节来源
- [client/src/renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317)
- [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188)
- [client/src/signaling.js:3-292](file://client/src/signaling.js#L3-L292)
- [client/src/inputremoting.js:1-300](file://client/src/inputremoting.js#L1-L300)
- [client/public/onebyone/renderer.js:28-800](file://client/public/onebyone/renderer.js#L28-L800)
## 架构总览
渲染流处理采用“信令驱动 + PeerConnection 协商 + 轨道渲染”的分层架构。前端通过 Signaling 与后端建立连接,随后在 RenderStreaming 中完成主机/参与方角色下的 peer 管理与事件路由Peer 负责 SDP 与 ICE 流程Renderer 负责将媒体轨道映射到页面元素。
```mermaid
sequenceDiagram
participant UI as "页面/UI"
participant RS as "RenderStreaming"
participant SIG as "Signaling(HTTP/WebSocket)"
participant PC as "Peer(RTCPeerConnection)"
participant REN as "Renderer"
UI->>RS : 创建连接/发起协商
RS->>SIG : createConnection()
SIG-->>RS : connect(connectId, role)
RS->>PC : _preparePeerConnection()
UI->>RS : addTrack()/createDataChannel()
RS->>PC : addTrack()/createDataChannel()
PC-->>RS : onnegotiationneeded -> setLocalDescription()
PC-->>SIG : sendoffer(sdp)
SIG-->>RS : offer
RS->>PC : onGotDescription(offer)
PC-->>PC : setRemoteDescription()
PC-->>PC : setLocalDescription() -> answer
PC-->>SIG : sendanswer(sdp)
SIG-->>RS : answer
RS->>PC : onGotDescription(answer)
PC-->>UI : ontrack -> trackevent
RS-->>REN : onTrackEvent(data)
REN-->>UI : 设置 video.srcObject / 占位符切换
```
图表来源
- [client/src/renderstreaming.js:40-130](file://client/src/renderstreaming.js#L40-L130)
- [client/src/peer.js:57-173](file://client/src/peer.js#L57-L173)
- [client/src/signaling.js:30-149](file://client/src/signaling.js#L30-L149)
- [client/public/onebyone/renderer.js:397-604](file://client/public/onebyone/renderer.js#L397-L604)
## 详细组件分析
### 渲染流编排RenderStreaming
- 角色与连接
- 主机host维护多个 participantId -> Peer 的映射,按需创建/复用 peer。
- 参与方participant单一 peer连接建立后立即协商。
- 事件路由
- 将底层 Peer 的 trackevent/adddatachannel/offer/answer 等事件透传给上层回调。
- 统计与通道
- 提供 getStats、createDataChannel、addTrack/addTransceiver 等能力,支持按 participantId 路由。
```mermaid
classDiagram
class RenderStreaming {
+onConnect()
+onDisconnect()
+onGotOffer()
+onGotAnswer()
+onTrackEvent()
+onAddChannel()
+onMessage()
+onParticipantLeft()
+onParticipantJoined()
+onNewPeer()
+createConnection()
+deleteConnection()
+getStats()
+createDataChannel()
+addTrack()
+addTransceiver()
+getTransceivers()
+sendMessage()
+start()
+stop()
}
class Peer {
+addEventListener()
+close()
+getTransceivers()
+addTrack()
+addTransceiver()
+createDataChannel()
+getStats()
+onGotDescription()
+onGotCandidate()
}
RenderStreaming --> Peer : "管理/路由"
```
图表来源
- [client/src/renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317)
- [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188)
章节来源
- [client/src/renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317)
### RTCPeerConnection 封装Peer
- 协商流程
- onnegotiationneeded 自动生成 offer 并通过事件上报;收到远端 offer 后 setRemoteDescription再 setLocalDescription 生成 answer。
- ICE 候选
- onicecandidate 上报候选,支持轮询重发 offer 以应对网络抖动。
- 断连检测
- iceConnectionState=failed 触发 disconnect 事件。
```mermaid
flowchart TD
Start(["开始"]) --> Negotiation["onNegotiationNeeded"]
Negotiation --> SetL["setLocalDescription() 生成 offer"]
SetL --> SendOffer["dispatchEvent('sendoffer')"]
SendOffer --> RecvOffer["收到远端 offer"]
RecvOffer --> SetR["setRemoteDescription(offer)"]
SetR --> SetL2["setLocalDescription() 生成 answer"]
SetL2 --> SendAnswer["dispatchEvent('sendanswer')"]
SendAnswer --> RecvAnswer["收到远端 answer"]
RecvAnswer --> Stable["signalingState=stable"]
Stable --> Track["ontrack -> trackevent"]
Track --> End(["结束"])
```
图表来源
- [client/src/peer.js:57-173](file://client/src/peer.js#L57-L173)
章节来源
- [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188)
### 信令Signaling
- HTTP 轮询
- start() 循环拉取消息队列,分发 connect/disconnect/offer/answer/candidate/on-message。
- WebSocket
- WebSocketSignaling 通过 ws/wss 通道推送消息,支持广播与多参与方路由。
```mermaid
sequenceDiagram
participant RS as "RenderStreaming"
participant HTTP as "Signaling(HTTP)"
participant WS as "WebSocketSignaling"
RS->>HTTP : PUT /signaling/connection
HTTP-->>RS : connect(connectId, polite)
RS->>HTTP : POST /signaling/offer|answer|candidate
HTTP-->>RS : 轮询返回消息
RS->>WS : createConnection/sendOffer...
WS-->>RS : offer/answer/candidate/on-message
```
图表来源
- [client/src/signaling.js:30-149](file://client/src/signaling.js#L30-L149)
- [client/src/signaling.js:230-291](file://client/src/signaling.js#L230-L291)
章节来源
- [client/src/signaling.js:3-292](file://client/src/signaling.js#L3-L292)
### 输入遥测与渲染Input Remoting + Renderer
- 输入遥测
- Sender 负责捕获鼠标/键盘/触摸/手柄事件,通过 PointerCorrector 校正坐标,打包为 StateEvent/TextEvent经 RTCDataChannel 发送。
- 渲染器
- Renderer 将状态映射到 DOM设置 video.srcObject、占位符切换、分辨率适配、网络质量指示与用户列表。
```mermaid
sequenceDiagram
participant UI as "页面"
participant SEN as "Sender"
participant IR as "InputRemoting"
participant SIG as "Signaling"
participant REN as "Renderer"
UI->>SEN : 鼠标/键盘/触摸/手柄事件
SEN->>IR : StateEvent/TextEvent
IR->>SIG : RTCDataChannel 发送
SIG-->>UI : on-message 回传可选
UI->>REN : 状态变更
REN-->>UI : 设置 video.srcObject / 占位符
```
图表来源
- [client/src/sender.js:14-188](file://client/src/sender.js#L14-L188)
- [client/src/inputremoting.js:63-169](file://client/src/inputremoting.js#L63-L169)
- [client/public/onebyone/renderer.js:397-604](file://client/public/onebyone/renderer.js#L397-L604)
章节来源
- [client/src/inputdevice.js:1-719](file://client/src/inputdevice.js#L1-L719)
- [client/src/inputremoting.js:1-300](file://client/src/inputremoting.js#L1-L300)
- [client/src/pointercorrect.js:1-125](file://client/src/pointercorrect.js#L1-L125)
- [client/src/sender.js:14-188](file://client/src/sender.js#L14-L188)
- [client/public/onebyone/renderer.js:28-800](file://client/public/onebyone/renderer.js#L28-L800)
## 依赖关系分析
- 前端依赖
- RenderStreaming 依赖 Peer 与 SignalingPeer 依赖浏览器 WebRTC APIRenderer 依赖 DOM 与媒体轨道。
- 后端依赖
- Express 应用启动,按配置选择 WebSocket 或 HTTP 信令;日志与选项解析来自独立模块。
```mermaid
graph LR
RS["RenderStreaming"] --> PEER["Peer"]
RS --> SIG["Signaling"]
PEER --> BR["浏览器 WebRTC API"]
SIG --> WS["WebSocket 服务"]
REN["Renderer"] --> DOM["DOM/VideoElement"]
APP["Express 应用"] --> WS
```
图表来源
- [client/src/renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317)
- [client/src/peer.js:3-188](file://client/src/peer.js#L3-L188)
- [client/src/signaling.js:3-292](file://client/src/signaling.js#L3-L292)
- [client/public/onebyone/renderer.js:28-800](file://client/public/onebyone/renderer.js#L28-L800)
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
章节来源
- [src/index.ts:13-109](file://src/index.ts#L13-L109)
- [package.json:14-46](file://package.json#L14-L46)
## 性能考量
- 缓冲与丢帧
- 使用 ontrack 事件及时设置 video.srcObject避免重复赋值导致的播放中断监听视频轨道 resize 事件动态调整显示尺寸。
- 延迟控制
- 通过 WebSocket 信令降低 HTTP 轮询延迟Peer 内部定时重发 offer提升握手成功率。
- 带宽与质量
- 通过 getStats 获取 ICE/媒体统计,结合 Renderer 的网络质量指示,辅助进行自适应策略(例如降分辨率/帧率)。
- 渲染优化
- 使用 object-fit 与容器宽高比计算,减少重绘;仅在必要时切换占位符,避免频繁 DOM 更新。
章节来源
- [client/public/onebyone/renderer.js:575-604](file://client/public/onebyone/renderer.js#L575-L604)
- [client/src/peer.js:75-82](file://client/src/peer.js#L75-L82)
- [client/src/peer.js:124-130](file://client/src/peer.js#L124-L130)
## 故障排查指南
- 无法建立连接
- 检查 Signaling.start() 是否成功;确认 connect/disconnect 事件是否触发;核对 sessionId 与连接 ID。
- 协商失败
- 关注 Peer 的 onnegotiationneeded 与 offer/answer 流程;确认 ignoreOffer 条件与 signalingState。
- ICE 候选异常
- 核验 onicecandidate 事件是否上报;确认 addIceCandidate 的调用时机与错误日志。
- 渲染无画面
- 检查 video.srcObject 是否设置;确认 trackevent 是否到达;关注占位符切换逻辑。
- 日志与调试
- 使用 Logger.enable() 开启调试输出;通过 getStats 输出详细统计信息。
章节来源
- [client/src/signaling.js:30-91](file://client/src/signaling.js#L30-L91)
- [client/src/peer.js:57-173](file://client/src/peer.js#L57-L173)
- [client/src/logger.js:1-30](file://client/src/logger.js#L1-L30)
- [client/public/onebyone/renderer.js:397-406](file://client/public/onebyone/renderer.js#L397-L406)
## 结论
本模块以 RenderStreaming 为核心,串联信令、协商与渲染三大环节,形成稳定高效的 WebRTC 渲染流处理框架。通过清晰的事件路由、完善的统计接口与直观的 UI 渲染器,既能满足多参与方场景,也能在复杂网络条件下保持良好的用户体验。建议在生产环境中结合 getStats 实施自适应策略,并持续优化渲染器的 DOM 更新频率与媒体轨道处理路径。
## 附录
### 渲染流配置与参数
- 分辨率与帧率
- 通过视频轨道的 getSettings 获取 width/height在渲染器中根据宽高比调整显示尺寸。
- 编码参数
- 通过 MediaStreamConstraints 与 RTCPeerConnection 的配置对象传递;具体编码参数取决于浏览器与对端支持。
- 数据通道
- 使用 createDataChannel 发送输入事件;注意通道状态与 readyState。
章节来源
- [client/public/onebyone/renderer.js:781-791](file://client/public/onebyone/renderer.js#L781-L791)
- [client/src/peer.js:116-122](file://client/src/peer.js#L116-L122)
### 媒体轨道管理
- 音频/视频分离
- addTrack/addTransceiver 支持分别添加音频与视频轨道Renderer 依据轨道数量决定占位符与播放行为。
- 质量自适应
- 结合 getStats 的比特率、丢包与 RTT 指标,动态调整轨道方向或发送参数。
章节来源
- [client/src/peer.js:100-114](file://client/src/peer.js#L100-L114)
- [client/public/onebyone/renderer.js:575-604](file://client/public/onebyone/renderer.js#L575-L604)
### 事件处理与调试示例(路径指引)
- 渲染流事件
- [client/test/renderstreaming.test.js:78-133](file://client/test/renderstreaming.test.js#L78-L133)
- Peer 协商事件
- [client/test/peerconnection.test.js:43-104](file://client/test/peerconnection.test.js#L43-L104)
- 输入事件打包
- [client/src/inputremoting.js:138-141](file://client/src/inputremoting.js#L138-L141)
- 视频渲染设置
- [client/public/onebyone/renderer.js:397-406](file://client/public/onebyone/renderer.js#L397-L406)