# webrtc **Repository Path**: yuefb/webrtc ## Basic Information - **Project Name**: webrtc - **Description**: 企业远程会议系统(WebRTC + mediasoup SFU) - **Primary Language**: TypeScript - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-08-19 - **Last Updated**: 2026-08-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 企业远程会议系统(WebRTC + mediasoup SFU) > 目标:支持多人语音/视频会议、屏幕分享;后端包含完整 WebRTC 媒体转发(SFU)与信令;前端为 Web(PC + H5 自适应)。 ## 目录结构 - `server/`:Node.js + TypeScript + mediasoup(SFU)+ WebSocket 信令 - `client/`:Vite + React + TypeScript + mediasoup-client(入会端) ## 架构概览 ### 1) 单仓库工作区 - 根目录使用 npm workspaces 管理 `server` / `client` - `npm run dev` 会并行启动后端与前端 ### 2) 关键模块(读代码入口) - 后端入口:`server/src/index.ts` - HTTP API:登录、用户信息、会议创建/列表、管理员接口 - WebSocket `/ws`:房间信令(join / transport / produce / consume / chat / leave) - mediasoup:创建 worker/router/transport,负责 SFU 转发 - 后端 DB:`server/src/db.ts`(可选 Postgres) - users / sessions / meetings / meeting_participants - 参会记录用于后台“在线/历史”与审计 - 前端媒体信令:`client/src/meetingClient.ts` - WebSocket 请求-响应封装(带 id) - mediasoup-client:Device、send/recv transport、produce/consume - 前端业务控制:`client/src/meeting/useMeetingController.ts` - 将 MeetingClient 的事件映射为 React state(远端轨、参会者、聊天、录屏审批等) - 前端入口与路由:`client/src/App.tsx`、`client/src/routes.ts` ## 本地运行(macOS) 需要 Node.js 22+(`mediasoup` 与 Vite 对 Node 版本有要求)。 如果你用 Homebrew 安装了 `node@22`,可在当前终端会话临时切换: ```bash export PATH="/usr/local/opt/node@22/bin:$PATH" node -v ``` 1) 安装依赖 ```bash cd /Users/zhanqi/Desktop/webrtc npm install ``` 2) 启动(后端 + 前端) ```bash npm run dev ``` 3) 打开浏览器 - 前端:`http://localhost:5173` - 后端健康检查:`http://localhost:4000/health` ## 核心流程(信令 + 媒体) ### 1) 会议/登录(HTTP) - `POST /api/login`:账号密码登录,返回 token(Bearer) - `GET /api/me`:用 token 获取当前用户信息 - `GET /api/meetings`:获取我创建的会议列表 - `POST /api/meetings`:创建会议(返回 meetingId) - `GET /api/meetings/:id`:校验会议是否存在/过期(邀请链接校验) ### 2) 入会与 SFU 转发(WebSocket + mediasoup) 客户端(mediasoup-client)与服务端(mediasoup)通过 WebSocket 交换参数,整体模式为: - join:服务端返回 routerRtpCapabilities、现有参会者、现有 producers 列表 - createWebRtcTransport:分别创建 send/recv transport,并返回 ICE/DTLS 参数 - connectTransport:客户端回传 dtlsParameters 完成 transport.connect - produce:上行(麦克风/摄像头/屏幕)创建 producer;服务端推送 newProducer 给其他人 - consume:下行订阅远端 producer;服务端创建 consumer(初始 paused),客户端创建本地 consumer 后再 resume - leave / ws close:清理 peer、释放资源、通知 peerLeft ### 3) 会议协作能力 - 聊天:`sendChat` 广播 `chatMessage` - 扬声器开关状态:`setSpeakerEnabled` 广播 `peerState` - 录屏审批(本地录制 + 主持人审批信令): - 参会者 `requestRecord` -> 主持人收到 `recordRequest` - 主持人 `respondRecordRequest` -> 参会者收到 `recordRequestResult` - 实际录制为浏览器本地 MediaRecorder(非服务端混流录制) ## 端到端时序图 ```mermaid sequenceDiagram autonumber participant UI as Web UI (React) participant MC as MeetingClient participant WS as WebSocket /ws participant SFU as Server (mediasoup) participant Router as mediasoup Router UI->>MC: join(roomId, displayName, token?) MC->>WS: {id, type:"join", roomId, displayName, token?} WS->>SFU: 校验会议/人数/身份 SFU->>Router: getOrCreateRoom(roomId)\ncreateRouter(mediaCodecs) SFU-->>WS: {peerId, routerRtpCapabilities,\nroomPeers, existingProducers} WS-->>MC: join response MC->>MC: device.load(routerRtpCapabilities) MC->>WS: {id, type:"createWebRtcTransport", direction:"send"} WS->>SFU: router.createWebRtcTransport(listenIps, ports) SFU-->>WS: {transport params} WS-->>MC: sendTransport params MC->>MC: device.createSendTransport(params) MC->>WS: {id, type:"connectTransport", transportId, dtlsParameters} WS->>SFU: transport.connect(dtlsParameters) SFU-->>WS: ok WS-->>MC: ok MC->>WS: {id, type:"createWebRtcTransport", direction:"recv"} WS->>SFU: router.createWebRtcTransport(...) SFU-->>WS: {transport params} WS-->>MC: recvTransport params MC->>MC: device.createRecvTransport(params) MC->>WS: {id, type:"connectTransport", transportId, dtlsParameters} WS->>SFU: transport.connect(dtlsParameters) SFU-->>WS: ok WS-->>MC: ok Note over UI,MC: 开麦/开摄像头/共享屏幕:produce UI->>MC: setMicEnabled(true) / setCamEnabled(true) / setScreenEnabled(true) MC->>WS: {id, type:"produce", transportId, kind, rtpParameters, appData} WS->>SFU: transport.produce(...) SFU-->>WS: {producerId} WS-->>MC: {producerId} SFU-->>WS: push {type:"newProducer", producerId, peerId, kind, appData} WS-->>MC: push newProducer (其他人收到) Note over MC: 收到 newProducer 后 consume + resume MC->>WS: {id, type:"consume", transportId:recv, producerId, rtpCapabilities} WS->>SFU: router.canConsume + transport.consume(paused:true) SFU-->>WS: {consumerId, rtpParameters, kind, peerId, displayName, appData} WS-->>MC: consume response MC->>MC: recvTransport.consume(...) MC->>WS: {id, type:"resume", consumerId} WS->>SFU: consumer.resume() SFU-->>WS: ok WS-->>MC: ok UI->>MC: leave() MC->>WS: {id, type:"leave"} WS->>SFU: cleanupPeer()\nclose transports/producers/consumers SFU-->>WS: push {type:"peerLeft"} WS-->>MC: push peerLeft (房间内其他人) ``` ## H5(手机)局域网入会 1) 让手机和电脑在同一 Wi-Fi 2) 在 `server/.env` 中配置:`MEDIASOUP_LISTEN_IP=你的电脑局域网IP` 3) 启动后,在手机浏览器打开:`http://<电脑局域网IP>:5173` ## app 已完成的工作 1. React Native 项目创建 - mobile/ 目录 - 基于 React Native 0.83.1 - 已安装依赖:react-native-webrtc, mediasoup-client, @react-navigation/native 等 2. 核心代码实现 - src/meeting/MeetingClient.ts - WebRTC 客户端(1:1 复刻 client 端) - src/screens/LoginScreen.tsx - 登录页面 - src/screens/MeetingScreen.tsx - 会议页面(包含视频通话、聊天、控制栏) - src/utils/api.ts - API 请求封装 - src/utils/storage.ts - 本地存储封装 - src/utils/time.ts - 时间格式化工具 - App.tsx - 应用入口 3. iOS 配置 - Info.plist - 已配置相机、麦克风、相册权限 - 已配置后台音频和 VoIP 模式 - 已允许 HTTP 请求(NSAllowsArbitraryLoads) 调试步骤 # 1. 进入 mobile 目录 cd /Users/zhanqi/Documents/gitlab/webrtc/mobile # 2. 安装 CocoaPods 依赖 cd ios && pod install # 3. 启动 Metro bundler cd .. && npm start # 4. 在另一个终端运行 iOS、Android 应用 npm run ios npm run android npm run ios:release npm run android:release 项目结构 mobile/ ├── App.tsx # 应用入口 ├── src/ │ ├── components/ # UI 组件 │ │ ├── Icons.tsx # 图标组件 │ │ ├── MeetingChatModal.tsx # 会议聊天弹窗 │ │ ├── MeetingControlBar.tsx # 会议控制栏 │ │ ├── MeetingParticipantsModal.tsx # 参会人员弹窗 │ │ └── MeetingVideoGrid.tsx # 视频网格布局 │ ├── context/ │ │ └── AuthContext.tsx # 用户认证上下文 │ ├── hooks/ │ │ └── useMeeting.ts # 会议核心逻辑 Hook │ ├── meeting/ │ │ ├── MeetingClient.ts # Meeting 客户端入口(对外 API) │ │ ├── MeetingClientImpl.ts # MeetingClient 具体实现(组合各模块) │ │ ├── MeetingClientWs.ts # WebSocket 连接与消息收发 │ │ ├── MeetingClientSession.ts # 入会/退会/重连与会话状态 │ │ ├── MeetingClientMedia.ts # 麦克风/摄像头/音视频相关 │ │ ├── MeetingClientScreenShare.ts # 屏幕共享相关 │ │ ├── MeetingClientBase.ts # 共享状态与工具方法 │ │ └── meetingTypes.ts # 类型定义 │ ├── screens/ │ │ ├── HistoryScreen.tsx # 历史会议页面 │ │ ├── HomeScreen.tsx # 首页(会议列表、预定会议) │ │ ├── LoginScreen.tsx # 登录页面 │ │ ├── MeetingDetailScreen.tsx # 会议详情页面 │ │ └── MeetingScreen.tsx # 会议进行中页面 │ ├── types/ │ │ └── meeting.ts # 会议相关类型定义 │ └── utils/ │ ├── api.ts # API 封装 │ ├── clipboard.ts # 剪贴板工具 │ ├── storage.ts # 存储封装 │ └── time.ts # 时间工具 ├── ios/ │ └── MeetingMobile/ │ └── Info.plist # iOS 权限配置 └── package.json 功能对照(1:1 复刻 client) | Web 端功能 | 移动端实现 | |----------------|------------------| | 登录认证 | ✅ LoginScreen | | 会议列表 | ✅ MeetingScreen | | 创建会议 | ✅ Modal 弹窗 | | 加入会议 | ✅ joinMeeting | | 视频通话 | ✅ RTCView | | 麦克风控制 | ✅ toggleMic | | 摄像头控制 | ✅ toggleCam | | 前后摄像头切换 | ✅ switchCamera | | 扬声器控制 | ✅ toggleSpeaker | | 聊天功能 | ✅ Chat Modal | | 离开会议 | ✅ handleLeave | ## 生产部署(TURN + Nginx) 见 [deploy/README.md](deploy/README.md) ## 关键配置(跨设备/公网必读) - `MEDIASOUP_LISTEN_IP`:SFU 监听地址(本机开发常用 127.0.0.1;局域网/容器可用 0.0.0.0 或内网 IP) - `MEDIASOUP_ANNOUNCED_IP`:客户端可达的 IP(公网/EIP 或局域网 IP),决定 ICE 候选是否可连通 - `MEDIASOUP_RTC_MIN_PORT` / `MEDIASOUP_RTC_MAX_PORT`:媒体端口范围(建议缩小并放通 UDP) - `ICE_SERVERS`:下发给浏览器的 STUN/TURN 列表(JSON 数组) - 生产环境建议:Nginx 同域反代 `/api` 与 `/ws`,并使用 HTTPS/WSS ## 常见问题 - **权限**:首次使用需要允许浏览器访问麦克风/摄像头/屏幕。 - **跨设备(手机/H5)**:需要让客户端能连到后端的 ICE 候选地址。 - 局域网:设置 `MEDIASOUP_LISTEN_IP=你的电脑局域网IP`(可参考 `server/.env.example`)。 - 公网/NAT:通常需要设置 `MEDIASOUP_ANNOUNCED_IP=公网IP`,并放通 UDP 端口范围。 - **HTTPS**:除 localhost 外,浏览器通常要求 HTTPS 才能使用摄像头/屏幕分享。