# 一对一通信示例 **本文引用的文件** - [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) ## 目录 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.js:ICE 服务器配置持久化与读取 ```mermaid graph TB subgraph "客户端(onebyone)" A["index.html
主界面"] B["main.js
入口与事件绑定"] C["store.js
状态管理/WebRTC"] D["renderer.js
视图渲染"] E["models.js
数据模型"] F["chatmessage.js
聊天消息"] G["utils.js
工具函数"] H["connect/connect.html
连接界面"] I["connect/connect.js
连接逻辑"] J["endcall/endcall.js
结束逻辑"] end subgraph "公共配置" K["config.js
服务器/RTC配置"] L["icesettings.js
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 响应状态变化更新 UI;WebRTC 通过 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 API(getUserMedia、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)