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

17 KiB
Raw Permalink Blame History

多播放示例

**本文引用的文件** - [client/src/renderstreaming.js](file://client/src/renderstreaming.js) - [client/src/signaling.js](file://client/src/signaling.js) - [client/src/peer.js](file://client/src/peer.js) - [client/public/multiplay/js/main.js](file://client/public/multiplay/js/main.js) - [src/index.ts](file://src/index.ts) - [src/server.ts](file://src/server.ts) - [src/websocket.ts](file://src/websocket.ts) - [src/class/websockethandler.ts](file://src/class/websockethandler.ts) - [src/signaling.ts](file://src/signaling.ts) - [src/class/httphandler.ts](file://src/class/httphandler.ts) - [package.json](file://package.json)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本示例演示如何在浏览器端与服务器之间构建多播式媒体分发通道,使多个客户端能够同时接收来自同一信源的相同媒体流。该实现以 WebRTC 为核心,结合 WebSocket 或 HTTP 轮询信令通道,支持:

  • 多客户端同时加入同一连接组
  • 服务器侧按连接组进行消息路由与广播
  • 客户端侧根据角色(主机/参与者)分别管理独立的 PeerConnection
  • 基于数据通道的广播消息与输入事件传递

本指南聚焦于多播场景下的连接管理、媒体流复制与同步机制,并给出在 Unity Render Streaming 的 Multiplay 场景中的使用方法、集成步骤、性能优化与故障处理建议。

项目结构

该仓库采用前后端分离结构:

  • 前端示例位于 client/public/multiplay包含多客户端多播播放器与输入通道
  • 前端核心逻辑位于 client/src封装了信令、WebRTC Peer 管理与渲染
  • 服务端位于 src提供 HTTP 服务与 WebSocket 信令服务,以及信令路由与广播
graph TB
subgraph "前端"
FE_Main["multiplay/main.js"]
FE_Render["renderstreaming.js"]
FE_Signaling["signaling.js"]
FE_Peer["peer.js"]
end
subgraph "服务端"
BE_Index["index.ts"]
BE_Server["server.ts"]
BE_WS["websocket.ts"]
BE_WS_Handler["class/websockethandler.ts"]
BE_HTTP_Router["signaling.ts"]
BE_HTTP_Handler["class/httphandler.ts"]
end
FE_Main --> FE_Render
FE_Render --> FE_Signaling
FE_Render --> FE_Peer
FE_Signaling --> BE_Index
BE_Index --> BE_Server
BE_Server --> BE_WS
BE_WS --> BE_WS_Handler
BE_Server --> BE_HTTP_Router
BE_HTTP_Router --> BE_HTTP_Handler

图表来源

章节来源

核心组件

  • 多播渲染层RenderStreaming
    • 负责根据角色(主机/参与者)管理多个 PeerConnection路由信令与媒体事件
    • 提供创建/删除连接、创建数据通道、添加轨道、获取统计等能力
  • 信令层Signaling/ WebSocketSignaling
    • 支持 HTTP 轮询与 WebSocket 两种信令协议
    • 负责连接生命周期管理、offer/answer/candidate 的收发与广播
  • Peer 层Peer
    • 封装 RTCPeerConnection处理 SDP 协商、ICE 候选、数据通道与统计
  • 服务器信令处理器WebSocket/HTTP
    • 维护连接组(主机 + 多个参与者),实现消息路由与广播
    • 支持私有模式(一对一)与公共模式(一对多)

章节来源

架构总览

多播架构的关键在于“连接组”与“角色路由”。服务器为每个 connectionId 维护一个连接组,首个加入者作为主机,后续加入者作为参与者。主机与参与者之间的信令消息按角色进行定向转发;广播消息则在组内进行全量分发。

sequenceDiagram
participant C1 as "客户端1主机"
participant C2 as "客户端2参与者"
participant Cn as "客户端n参与者"
participant WS as "WebSocket服务器"
participant H as "连接组处理器"
C1->>WS : "connect(connectionId)"
WS->>H : "onConnect(connectionId)"
H-->>C1 : "connect(role=host, participantId=p1)"
C2->>WS : "connect(connectionId)"
WS->>H : "onConnect(connectionId)"
H-->>C2 : "connect(role=participant, participantId=p2)"
H-->>C1 : "participant-joined(p2)"
C2->>WS : "offer(sdp, from=p2)"
WS->>H : "onOffer(data, from=p2)"
H-->>C1 : "offer(sdp, from=p2, participantId=p2)"
C1->>WS : "answer(sdp, from=p1)"
WS->>H : "onAnswer(data, from=p1)"
H-->>C2 : "answer(sdp, from=p1, participantId=p2)"
C1->>WS : "candidate(...)"
C2->>WS : "candidate(...)"
WS->>H : "onCandidate(...)"
H-->>C2 : "candidate(..., participantId=p2)"
H-->>C1 : "candidate(...)"

图表来源

章节来源

详细组件分析

多客户端连接管理与媒体流复制

  • 连接组与角色
    • 首个连接者为主机,后续连接者为参与者
    • 服务器为每个 connectionId 维护 host 与 participants 集合
  • 多 Peer 管理
    • 主机端为每个参与者维护独立的 Peer 实例,确保多路媒体复制与独立统计
    • 参与者端仅维护单一 Peer避免不必要的连接开销
  • 媒体复制与同步
    • 服务器对 offer/answer/candidate 进行定向转发或广播
    • 客户端在收到 offer 时创建/复用对应参与者的 Peer 并设置 SDP随后自动生成 answer 并回传
    • ICE 候选同样按目标参与者路由,保证连接建立的正确性
flowchart TD
Start(["开始收到offer"]) --> IsHost{"是否为主机?"}
IsHost --> |是| GetPeer["获取/创建目标参与者的Peer"]
IsHost --> |否| CreatePeer["创建或复用单一Peer"]
GetPeer --> SetSDP["设置远程SDPoffer"]
CreatePeer --> SetSDP
SetSDP --> GenAnswer["生成本地SDPanswer"]
GenAnswer --> SendAnswer["发送answer给目标方"]
SendAnswer --> End(["结束"])

图表来源

章节来源

服务器端多播路由与广播

  • 连接组维护
    • 使用 Map<connectionId, { host, participants }> 维护连接组
    • 通过 participantId 标识参与者,实现按参与者定向转发
  • 消息路由
    • offer/answer/candidate主机发给指定参与者参与者发给主机
    • broadcast服务器向组内所有成员广播消息
  • 断线与清理
    • 主机断线时,向所有参与者广播断线通知并清理连接组
    • 参与者断线时,通知主机并从组内移除
flowchart TD
A["收到消息"] --> Type{"消息类型?"}
Type --> |offer/answer/candidate| Route["按角色与participantId路由"]
Type --> |broadcast| Broad["向组内广播"]
Route --> Send["发送到目标WS"]
Broad --> Send
Send --> Done["完成"]

图表来源

章节来源

客户端渲染与输入通道Multiplay 示例)

  • 页面初始化
    • 读取服务器配置,决定使用 WebSocket 或 HTTP 信令
    • 创建 RenderStreaming 实例,注册连接、断开、轨道事件回调
  • 数据通道
    • 创建名为 "multiplay" 的数据通道用于广播消息
    • 打开后随机生成标签并向服务器广播
  • 媒体播放
    • 通过 onTrackEvent 回调将远端轨道接入播放器
sequenceDiagram
participant UI as "页面"
participant RS as "RenderStreaming"
participant Sig as "Signaling"
participant PC as "Peer"
participant VP as "VideoPlayer"
UI->>RS : "start()"
RS->>Sig : "start()"
UI->>RS : "createConnection()"
RS->>Sig : "createConnection()"
Sig-->>RS : "connect(role, participantId)"
RS->>PC : "创建/复用Peer"
RS-->>VP : "onTrackEvent -> addTrack()"
UI->>RS : "createDataChannel('multiplay')"
RS-->>UI : "onopen -> 发送标签"

图表来源

章节来源

服务器启动与信令服务

  • 启动流程
    • 解析命令行参数,创建 Express 应用
    • 根据安全与信令类型启动 HTTP/HTTPS 与 WebSocket 服务
  • 静态资源与 API
    • 提供前端静态页面与模块
    • 暴露 /config、/signaling 路由
  • WebSocket 信令
    • 监听连接、消息、关闭事件
    • 分派到连接组处理器进行路由与广播
flowchart TD
Start(["启动"]) --> Parse["解析参数"]
Parse --> CreateApp["创建Express应用"]
CreateApp --> HTTPS{"启用HTTPS"}
HTTPS --> |是| HTTPSrv["启动HTTPS服务器"]
HTTPS --> |否| HTTPsrv["启动HTTP服务器"]
HTTPSrv --> WS{"信令类型?"}
HTTPsrv --> WS
WS --> |websocket| WSSrv["启动WebSocket信令"]
WS --> |http| End(["完成"])

图表来源

章节来源

依赖关系分析

  • 前端模块
    • multiplay/main.js 依赖 renderstreaming.js 与 signaling.js
    • renderstreaming.js 依赖 peer.js 与 signaling.js
    • peer.js 依赖浏览器 WebRTC API
  • 服务端模块
    • index.ts 作为入口,创建 server.ts 并根据配置启动 WebSocket 信令
    • server.ts 注册 /signaling 路由,路由到 HTTP 信令处理器
    • websocket.ts 接收 WebSocket 消息,路由到连接组处理器
graph LR
MJS["multiplay/main.js"] --> RS["renderstreaming.js"]
RS --> SIG["signaling.js"]
RS --> PEER["peer.js"]
IDX["index.ts"] --> SRV["server.ts"]
SRV --> WSC["websocket.ts"]
SRV --> HTTR["signaling.ts"]
HTTR --> HTH["class/httphandler.ts"]
WSC --> WSH["class/websockethandler.ts"]

图表来源

章节来源

性能考量

  • 信令协议选择
    • WebSocket 适合低延迟、高并发的多播场景HTTP 轮询开销较大但兼容性更好
  • 连接组规模
    • 主机端为每个参与者维护独立 Peer注意内存与 CPU 开销随参与者数量增长
  • 媒体编解码与带宽
    • 使用 setCodecPreferences 指定编解码器,降低不必要转码成本
    • 合理设置传输参数(如 maxBitrate避免上行拥塞
  • 统计与监控
    • 定期获取 Peer 统计,观察丢包、抖动与编码速率
  • 负载均衡与扩展
    • 多实例部署时,需确保同一 connectionId 的消息在同一实例内处理
    • 使用外部存储(如 Redis共享连接组状态实现水平扩展

[本节为通用指导,无需列出具体文件来源]

故障排查指南

  • 无法建立连接
    • 检查服务器日志与 WebSocket/HTTP 信令是否正常
    • 确认客户端使用的信令类型与服务器配置一致
  • offer/answer 循环异常
    • 查看客户端 Peer 日志,确认 SDP 设置顺序与状态机是否正确
    • 检查服务器 onOffer/onAnswer 路由是否按角色与 participantId 正确转发
  • ICE 候选无法到达
    • 确认候选消息是否按目标参与者路由
    • 检查 NAT/防火墙与 STUN/TURN 配置
  • 广播消息未达
    • 确认 broadcast 消息的目标 connectionId 与连接组是否存在
    • 检查客户端 on-message 事件是否正确接收

章节来源

结论

本多播示例通过“连接组 + 角色路由 + 广播”的架构,实现了多客户端同时接收相同媒体流的广播式分发。前端以 RenderStreaming 为核心,配合 WebSocket/HTTP 信令与独立 Peer 管理,确保了多路媒体复制与同步;后端通过连接组处理器实现高效的消息路由与广播。结合合理的性能优化与故障排查策略,可在生产环境中稳定运行。

[本节为总结性内容,无需列出具体文件来源]

附录

在 Unity Render Streaming 的 Multiplay 场景中使用

  • 场景定位
    • Multiplay 示例展示了多客户端同时观看同一渲染流的典型用法
  • 关键步骤
    • 启动服务器并确认 /config 返回的 useWebSocket 与 startupMode
    • 在页面中点击“开始播放”,初始化 RenderStreaming 并创建连接
    • 通过 "multiplay" 数据通道发送标签等广播消息
  • 注意事项
    • 私有模式下,页面提示不适用;请使用公共模式或调整前端逻辑
    • 若浏览器不支持 setCodecPreferences需降级处理或提示用户

章节来源

服务器配置与部署

  • 启动参数
    • 端口、HTTPS 证书、信令类型websocket/http、通信模式public/private、日志级别
  • 部署建议
    • 使用 HTTPS 以满足现代浏览器对媒体权限的要求
    • 将静态资源与模块路径正确暴露,确保前端示例可访问
    • 对外暴露 /config、/signaling 路由,以便前端动态获取配置

章节来源

客户端连接与负载均衡考虑

  • 客户端连接
    • 通过 /config 动态获取信令类型与模式
    • 使用 createConnection 与 start 建立信令通道
  • 负载均衡
    • 多实例部署时,确保同一 connectionId 的消息在同一实例内处理
    • 使用外部存储共享连接组状态,实现跨实例一致性

章节来源