Vue中WebSocket流页面刷新异常与SourceBuffer错误处理
WebSocket视频流Electron应用问题修复方案
问题1:页面刷新后无流传输仍接收Socket缓冲区数组
原因
页面刷新后,前端旧WebSocket连接断开,但后端未收到停止流的通知,仍持续推送视频帧;同时前端刷新后streaming_client_id、media_source被清空,但新Socket连接仍会接收后端推送的帧数据,导致无效处理。
修复方案
- 页面卸载前主动通知后端停止流:在组件卸载或页面刷新前,向WebSocket发送停止指令,让后端终止推送。
- 接收视频帧时增加双重校验:同时检查
media_source和streaming_client_id是否有效,避免无效数据处理。 - 清理Socket监听:组件卸载时移除所有Socket事件监听,防止内存泄漏。
代码修改示例
// 组件mounted钩子中添加页面刷新监听 mounted() { window.addEventListener('beforeunload', () => { if (this.socket && this.streaming_client_id) { this.socket.emit("stop-streaming", { id: this.streaming_client_id }); } }); } // 组件beforeUnmount钩子中清理资源 beforeUnmount() { if (this.socket && this.streaming_client_id) { this.socket.emit("stop-streaming", { id: this.streaming_client_id }); } // 移除所有Socket监听 this.socket.off("start-streaming"); this.socket.off("video-frame"); this.socket.off("stop-streaming"); window.removeEventListener('beforeunload', this.handleBeforeUnload); } // 修改video-frame监听逻辑 this.socket.on('video-frame', (data) => { // 增加streaming_client_id有效性校验 if(!this.media_source || !this.streaming_client_id) return; // ...原有统计计算逻辑 const arrayU8 = new Uint8Array(data); // 检查MediaSource和SourceBuffer状态 if (this.media_source.readyState === 'open' && this.media_source.sourceBuffers.length > 0) { try { this.media_source.sourceBuffers[0].appendBuffer(arrayU8); } catch (e) { console.error('Append buffer failed:', e); } } else { console.log('Media source or SourceBuffer not ready'); } });
问题2:打开模态框时MediaSource/SourceBuffer报错
原因
- 模态框关闭时未完整清理MediaSource资源,导致旧SourceBuffer已被移除但仍被引用;
- 重复创建MediaSource或重复设置video.src,引发MediaSource状态异常;
- 模态框打开时,
sourceopen事件未触发完成就尝试操作SourceBuffer。
修复方案
- 模态框关闭时完整清理资源:终止流、移除SourceBuffer、释放URL对象、重置相关引用;
- 保存SourceBuffer独立引用,避免直接通过
sourceBuffers[0]访问,操作前检查有效性; - 等待
sourceopen事件触发后再处理视频帧,防止提前操作; - 初始化MediaSource前先清理旧的无效资源。
代码修改示例
// 新增模态框关闭时的清理方法 closeStreamModal() { this.is_stream_modal_open = false; if (this.remote_video) { this.remote_video.pause(); URL.revokeObjectURL(this.remote_video.src); // 释放URL对象 this.remote_video.src = ''; } if (this.media_source) { try { if (this.media_source.readyState === 'open') { this.media_source.endOfStream(); } // 移除所有SourceBuffer while (this.media_source.sourceBuffers.length > 0) { this.media_source.removeSourceBuffer(this.media_source.sourceBuffers[0]); } } catch (e) { console.error('Cleanup MediaSource failed:', e); } this.media_source = null; this.sourceBuffer = null; // 新增SourceBuffer独立引用 } } // 修改initMediaSource方法 initMediaSource() { // 先清理旧的无效资源 if (this.media_source) { try { if (this.media_source.readyState === 'open') { this.media_source.endOfStream(); } while (this.media_source.sourceBuffers.length > 0) { this.media_source.removeSourceBuffer(this.media_source.sourceBuffers[0]); } } catch (e) { console.error('Cleanup old MediaSource failed:', e); } this.media_source = null; this.sourceBuffer = null; } this.media_source = new MediaSource(); this.media_source.addEventListener('sourceopen', () => { console.log('MediaSource sourceopen'); try { this.sourceBuffer = this.media_source.addSourceBuffer('video/webm; codecs="vp8"'); console.log('SourceBuffer created', this.sourceBuffer); this.sourceBuffer.addEventListener('updateend', () => { console.log('SourceBuffer updateend'); if (this.remote_video && this.remote_video.paused) { this.remote_video.play().catch(e => console.error('Play failed:', e)); } }); this.sourceBuffer.addEventListener('error', (event) => { console.error('SourceBuffer error:', event); }); // 监听SourceBuffer被移除事件 this.sourceBuffer.addEventListener('abort', () => { console.log('SourceBuffer aborted'); this.sourceBuffer = null; }); } catch (e) { console.error('Create SourceBuffer failed:', e); } }); this.media_source.addEventListener('sourceclose', () => { console.log('MediaSource sourceclose'); this.sourceBuffer = null; this.media_source = null; }); if(this.remote_video) { URL.revokeObjectURL(this.remote_video.src); // 先释放旧URL this.remote_video.src = URL.createObjectURL(this.media_source); } } // 修改video-frame中的appendBuffer逻辑 this.socket.on('video-frame', (data) => { if(!this.media_source || !this.streaming_client_id || !this.sourceBuffer) return; // ...统计计算逻辑 const arrayU8 = new Uint8Array(data); // 检查SourceBuffer是否正在更新 if (this.media_source.readyState === 'open' && !this.sourceBuffer.updating) { try { this.sourceBuffer.appendBuffer(arrayU8); } catch (e) { console.error('Append buffer failed:', e); // 若报错为SourceBuffer已移除,重置引用 if (e.message.includes('removed from the parent media source')) { this.sourceBuffer = null; } } } });
内容的提问来源于stack exchange,提问作者Himanshu Rahi
相关产品推荐
相关产品推荐

