WebSocket 实时视频流传输技术实践:从连接建立到画质控制
引言
本文将介绍我们在物联网设备管理平台中,WebSocket 实时视频流传输功能的开发历程。通过四个阶段的渐进式迭代,我们实现了一套稳定、支持双端的实时视频流系统,支持动态画质控制、分辨率切换,同时解决了连接稳定性、内存泄漏等问题。
系统架构背景
我们的项目是基于 Vue3 + TypeScript 的物联网设备管理平台,核心需求包括:
-
设备的实时视频流监控
-
温湿度等传感器数据展示
-
设备的远程管理与控制
-
同时支持桌面端与移动端的双端访问
为了实现低延迟的实时视频传输,我们选择了 WebSocket 协议,相比传统的 HTTP 轮询,它可以实现更低的延迟,同时支持双向的实时控制。
渐进式开发阶段
我们采用了渐进式的开发策略,从基础功能开始,逐步迭代优化,每个阶段都保证系统可正常运行:
阶段一:基础 WebSocket 连接实现
开发的第一步,我们先实现了最基础的 WebSocket 连接与数据接收功能,这是整个功能的基础:
// 核心状态管理const currentFrameImage = ref<string>('');const isStreamDisconnected = ref<boolean>(false);const currentFps = ref<number>(0);let ws: WebSocket | null = null;let currentImageUrl: string = '';
// 连接建立流程const startRealtimeMonitoring = async () => { // 先关闭旧的连接,避免连接泄漏 stopRealtimeMonitoring();
// 获取Token,建立连接 const token = await refreshToken(); const wsUrl = `/api/stream/viewer/ws/${deviceId}?token=${token}`; ws = new WebSocket(wsUrl);
// 连接状态检测:2.5秒检查一次,2秒无新帧标记为断开 ws.onopen = () => { streamCheckInterval = window.setInterval(() => { if (Date.now() - lastFrameTimestamp > 2000) { isStreamDisconnected.value = true; } }, 2500); };};最核心的是图片数据的处理,我们需要兼容多种数据格式,同时做好内存管理:
const handleFrameData = (data: any) => { // 兼容多种数据格式:base64、Blob、ArrayBuffer、Uint8Array let blob: Blob; if (typeof data === 'string') blob = base64ToBlob(data); else if (data instanceof Blob) blob = data; else if (data instanceof ArrayBuffer) blob = new Blob([data], { type: 'image/jpeg' }); else if (data instanceof Uint8Array) blob = new Blob([data], { type: 'image/jpeg' });
// 释放旧的URL,防止内存泄漏 if (currentImageUrl) URL.revokeObjectURL(currentImageUrl); // 创建新的URL用于图片展示 currentImageUrl = URL.createObjectURL(blob); currentFrameImage.value = currentImageUrl;
// 计算实时帧率 const now = Date.now(); currentFps.value = Math.round(1000 / (now - previousFrameTimestamp)); previousFrameTimestamp = now; lastFrameTimestamp = now;};同时我们配置了 Vite 的代理,支持 WebSocket 的代理转发,解决了开发环境的跨域问题。这一步我们修改了 6 个文件,新增了 404 行基础代码。
阶段二:画质与分辨率控制
完成了基础的连接后,我们添加了动态的画质与分辨率控制功能,让用户可以根据网络状况调整: 核心的逻辑是通过 WebSocket 发送控制命令,实时调整设备的摄像头参数:
// 发送画质设置到设备const sendStreamQuality = async (quality: number) => { if (!ws || ws.readyState !== WebSocket.OPEN) return;
// 双向控制协议 const message = JSON.stringify({ code: 1, item: 'camera', key: 'jpeg_quality', values: quality.toString() }); ws.send(message);};
// 监听滑块变化,自动反转值适配用户直觉watch(imageQuality, (newQuality) => { // 设备的参数是值越小质量越高,所以我们反转UI的值 // UI滑块:1-63(低-高),实际发送:63-1 const invertedQuality = 63 - newQuality; sendStreamQuality(invertedQuality);});同样的,我们也实现了分辨率的切换,支持 7 种不同的分辨率,从 128x128 到 FHD,用户可以根据需求选择。 UI 界面我们做了简单的控制面板,包含滑块和下拉选择,这一步我们新增了 110 行代码。
阶段三:移动端适配
完成了桌面端的功能后,我们对移动端做了适配,核心的业务逻辑完全复用桌面端的,仅 UI 层做了差异化:
-
布局调整:移动端使用垂直堆叠的布局,替代桌面端的水平排列,适配小屏
-
交互适配:增大了控件的尺寸,适配触摸操作
-
网络适配:针对移动端的网络环境,默认使用适中的画质,同时提供手动重连按钮
-
简化界面:移除了不必要的信息,简化了控制面板,保证移动端的简洁
这一步我们修改了移动端的组件,新增了 187 行适配代码,保证了双端的功能一致性。
阶段四:连接模式优化
最后,我们对连接模式做了优化,解决了之前的一些问题: 我们将原来的多设备共享连接的模式,改为了单设备单连接的模式,每个设备的视频流使用独立的 WebSocket 连接,服务器会自动根据 URL 中的设备 ID 开始推流,不需要额外的请求开启推流,简化了流程。 同时我们优化了消息的处理,支持区分控制命令响应和视频帧数据:
ws.onmessage = (event) => { // 先处理文本消息:控制命令响应、断开通知 if (typeof event.data === 'string') { try { // 尝试解析为JSON,处理控制响应 const data = JSON.parse(event.data); // 处理画质、分辨率的设置响应 } catch { // 普通文本消息,比如断开通知 connectionError.value = event.data; } } else { // 二进制数据,处理为视频帧 handleFrameData(event.data); }};这一步我们还修复了设备切换时旧连接未关闭的问题,优化了连接状态的管理,同时精简了 10 行冗余代码。
核心技术架构
连接生命周期
整个视频流连接的生命周期非常清晰:
1. 用户选择要监控的设备 ↓2. 获取访问Token,保证连接的安全性 ↓3. 建立WebSocket连接:/api/stream/viewer/ws/{deviceId}?token={token} ↓4. 连接成功,开始接收视频帧,同时可以发送控制命令 ↓5. 用户切换设备或离开页面时,自动关闭连接双向消息协议
我们设计了一套简单的双向消息协议,支持数据传输与控制:
| 消息类型 | 方向 | 格式 | 说明 |
|---|---|---|---|
| 视频帧数据 | Server → Client | Binary | JPEG 二进制图像数据 |
| 画质设置 | Client → Server | JSON | 动态调整 JPEG 压缩质量 |
| 分辨率设置 | Client → Server | JSON | 动态调整视频分辨率 |
| 控制响应 | Server → Client | JSON | 控制命令的执行结果 |
| 状态通知 | Server → Client | Text | 设备断开、错误等通知 |
性能优化策略
为了保证系统的稳定与高效,我们做了这些优化:
-
内存管理:每次更新帧的时候,都会释放旧的 Blob URL,避免内存泄漏
-
连接状态检测:定期检查帧的接收状态,2 秒无新帧自动标记为断开
-
动态画质调整:支持用户根据网络状况实时调整画质,无需重新连接
-
多格式兼容:兼容多种数据格式,保证不同环境下的兼容性
实现中的挑战与解决方案
在开发过程中,我们遇到了一些挑战,对应的解决方案如下:
-
多数据格式兼容:WebSocket 可能传输多种格式的图片数据,我们通过类型判断,兼容了所有常见的格式
-
用户直觉适配:设备的画质参数是反的(值越小质量越高),我们通过 UI 值的反转,让用户的操作符合直觉
-
移动端网络适配:针对移动端的不稳定网络,我们提供了手动重连,同时默认使用适中的画质
-
连接稳定性:我们实现了连接状态检测,同时在切换设备时自动关闭旧连接,避免连接泄漏
总结
通过这四个阶段的迭代开发,我们实现了一个稳定、高效的 WebSocket 实时视频流系统,目前这套系统支持:
-
低延迟的实时视频流传输
-
63 级的动态画质控制
-
7 种分辨率的实时切换
-
桌面端与移动端的双端自适应
-
稳定的连接状态管理
这次开发的关键经验是:
-
渐进式迭代:先实现基础连接,再添加控制功能,最后优化体验,降低开发风险
-
核心逻辑复用:双端共享所有的业务逻辑,仅 UI 层做差异化,减少重复代码
-
双向协议设计:简单清晰的双向消息协议,支持数据传输与实时控制
-
细节优化:内存管理、连接状态检测这些细节,保证了系统的长期稳定运行
部分信息可能已经过时









