# webrtc **Repository Path**: pkmer/webrtc ## Basic Information - **Project Name**: webrtc - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2025-06-03 - **Last Updated**: 2025-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [Bilibili WebRTC Course](https://www.bilibili.com/video/BV1kg4y127zr?spm_id_from=333.788.player.switch&vd_source=f9745f81b4981bb1eca8c2d80be33ff9&p=6) | 特性 | | ------ | | 获取媒体流 | | 获取设备列表 | | 分辨率选择 | | 录制视频 | | 共享屏幕 | | ssl证书生成 | | 基于原生的websocket优化成socket.io基于路由的事件监听,支持typescript | | vue重构完成 | | 视频设备切换 | | 视频隐藏 | ## webrtc的连接 1. 信令服务器(Signaling)协商如何建立连接 1. 前端使用socket.io连接信令服务器 2. ICE(NAT穿透) 找到NAT设备的映射关系 3. PeerConnection webrtc建立之后客户端的直连通道 ### wss 自建证书,在程序中直接使用wss会连接不上,最简单的方式先使用`https://restful-api`,接收该证书,然后程序就能连接成功。为了避免每次的证书的认证,可以在系统中设置。 ## 信令服务器 > 基于springboot原生的websocket,设计一个灵活的路由系统,支持typescript。 ![](./attachment/websocket_ts.png) ### springboot websocket消息大小限制 WebSocket 默认限制: 1. Spring Boot WebSocket 默认消息大小限制通常为 64KB 左右 2. 这是为了防止内存耗尽和拒绝服务攻击 在交互ice的过程中,会产生大量的ice候选,所以需要注意消息的大小限制。当超过消息大小限制,springboot会自动关闭websocket连接。 --- ### WebsocketSession.sendMessage线程安全问题 > The underlying standard WebSocket session (JSR-356) does **not allow concurrent sending**. Therefore, sending must be synchronized. 用ConcurrentWebSocketSessionDecorator包装WebSocketSession,以确保线程安全。 其底层使用`private final Queue> buffer = new LinkedBlockingQueue<>();`使用了一个阻塞队列来存储消息,以及ReentrantLock来实现线程安全。 ```java private final Queue> buffer = new LinkedBlockingQueue<>(); private final Lock flushLock = new ReentrantLock(); public void sendMessage(WebSocketMessage message) throws IOException { // 往阻塞队列中添加消息 this.buffer.add(message); do { if (!tryFlushMessageBuffer()) { break; } }while (!this.buffer.isEmpty()); } private boolean tryFlushMessageBuffer() throws IOException { // 获得锁 if (this.flushLock.tryLock()) { try { while (true) { WebSocketMessage message = this.buffer.poll(); if (message == null || shouldNotSend()) { break; } // 获得包裹的session发送 getDelegate().sendMessage(message); this.sendStartTime = 0; } } finally { // 释放锁 this.flushLock.unlock(); } } } ``` 1. `poll()` 是 非阻塞 操作,当队列为空时,返回null。 2. `take()` 方法才会在队列为空时阻塞,直到队列中有数据。 ## https证书 ```sh mkcert create-ca mkcert create-cert ``` springboot ```sh openssl pkcs12 -export -out keystore.p12 -inkey cert.key -in cert.crt ``` vite ```ts server: { port: 5373, host: true, https: { cert: "./ssl/cert.crt", key: "./ssl/cert.key", }, }, ``` --- ## vue project 1. jwt引入 2. date-fns.js 3. mousemove show buttons 4. click button show feed 5. 状态中心化 ```ts { who: { who: stream, peerConnection } } 比如{ "localStream": { who: "localStream", stream: localStream --> navigator.mediaDevices.getUserMedia(), peerConnection?: peerConnection --> new RTCPeerConnection() }, "remoteStream": { who: "remoteStream", stream: remoteStream --> new MediaStream() --> set track from peerConnection track, peerConnection: peerConnection --> new RTCPeerConnection() } } ``` 6. 使用provide和inject提供videoEl 7. click play video 处理localStream还没加载的状态,使用一个中间状态pending 8. websocket拦截了