This commit is contained in:
2026-05-16 13:24:02 +08:00
parent eae60714b4
commit 6c13817527
42 changed files with 15921 additions and 0 deletions

View File

@@ -0,0 +1,437 @@
# 一对一通信示例
<cite>
**本文引用的文件**
- [models.js](file://client/public/onebyone/models.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [connect.js](file://client/public/onebyone/connect/connect.js)
- [endcall.js](file://client/public/onebyone/endcall/endcall.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [utils.js](file://client/public/onebyone/utils.js)
- [main.js](file://client/public/onebyone/main.js)
- [index.html](file://client/public/onebyone/index.html)
- [connect.html](file://client/public/onebyone/connect/connect.html)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本项目是一对一视频通话示例,基于浏览器 WebRTC 技术实现点对点P2P视频通话包含连接建立、媒体协商、通话管理、聊天消息与用户界面等完整功能。本文档将深入解析代码架构涵盖数据模型、状态管理、视图渲染、连接流程与通话结束处理并提供扩展、定制与集成建议。
## 项目结构
客户端采用模块化组织,核心目录与文件如下:
- onebyone 目录:一对一通话主界面与相关模块
- models.js数据模型定义
- store.js状态管理与 WebRTC 会话控制
- renderer.js视图渲染器负责将状态映射到 DOM
- main.js应用入口绑定事件与初始化
- index.html主界面 HTML 模板
- connect/:连接界面
- connect.html初始连接界面
- connect.js连接界面逻辑
- endcall/:通话结束界面
- endcall.html结束界面
- endcall.js结束逻辑
- chatmessage.js聊天消息模块
- utils.js工具函数
- public/js通用配置与 ICE 设置
- config.js获取服务器配置与 RTC 配置
- icesettings.jsICE 服务器配置持久化与读取
```mermaid
graph TB
subgraph "客户端(onebyone)"
A["index.html<br/>主界面"]
B["main.js<br/>入口与事件绑定"]
C["store.js<br/>状态管理/WebRTC"]
D["renderer.js<br/>视图渲染"]
E["models.js<br/>数据模型"]
F["chatmessage.js<br/>聊天消息"]
G["utils.js<br/>工具函数"]
H["connect/connect.html<br/>连接界面"]
I["connect/connect.js<br/>连接逻辑"]
J["endcall/endcall.js<br/>结束逻辑"]
end
subgraph "公共配置"
K["config.js<br/>服务器/RTC配置"]
L["icesettings.js<br/>ICE服务器持久化"]
end
A --> B
B --> C
B --> D
B --> F
C --> D
C --> F
C --> E
C --> K
K --> L
H --> I
A --> J
```
图表来源
- [index.html](file://client/public/onebyone/index.html)
- [main.js](file://client/public/onebyone/main.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [models.js](file://client/public/onebyone/models.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [utils.js](file://client/public/onebyone/utils.js)
- [connect/connect.html](file://client/public/onebyone/connect/connect.html)
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
- [endcall/endcall.js](file://client/public/onebyone/endcall/endcall.js)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)
章节来源
- [index.html](file://client/public/onebyone/index.html)
- [main.js](file://client/public/onebyone/main.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [models.js](file://client/public/onebyone/models.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [utils.js](file://client/public/onebyone/utils.js)
- [connect/connect.html](file://client/public/onebyone/connect/connect.html)
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
- [endcall/endcall.js](file://client/public/onebyone/endcall/endcall.js)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)
## 核心组件
- 数据模型models.js
- 定义通话会话、本地/远端用户、媒体状态、聊天消息等类型与模拟数据
- 状态管理store.js
- 使用简单观察者模式管理应用状态,封装 WebRTC 连接、媒体轨道、编解码器偏好、编码参数、网络质量检测、音频活动检测、消息广播等
- 视图渲染renderer.js
- 将状态映射到 DOM支持多参与者网格、占位符、占位符切换、分辨率自适应、网络质量指示等
- 连接界面connect/connect.js
- 提供创建/加入通话、连接 ID 列表浏览、用户设置头像、昵称、ID、头像上传等功能
- 通话结束界面endcall/endcall.js
- 提供重新连接与离开选项
- 聊天消息chatmessage.js
- 负责消息的发送、接收、显示、未读计数与侧边栏切换
- 工具函数utils.js
- 时间格式化、通知、元素显示/隐藏、按钮状态切换等
- 应用入口main.js
- 初始化渲染器、绑定 DOM 事件、键盘快捷键、挂断确认对话框等
章节来源
- [models.js](file://client/public/onebyone/models.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
- [endcall/endcall.js](file://client/public/onebyone/endcall/endcall.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [utils.js](file://client/public/onebyone/utils.js)
- [main.js](file://client/public/onebyone/main.js)
## 架构总览
整体采用“状态驱动”的架构store.js 维护核心状态renderer.js 响应状态变化更新 UIWebRTC 通过 RenderStreaming 实例进行媒体协商与传输;聊天消息通过 WebSocket 或信令通道传递;配置通过 config.js 与 icesettings.js 提供 STUN/TURN 与媒体约束。
```mermaid
sequenceDiagram
participant U as "用户"
participant M as "main.js"
participant S as "store.js"
participant R as "renderer.js"
participant RS as "RenderStreaming(WebRTC)"
participant WS as "WebSocket/信令"
U->>M : 打开主界面
M->>S : 初始化并加入通话
S->>RS : 创建连接并启动
RS-->>S : 连接建立回调
S->>R : 通知 CALL_STATUS_CHANGE/ONGOING
S->>WS : 发送 user-info / media-state-changed
WS-->>S : 接收 chat-message / media-state-changed / user-info
S->>R : 通知 REMOTE_STREAM_OBTAINED / USER_LIST_UPDATE
R-->>U : 渲染远端视频/用户列表/网络质量
```
图表来源
- [main.js](file://client/public/onebyone/main.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
## 详细组件分析
### 数据模型models.js
- 类型定义
- CallSession通话会话包含通话类型、状态、起始时间、时长、加密标志、本地/远端用户信息
- LocalUser/RemoteUser用户信息含媒体状态与网络质量
- MediaState音频/视频/屏幕共享/录屏/说话检测状态
- ChatMessage消息结构含发送者信息、内容、类型、时间戳与是否自已发送
- 模拟数据
- 提供 mockCallSession 与 mockMessages便于开发调试与演示
章节来源
- [models.js](file://client/public/onebyone/models.js)
### 状态管理store.js
- 核心职责
- 初始化与本地媒体流获取、媒体状态变更、WebRTC 连接建立与回调注册
- 编解码器偏好设置、视频编码参数动态调整、分辨率切换
- 网络质量检测、音频活动检测、统计信息输出
- 聊天消息广播、用户信息同步、成员列表广播
- 通话结束处理(挂断、清理、通知)
- 关键流程
- 连接建立_createSignalingAndRTC -> setUp -> _registerCallbacks -> _startConnection
- 媒体协商onNewPeer/addTransceiver/setCodecPreferences/setVideoEncodingParameters
- 状态通知notify -> renderer 渲染
- 媒体状态变化emitMediaStateChange -> WebSocket 广播
- 网络质量detectNetworkQuality -> 综合丢包率/抖动/RTT 评估
- 音频活动startActivityDetection -> VAD 检测
- 设计要点
- 观察者模式subscribe/notify 解耦状态与 UI
- 可扩展性:编解码器与编码参数策略可按平台能力扩展
- 容错性:轨道替换、占位符延迟通知、分辨率回退
```mermaid
classDiagram
class CallStateManager {
+state
+listeners
+init()
+setUp(connectionId)
+updateLocalMedia(type, value)
+hangUp()
+broadcastParticipantsList()
+setCodecPreferences(participantId)
+setVideoEncodingParameters(participantId)
+changeResolution(width, height)
+emitMediaStateChange()
+detectNetworkQuality()
+startActivityDetection(stream, options)
+showStatsMessage()
}
class UIRenderer {
+render(state, changes)
+renderRemoteStream(stream, connectionId, isHost)
+renderUserList(localUser, remoteUser, participants)
}
CallStateManager --> UIRenderer : "通知状态变化"
```
图表来源
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
章节来源
- [store.js](file://client/public/onebyone/store.js)
### 视图渲染renderer.js
- 渲染策略
- 基于变化类型分派渲染INIT、DURATION_UPDATE、LOCAL_MEDIA_CHANGE、REMOTE_STREAM_OBTAINED、REMOTE_MEDIA_CHANGE、USER_LIST_UPDATE、NETWORK_CHANGE、CALL_STATUS_CHANGE、CALL_ENDED、PARTICIPANT_LEFT、RESOLUTION_CHANGED
- 支持多参与者网格Host 端与单路远端视频Participant 端)
- 占位符与占位符切换:音频先到、视频后到时延迟通知,避免黑屏
- 网络质量指示:根据远端网络质量更新头部与侧边栏
- 分辨率自适应:监听视频轨道 resize 事件调整显示尺寸
- 交互与事件
- 绑定事件监听器(由 main.js 绑定),响应按钮与快捷键
- 侧边栏开关与未读计数联动
章节来源
- [renderer.js](file://client/public/onebyone/renderer.js)
- [index.html](file://client/public/onebyone/index.html)
### 连接建立connect/connect.js
- 功能
- 加入通话:校验连接 ID保存到本地存储跳转主界面
- 创建通话:生成随机连接 ID保存并跳转主界面
- 浏览连接 ID调用 /signaling/connection-ids 获取列表并展示
- 用户设置:昵称、头像、用户 ID支持头像上传/api/upload/avatar
- 交互
- 输入框回车触发加入
- 选择连接 ID 自动填充
- 设置菜单外点击关闭
章节来源
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
- [connect/connect.html](file://client/public/onebyone/connect/connect.html)
### 通话结束处理endcall/endcall.js
- 功能
- 重新连接:跳转主界面
- 离开:清除本地连接 ID回到连接界面
- 交互
- 页面加载时显示断开连接信息(连接 ID 与时间)
章节来源
- [endcall/endcall.js](file://client/public/onebyone/endcall/endcall.js)
### 聊天消息chatmessage.js
- 功能
- 消息状态管理:消息列表、未读计数、侧边栏开关
- 发送消息:构造消息对象,通过 store 发送到远端
- 接收消息handleChatMessage -> addMessage -> 通知 UI
- 图片消息:限制大小、读取为 DataURL、发送文件消息
- 未读通知:侧边栏关闭时累加未读,打开时清零
- 事件绑定
- sendMessage/handleChatSubmit/openImagePicker/handleImageUpload
章节来源
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
### 工具函数utils.js
- 功能
- formatTime/formatTimestamp时间格式化
- generateId生成唯一 ID
- showNotification通知组件显示与隐藏
- toggleElement/toggleButtonState元素与按钮状态切换
章节来源
- [utils.js](file://client/public/onebyone/utils.js)
### 应用入口main.js
- 功能
- 初始化渲染器与聊天模块
- 绑定 DOM 事件:静音/视频切换、录屏、更多选项、分辨率切换、结束通话确认
- 键盘快捷键Space 静音、Ctrl+V 切换视频
- 接收通话请求弹窗:接受/拒绝
- 页面加载后检查连接 ID初始化并启动 WebRTC 连接
- 交互
- 对话框事件绑定、更多选项菜单外点击关闭
章节来源
- [main.js](file://client/public/onebyone/main.js)
- [index.html](file://client/public/onebyone/index.html)
## 依赖关系分析
- 模块依赖
- main.js 依赖 store、renderer、chatmessage、utils
- store.js 依赖 models、config、icesettings、chatmessage、utils
- renderer.js 依赖 models、chatmessage、utils、store
- connect/connect.js 依赖 utils
- endcall/endcall.js 依赖 utils
- config.js 依赖 icesettings.js
- 外部依赖
- WebRTC APIgetUserMedia、RTCPeerConnection、RTCRtpTransceiver
- WebSocket/信令通道(用于媒体状态、用户信息、聊天消息、参与者列表同步)
- 服务器接口:/config、/api/upload/avatar、/signaling/connection-ids
```mermaid
graph LR
main_js["main.js"] --> store_js["store.js"]
main_js --> renderer_js["renderer.js"]
main_js --> chat_js["chatmessage.js"]
store_js --> models_js["models.js"]
store_js --> config_js["config.js"]
store_js --> chat_js
renderer_js --> models_js
renderer_js --> chat_js
renderer_js --> utils_js["utils.js"]
store_js --> utils_js
connect_js["connect/connect.js"] --> utils_js
endcall_js["endcall/endcall.js"] --> utils_js
config_js --> icesettings_js["icesettings.js"]
```
图表来源
- [main.js](file://client/public/onebyone/main.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [models.js](file://client/public/onebyone/models.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [utils.js](file://client/public/onebyone/utils.js)
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
- [endcall/endcall.js](file://client/public/onebyone/endcall/endcall.js)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)
章节来源
- [main.js](file://client/public/onebyone/main.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [models.js](file://client/public/onebyone/models.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [utils.js](file://client/public/onebyone/utils.js)
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
- [endcall/endcall.js](file://client/public/onebyone/endcall/endcall.js)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)
## 性能考量
- 媒体轨道管理
- 音频先到、视频后到时延迟通知,避免黑屏与频繁 UI 刷新
- 替换视频轨道时保留音频轨道,减少资源开销
- 编解码器与编码参数
- 优先选择 AV1/VP9回退 H264提升压缩效率
- 根据采集分辨率动态设置最大比特率,平衡画质与带宽
- 网络质量检测
- 丢包率、抖动、RTT 综合评估,定期更新网络质量指示
- 统计信息
- 定时输出视频/音频统计,便于诊断与优化
- 分辨率切换
- 使用 applyConstraints 实时调整,避免重新获取流
章节来源
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
## 故障排查指南
- 无法获取本地媒体流
- 检查 getUserMedia 权限与约束;若失败,保持媒体状态关闭并通知 UI
- 远端视频黑屏
- 确认音频轨道先到时的延迟通知逻辑;检查轨道是否正确添加到 MediaStream
- 连接状态异常
- 检查 onConnect/onDisconnect 回调;确认信令通道正常
- 媒体状态不同步
- 确认 emitMediaStateChange 是否被调用;检查 onMessage 中 media-state-changed 分发
- 网络质量指示不更新
- 确认 startNetworkQualityDetection 是否启动;检查 detectNetworkQuality 统计字段
- 头像上传失败
- 检查文件类型与大小限制;确认 /api/upload/avatar 接口可用
章节来源
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [connect/connect.js](file://client/public/onebyone/connect/connect.js)
## 结论
本示例通过清晰的模块划分与状态驱动架构,实现了稳定的一对一视频通话体验。其关键优势在于:
- 状态与 UI 解耦,易于扩展与维护
- 完整的媒体协商与网络质量处理
- 丰富的聊天与用户信息同步机制
- 友好的用户界面与交互体验
建议在生产环境中进一步完善:
- 使用成熟的状态管理库(如 Redux/Pinia替代简易观察者模式
- 增强错误处理与重连机制
- 集成更多媒体能力检测与自适应策略
- 优化统计信息展示与告警机制
## 附录
### 开发指南:扩展与定制
- 扩展聊天功能
- 新增消息类型:在 ChatMessage 类型中扩展 type 字段,更新 chatmessage.js 的发送/接收逻辑
- 文件上传:参考 handleImageUpload 的流程,扩展更多文件类型与大小限制
- 自定义界面
- 修改 index.html 的布局与样式,注意与 renderer.js 的 DOM 选择器保持一致
- 新增控制按钮:在 main.js 绑定事件并在 store 中实现对应逻辑
- 集成其他服务
- 服务器配置:通过 /config 接口返回 useWebSocket 等配置store.js 中据此选择 WebSocketSignaling 或 Signaling
- ICE 服务器:通过 icesettings.js 的持久化配置,支持多 STUN/TURN 服务器
- WebRTC 优化
- 编解码器与编码参数:根据设备能力动态调整,提升兼容性与画质
- 分辨率与帧率:结合网络状况动态调整,保证流畅度
章节来源
- [models.js](file://client/public/onebyone/models.js)
- [store.js](file://client/public/onebyone/store.js)
- [renderer.js](file://client/public/onebyone/renderer.js)
- [chatmessage.js](file://client/public/onebyone/chatmessage.js)
- [config.js](file://client/public/js/config.js)
- [icesettings.js](file://client/public/js/icesettings.js)