# 双向通信示例 **本文档引用的文件** - [main.js](file://client/public/bidirectional/js/main.js) - [sendvideo.js](file://client/public/bidirectional/js/sendvideo.js) - [index.html](file://client/public/bidirectional/index.html) - [style.css](file://client/public/bidirectional/css/style.css) - [config.js](file://client/public/js/config.js) - [icesettings.js](file://client/public/js/icesettings.js) - [signaling.js](file://client/src/signaling.js) - [peer.js](file://client/src/peer.js) - [renderstreaming.js](file://client/src/renderstreaming.js) - [index.ts](file://src/index.ts) - [websocket.ts](file://src/websocket.ts) - [options.ts](file://src/class/options.ts) - [package.json](file://package.json) - [run.bat](file://run.bat) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排除指南](#故障排除指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本示例演示了基于 WebRTC 的双向音视频通信,涵盖从本地媒体采集、信令交互、PeerConnection 建立到媒体流收发的完整流程。前端通过双向页面控制本地摄像头与麦克风,选择分辨率与编解码器偏好,发起连接后自动建立点对点通道,实现本地预览与远端播放的双向传输。后端提供 WebSocket 信令服务器,负责转发 offer/answer/candidate 等信令消息,并支持公共与私有通信模式。 ## 项目结构 该项目采用前后端分离架构: - 前端部分位于 client/public 与 client/src,包含双向示例页面、通用配置与工具模块、WebRTC 连接管理与信令封装。 - 后端部分位于 src,提供 Express 应用与 WebSocket 信令服务,支持 HTTPS、日志与多种运行参数。 ```mermaid graph TB subgraph "前端" A["bidirectional 页面
index.html"] B["主控制器
main.js"] C["媒体处理
sendvideo.js"] D["信令封装
signaling.js"] E["连接管理
renderstreaming.js"] F["Peer 封装
peer.js"] G["配置与ICE
config.js / icesettings.js"] end subgraph "后端" H["入口与参数解析
index.ts"] I["WebSocket 信令
websocket.ts"] J["选项接口
options.ts"] end A --> B B --> C B --> D B --> E E --> F D --> I H --> I G --> B ``` 图表来源 - [index.html:1-84](file://client/public/bidirectional/index.html#L1-L84) - [main.js:1-383](file://client/public/bidirectional/js/main.js#L1-L383) - [sendvideo.js:1-54](file://client/public/bidirectional/js/sendvideo.js#L1-L54) - [signaling.js:1-292](file://client/src/signaling.js#L1-L292) - [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317) - [peer.js:1-188](file://client/src/peer.js#L1-L188) - [config.js:1-39](file://client/public/js/config.js#L1-L39) - [icesettings.js:1-104](file://client/public/js/icesettings.js#L1-L104) - [index.ts:1-109](file://src/index.ts#L1-L109) - [websocket.ts:1-118](file://src/websocket.ts#L1-L118) 章节来源 - [index.html:1-84](file://client/public/bidirectional/index.html#L1-L84) - [main.js:1-383](file://client/public/bidirectional/js/main.js#L1-L383) - [index.ts:1-109](file://src/index.ts#L1-L109) ## 核心组件 - 双向页面控制器:负责 UI 控件初始化、媒体设备选择、分辨率与编解码器配置、启动本地媒体、建立 WebRTC 连接、处理统计信息与错误。 - 媒体处理类:封装本地媒体流采集、本地/远端视频播放、本地轨道获取与远端轨道添加。 - 信令封装:提供 HTTP 与 WebSocket 两种信令实现,统一事件接口,负责会话创建、offer/answer/candidate 发送与接收。 - 连接管理:封装多个 Peer 的生命周期与事件路由,负责 SDP 协商、ICE 候选处理、统计数据查询与消息广播。 - Peer 封装:封装 RTCPeerConnection 生命周期、SDP 描述交换、ICE 候选收集与注入、数据通道创建与事件派发。 - 配置与 ICE:提供 RTC 配置(sdpSemantics、ICE 服务器、音频增强)、读取/写入本地存储的 STUN/TURN 服务器。 章节来源 - [main.js:1-383](file://client/public/bidirectional/js/main.js#L1-L383) - [sendvideo.js:1-54](file://client/public/bidirectional/js/sendvideo.js#L1-L54) - [signaling.js:1-292](file://client/src/signaling.js#L1-L292) - [renderstreaming.js:1-317](file://client/src/renderstreaming.js#L1-L317) - [peer.js:1-188](file://client/src/peer.js#L1-L188) - [config.js:1-39](file://client/public/js/config.js#L1-L39) - [icesettings.js:1-104](file://client/public/js/icesettings.js#L1-L104) ## 架构总览 下图展示了从用户操作到媒体流双向传输的整体架构与数据流。 ```mermaid sequenceDiagram participant U as "用户" participant UI as "双向页面(main.js)" participant SV as "媒体处理(sendvideo.js)" participant RS as "连接管理(renderstreaming.js)" participant P as "Peer 封装(peer.js)" participant SIG as "信令封装(signaling.js)" participant WS as "WebSocket 信令(backend)" U->>UI : "启动视频/设置连接" UI->>SV : "startLocalVideo()" SV-->>UI : "本地媒体流可用" UI->>SIG : "创建信令实例(HTTP/WebSocket)" UI->>RS : "start()/createConnection()" RS->>P : "_preparePeerConnection()" P-->>RS : "onNegotiationneeded -> 发送 offer" RS->>SIG : "sendOffer()" SIG->>WS : "转发 offer" WS-->>SIG : "广播/定向转发" SIG-->>RS : "on offer" RS->>P : "onGotDescription(offer)" P-->>RS : "setLocalDescription -> answer" RS->>SIG : "sendAnswer()" SIG->>WS : "转发 answer" WS-->>SIG : "广播/定向转发" SIG-->>RS : "on answer" RS->>P : "onGotDescription(answer)" P-->>RS : "稳定状态" RS-->>UI : "onTrackEvent -> 添加远端轨道" UI->>SV : "addRemoteTrack()" SV-->>UI : "远端视频播放" ``` 图表来源 - [main.js:146-184](file://client/public/bidirectional/js/main.js#L146-L184) - [sendvideo.js:44-52](file://client/public/bidirectional/js/sendvideo.js#L44-L52) - [renderstreaming.js:191-250](file://client/src/renderstreaming.js#L191-L250) - [peer.js:57-82](file://client/src/peer.js#L57-L82) - [signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [websocket.ts:44-115](file://src/websocket.ts#L44-L115) ## 详细组件分析 ### 主控制器:双向页面逻辑(main.js) - 功能职责 - 设备与分辨率选择:枚举媒体设备,填充视频/音频选择框;支持预设分辨率与自定义分辨率。 - 本地媒体启动:根据选择的设备与分辨率调用媒体采集,设置本地视频元素并自动播放。 - 连接建立:根据配置选择 HTTP 或 WebSocket 信令,创建 RenderStreaming 实例,注册连接事件回调,添加本地轨道,设置编解码器偏好,启动统计信息展示。 - 断开连接:清理统计信息、停止渲染、删除连接、恢复 UI 状态。 - 编解码器偏好:在支持的浏览器上动态设置视频编解码器偏好,影响远端接收质量。 - 统计信息:周期性获取 WebRTC 统计并格式化显示,包含本地/远端分辨率与关键指标。 - 关键流程 - 启动本地视频:禁用输入控件,计算分辨率,调用媒体采集,启用设置按钮。 - 设置连接:创建信令实例,准备 RenderStreaming,注册 onConnect/onDisconnect/onTrackEvent 回调,添加本地轨道,设置编解码器,启动统计。 - 断开连接:清除统计,删除连接,停止渲染,恢复 UI。 - 最佳实践 - 使用统一的 RTC 配置与媒体约束,避免分辨率不匹配导致的性能问题。 - 在支持的浏览器上启用编解码器偏好,提升兼容性与质量。 - 注意统计信息的周期性更新与清理,防止内存泄漏。 章节来源 - [main.js:112-184](file://client/public/bidirectional/js/main.js#L112-L184) - [main.js:220-241](file://client/public/bidirectional/js/main.js#L220-L241) - [main.js:289-303](file://client/public/bidirectional/js/main.js#L289-L303) - [main.js:337-367](file://client/public/bidirectional/js/main.js#L337-L367) ### 媒体处理:SendVideo(sendvideo.js) - 功能职责 - 本地媒体采集:根据设备 ID 与分辨率约束获取媒体流,设置本地视频元素并播放。 - 本地轨道获取:返回本地媒体流中的轨道集合,供连接管理添加到 PeerConnection。 - 远端轨道添加:创建或复用 MediaStream,将远端轨道加入,驱动远端视频播放。 - 关键流程 - startLocalVideo:构造约束,调用 getUserMedia,设置本地视频源并播放。 - getLocalTracks:从本地流获取轨道。 - addRemoteTrack:将远端轨道加入本地流。 - 最佳实践 - 在调用 getUserMedia 前检查权限与设备可用性。 - 使用 MediaStreamTrack 的 addTrack/removeTrack 精确管理轨道生命周期。 章节来源 - [sendvideo.js:15-35](file://client/public/bidirectional/js/sendvideo.js#L15-L35) - [sendvideo.js:40-52](file://client/public/bidirectional/js/sendvideo.js#L40-L52) ### 信令封装:HTTP 与 WebSocket(signaling.js) - 功能职责 - HTTP 信令:基于轮询的信令实现,维护 Session-Id,支持连接创建/删除、offer/answer/candidate 发送与接收,以及自定义消息。 - WebSocket 信令:基于 WebSocket 的实时信令实现,支持连接/断开、offer/answer/candidate、参与者加入/离开、广播消息等。 - 关键流程 - HTTP:start() 循环创建会话,loopGetAll() 轮询消息并分发事件;createConnection/deleteConnection/sendOffer/sendAnswer/sendCandidate。 - WebSocket:onopen/onmessage/onclose 处理消息类型映射,分发事件;createConnection/deleteConnection/sendOffer/sendAnswer/sendCandidate/sendMessage。 - 最佳实践 - 在 HTTP 模式下合理设置轮询间隔,避免频繁请求。 - WebSocket 模式下注意连接状态与重连策略,确保消息可靠传递。 章节来源 - [signaling.js:30-97](file://client/src/signaling.js#L30-L97) - [signaling.js:152-292](file://client/src/signaling.js#L152-L292) ### 连接管理:RenderStreaming(renderstreaming.js) - 功能职责 - 统一封装信令事件与 Peer 生命周期,支持单 Peer(participant)与多 Peer(host)场景。 - 路由 offer/answer/candidate 到对应 Peer,处理 onTrack 事件,提供统计数据查询与消息广播。 - 关键流程 - _onConnect:根据角色创建 Peer,触发 onConnect。 - _onOffer/_onAnswer/_onIceCandidate:根据是否为 host 与 participantId 路由到对应 Peer。 - _preparePeerConnection:创建 Peer 并绑定事件,桥接信令与 Peer。 - getStats/addTrack/addTransceiver/createDataChannel:按角色与 participantId 路由到具体 Peer。 - 最佳实践 - host 端应为每个参与者维护独立 Peer,确保消息隔离。 - 在断开或失败状态下及时清理 Peer,避免资源泄露。 章节来源 - [renderstreaming.js:40-70](file://client/src/renderstreaming.js#L40-L70) - [renderstreaming.js:191-250](file://client/src/renderstreaming.js#L191-L250) - [renderstreaming.js:252-290](file://client/src/renderstreaming.js#L252-L290) ### Peer 封装:RTCPeerConnection 管理(peer.js) - 功能职责 - 封装 RTCPeerConnection 的创建、SDP 描述交换、ICE 候选收集与注入、数据通道创建与事件派发。 - 提供防闪烁(glare)处理,避免同时进行 offer/answer 导致的状态冲突。 - 关键流程 - _onNegotiationneeded:setLocalDescription 生成 offer,派发 sendoffer 事件。 - onGotDescription:根据描述类型设置远端描述,必要时生成 answer 并派发 sendanswer。 - onIceCandidate:收集候选并通过事件派发 sendcandidate。 - loopResendOffer:定期重发未确认的 offer,保证协商完成。 - 最佳实践 - 正确处理 signalingState 与 iceConnectionState,及时响应失败事件。 - 在多参与者场景下,确保 participantId 与 Peer 的一一对应。 章节来源 - [peer.js:57-82](file://client/src/peer.js#L57-L82) - [peer.js:132-173](file://client/src/peer.js#L132-L173) - [peer.js:175-186](file://client/src/peer.js#L175-L186) ### 配置与 ICE:RTC 配置与 STUN/TURN(config.js, icesettings.js) - 功能职责 - getServerConfig:获取服务器配置(是否使用 WebSocket、启动模式等)。 - getRTCConfiguration:设置 sdpSemantics、ICE 服务器、媒体约束与音频增强选项。 - icesettings.js:管理本地存储的 STUN/TURN 服务器列表,支持增删改查与默认值。 - 最佳实践 - 在公网部署时配置可靠的 TURN 服务器,提升连通性。 - 根据网络环境调整 ICE 服务器列表与轮询间隔。 章节来源 - [config.js:3-7](file://client/public/js/config.js#L3-L7) - [config.js:9-38](file://client/public/js/config.js#L9-L38) - [icesettings.js:94-104](file://client/public/js/icesettings.js#L94-L104) ### 后端:WebSocket 信令服务器(websocket.ts) - 功能职责 - 接收并处理 WebSocket 消息,根据类型分发到处理器,支持 connect/disconnect/offer/answer/candidate/ping/pong/broadcast/on-message 等。 - 维护连接池与广播机制,支持心跳检测与消息路由。 - 关键流程 - connection 事件:添加新连接到处理器。 - onmessage:解析消息类型并调用相应处理器。 - onclose:移除关闭连接。 - 最佳实践 - 对未知消息类型进行安全处理,避免异常传播。 - 实现心跳与超时检测,维持长连接稳定性。 章节来源 - [websocket.ts:27-39](file://src/websocket.ts#L27-L39) - [websocket.ts:44-115](file://src/websocket.ts#L44-L115) ### 入口与参数:服务器启动(index.ts) - 功能职责 - 解析命令行参数,支持端口、HTTPS、信令类型、通信模式与日志级别。 - 根据参数启动 HTTP/HTTPS 服务器,初始化 WebSocket 信令服务。 - 关键流程 - 参数解析:读取 PORT/SECURE/KEYFILE/CERTFILE/TYPE/MODE/LOGGING。 - 服务器启动:根据 secure 决定 HTTP/HTTPS,监听端口并输出访问地址。 - 信令类型:仅支持 websocket 或 http,其他值将被修正为 websocket。 - 最佳实践 - 生产环境建议启用 HTTPS 并配置有效的证书文件。 - 根据部署环境选择合适的通信模式(public/private)。 章节来源 - [index.ts:14-44](file://src/index.ts#L14-L44) - [index.ts:52-91](file://src/index.ts#L52-L91) ## 依赖关系分析 ```mermaid classDiagram class SendVideo { +startLocalVideo(videoSource, audioSource, width, height) +getLocalTracks() +addRemoteTrack(track) } class Signaling { +start() +stop() +createConnection(connectionId) +deleteConnection(connectionId) +sendOffer(connectionId, sdp) +sendAnswer(connectionId, sdp) +sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex) +sendMessage(connectionId, message) } class WebSocketSignaling { +start() +stop() +createConnection(connectionId) +deleteConnection(connectionId) +sendOffer(connectionId, sdp, participantId) +sendAnswer(connectionId, sdp, participantId) +sendCandidate(connectionId, candidate, sdpMid, sdpMLineIndex, participantId) +sendMessage(connectionId, message) } class Peer { +ontrack +ondatachannel +onicecandidate +onnegotiationneeded +onsignalingstatechange +oniceconnectionstatechange +onicegatheringstatechange +onGotDescription(connectionId, description) +onGotCandidate(connectionId, candidate) +getTransceivers(connectionId) +addTrack(connectionId, track) +addTransceiver(connectionId, trackOrKind, init) +createDataChannel(connectionId, label) +getStats(connectionId) +close() } class RenderStreaming { +start() +stop() +createConnection(connectionId) +deleteConnection() +addTrack(track, participantId) +addTransceiver(trackOrKind, init, participantId) +createDataChannel(label, participantId) +getStats(participantId) +sendMessage(message) +getTransceivers(participantId) } SendVideo --> RenderStreaming : "提供本地轨道" RenderStreaming --> Signaling : "使用" RenderStreaming --> Peer : "封装" Signaling <|-- WebSocketSignaling : "继承" ``` 图表来源 - [sendvideo.js:3-53](file://client/public/bidirectional/js/sendvideo.js#L3-L53) - [signaling.js:3-150](file://client/src/signaling.js#L3-L150) - [signaling.js:152-292](file://client/src/signaling.js#L152-L292) - [peer.js:3-188](file://client/src/peer.js#L3-L188) - [renderstreaming.js:11-317](file://client/src/renderstreaming.js#L11-L317) ## 性能考虑 - 分辨率与编解码器 - 合理选择分辨率,避免过高分辨率导致带宽与 CPU 压力过大。 - 在支持的浏览器上设置编解码器偏好,提升解码效率与画质一致性。 - ICE 服务器 - 在公网部署时配置可靠的 STUN/TURN 服务器,减少打洞失败与延迟。 - 统计信息 - 定期但不过度地获取 WebRTC 统计,避免频繁 IO 影响性能。 - 信令类型 - WebSocket 信令相比 HTTP 轮询具有更低的延迟与更少的请求开销,推荐在生产环境使用。 ## 故障排除指南 - 浏览器不支持编解码器偏好 - 现象:无法设置编解码器偏好,界面提示当前浏览器不支持。 - 处理:忽略偏好设置,使用默认编解码器;或升级浏览器版本。 - 公共模式限制 - 现象:公共模式下页面显示警告,示例不可用。 - 处理:切换到私有模式或使用支持的部署方式。 - 本地媒体无法启动 - 现象:点击“启动视频”无反应或报错。 - 处理:检查摄像头/麦克风权限、设备 ID 是否正确、分辨率是否受支持。 - 连接无法建立 - 现象:点击“设置连接”后无进展或很快断开。 - 处理:检查信令服务器是否在线、WebSocket 是否连通、ICE 服务器配置是否正确。 - 远端无画面 - 现象:本地有画面但远端无画面。 - 处理:确认已添加本地轨道到 PeerConnection、远端轨道已加入本地流、统计信息显示远端轨道已接收。 - 服务器启动失败 - 现象:启动脚本报错或无法访问。 - 处理:检查端口占用、HTTPS 证书文件是否存在、命令行参数是否正确。 章节来源 - [main.js:99-105](file://client/public/bidirectional/js/main.js#L99-L105) - [main.js:309-328](file://client/public/bidirectional/js/main.js#L309-L328) - [index.ts:78-82](file://src/index.ts#L78-L82) - [run.bat:8-16](file://run.bat#L8-L16) ## 结论 本双向通信示例通过清晰的模块划分与事件驱动设计,实现了从本地媒体采集到远端播放的完整链路。前端以 main.js 为核心协调各模块,后端以 WebSocket 信令服务器提供可靠的消息转发。通过合理的配置与最佳实践,可在不同网络环境下获得稳定的双向音视频体验。 ## 附录 ### 使用步骤 - 启动服务器 - 构建并启动后端服务,支持 HTTPS 与多种运行参数。 - 示例脚本提供了构建与启动命令,也可直接运行 TypeScript 源码进行开发调试。 - 打开示例页面 - 在浏览器中访问服务器提供的地址,进入双向示例页面。 - 页面会自动枚举可用的摄像头与麦克风,并提供分辨率与编解码器选择。 - 建立连接 - 输入连接 ID(可随机生成),点击“启动视频”采集本地媒体。 - 点击“设置连接”,系统将创建信令连接并开始协商。 - 成功后本地与远端视频将分别显示,统计信息会周期性更新。 - 进行双向通信 - 在双方都完成连接后,即可实现视频与音频的双向传输。 - 可通过挂断按钮结束连接,清理资源并恢复 UI 状态。 章节来源 - [package.json:9-12](file://package.json#L9-L12) - [run.bat:8-16](file://run.bat#L8-L16) - [index.html:76-79](file://client/public/bidirectional/index.html#L76-L79) - [main.js:112-184](file://client/public/bidirectional/js/main.js#L112-L184) ### 常见配置选项 - 服务器参数 - 端口:-p/--port,默认 80 或环境变量 PORT。 - HTTPS:-s/--secure,默认启用;需提供 keyfile 与 certfile。 - 信令类型:-t/--type,websocket 或 http,默认 websocket。 - 通信模式:-m/--mode,public 或 private,默认 public。 - 日志级别:-l/--logging,combined/dev/short/tiny/none,默认 dev。 - RTC 配置 - sdpSemantics:统一计划(unified-plan)。 - ICE 服务器:STUN/TURN,可通过本地存储配置。 - 音频增强:回声消除、噪声抑制、自动增益、高通滤波、打字噪声检测等。 - 编解码器偏好 - 在支持的浏览器上可选择特定视频编解码器,提升兼容性与质量。 章节来源 - [index.ts:20-41](file://src/index.ts#L20-L41) - [config.js:9-38](file://client/public/js/config.js#L9-L38) - [icesettings.js:94-104](file://client/public/js/icesettings.js#L94-L104) - [main.js:189-213](file://client/public/bidirectional/js/main.js#L189-L213) ### 代码示例路径 - 双向页面主逻辑:[main.js:146-184](file://client/public/bidirectional/js/main.js#L146-L184) - 媒体处理类:[sendvideo.js:15-35](file://client/public/bidirectional/js/sendvideo.js#L15-L35) - 信令封装(HTTP/WebSocket):[signaling.js:152-292](file://client/src/signaling.js#L152-L292) - 连接管理:[renderstreaming.js:191-250](file://client/src/renderstreaming.js#L191-L250) - Peer 封装:[peer.js:57-82](file://client/src/peer.js#L57-L82) - 服务器启动与参数:[index.ts:52-91](file://src/index.ts#L52-L91)