diff --git a/src/index.html b/src/index.html index bf49711..5f3879b 100644 --- a/src/index.html +++ b/src/index.html @@ -451,7 +451,7 @@
- 头像 + 头像
diff --git a/src/index.js b/src/index.js index 029b1da..013ff45 100644 --- a/src/index.js +++ b/src/index.js @@ -9,8 +9,6 @@ class WebRTCChat { constructor() { console.log('WebRTCChat 构造函数开始执行'); - - // 初始化历史消息(异步) this.initializeHistory(); @@ -30,6 +28,10 @@ class WebRTCChat { this.precreatedStreams = new Map(); // 预创建的视频流 this.importantVideos = ['d-3s.mp4', 's-1.mp4']; // 重要视频列表 this.isInitialized = false; + // 添加视频加载状态标志 + this.isVideoReady = false; + this.isDefaultVideoLoaded = false; + this.retryCount = 0; // 添加重试计数器 // 添加视频相关属性 this.videoSender = null; // WebRTC视频发送器 @@ -67,7 +69,9 @@ class WebRTCChat { this.initializeSocket(); this.loadVideoMapping(); this.loadVideoList(); - this.loadDefaultVideo(); + + // 只预加载视频资源,不显示视频 + this.preloadVideoResources(); this.bindEvents(); // 在初始化完成后预加载常用视频 @@ -76,7 +80,7 @@ class WebRTCChat { this.preloadCommonVideos().catch(error => { this.logMessage(`预加载过程出错: ${error.message}`, 'error'); }); - }, 500); // 延迟2秒开始预加载,避免影响 + }, 500); // 预创建重要视频流 setTimeout(() => { @@ -88,6 +92,30 @@ class WebRTCChat { window.webrtcApp = this; } + // 新增方法:预加载视频资源 + async preloadVideoResources() { + try { + await this.loadDefaultVideo(); + // 预创建视频流但不设置到video元素 + const defaultStream = await this.createVideoStreamOptimized(this.defaultVideo); + this.precreatedStreams.set(this.defaultVideo, defaultStream); + + this.isVideoReady = true; + this.isDefaultVideoLoaded = true; + + // 启用开始通话按钮 + // if (this.startButton) { + // this.startButton.disabled = false; + // this.startButton.style.opacity = '1'; + // } + + console.log('视频资源预加载完成,可以开始通话', 'success'); + } catch (error) { + this.logMessage(`视频资源预加载失败: ${error.message}`, 'error'); + this.isVideoReady = false; + } + } + initializeElements() { // 视频元素 this.localVideo = document.getElementById('localVideo'); @@ -111,6 +139,14 @@ class WebRTCChat { this.stopButton = document.getElementById('stopButton'); this.muteButton = document.getElementById('muteButton'); this.sendTextButton = document.getElementById('sendTextButton'); + + // 初始状态下禁用开始通话按钮,直到视频加载完成 + if (this.startButton) { + this.startButton.disabled = true; + this.startButton.style.opacity = '0.5'; + // this.startButton.title = '加载中,请稍候...'; + } + // this.startVoiceButton = document.getElementById('startVoiceButton'); // this.stopVoiceButton = document.getElementById('stopVoiceButton'); // this.defaultVideoButton = document.getElementById('defaultVideoButton'); @@ -165,7 +201,7 @@ class WebRTCChat { // 通话开始处理 this.socket.on('call-started', (data) => { console.log('通话已开始', 'success'); - this.startDefaultVideoStream(); + // 移除这里的视频流启动,因为现在在startCall中处理 }); // 场景切换处理 @@ -204,7 +240,7 @@ class WebRTCChat { this.videoMapping = data.mapping; this.interactionVideo = data.mapping['8-4-sh']; this.defaultVideo = data.mapping["default"]; - this.logMessage('视频映射加载成功', 'success'); + console.log('映射加载成功', 'success'); } catch (error) { this.logMessage('加载视频映射失败: ' + error.message, 'error'); } @@ -264,18 +300,35 @@ class WebRTCChat { async startDefaultVideoStream() { try { - this.logMessage('开始创建默认视频流', 'info'); + console.log('开始创建默认视频流', 'info'); + + // 显示视频元素 + if (this.recordedVideo) { + this.recordedVideo.style.display = 'block'; + } // 添加加载状态 this.recordedVideo.classList.add('loading'); // 创建默认视频的MediaStream - const defaultStream = this.precreatedStreams.get(this.defaultVideo); + let defaultStream = this.precreatedStreams.get(this.defaultVideo); + + // 检查流是否有效,如果无效则重新创建 + if (!defaultStream || defaultStream.getTracks().length === 0 || + defaultStream.getTracks().some(track => track.readyState === 'ended')) { + console.log('预创建流无效,重新创建默认视频流'); + try { + defaultStream = await this.createVideoStreamOptimized(this.defaultVideo); + this.precreatedStreams.set(this.defaultVideo, defaultStream); + } catch (createError) { + throw new Error(`重新创建默认视频流失败: ${createError.message}`); + } + } // 等待流稳定 await new Promise(resolve => setTimeout(resolve, 1000)); - // 检查流是否有效 + // 再次检查流是否有效 if (!defaultStream || defaultStream.getTracks().length === 0) { throw new Error('默认视频流创建失败'); } @@ -311,20 +364,39 @@ class WebRTCChat { // 确保视频开始播放 try { await this.recordedVideo.play(); - this.logMessage('默认视频开始播放', 'success'); + // this.logMessage('默认视频开始播放', 'success'); // 移除加载状态,添加播放状态 this.recordedVideo.classList.remove('loading'); this.recordedVideo.classList.add('playing'); } catch (playError) { - this.logMessage(`默认视频播放失败: ${playError.message}`, 'error'); + // this.logMessage(`默认视频播放失败: ${playError.message}`, 'error'); this.recordedVideo.classList.remove('loading'); } + + // 再次检查视频是否准备就绪 + // this.startButton.disabled = false; - this.logMessage('默认视频流创建成功', 'success'); + console.log('默认流创建成功', 'success'); } catch (error) { - this.logMessage('创建默认视频流失败: ' + error.message, 'error'); + console.log('创建默认视频流失败: ' + error.message, 'error'); + + // 隐藏视频元素 + if (this.recordedVideo) { + this.recordedVideo.style.display = 'none'; + } this.recordedVideo.classList.remove('loading'); + + // 添加重试机制 + if (!this.retryCount) this.retryCount = 0; + if (this.retryCount < 2) { + this.retryCount++; + console.log(`尝试重新创建默认视频流 (${this.retryCount}/2)`); + setTimeout(() => this.startDefaultVideoStream(), 1000); + } else { + this.retryCount = 0; + console.log('默认视频流创建失败,已达到最大重试次数', 'error'); + } } } @@ -368,6 +440,11 @@ class WebRTCChat { } this.isInitialized = true; + // 启用开始通话按钮 + if (this.startButton) { + this.startButton.disabled = false; + this.startButton.style.opacity = '1'; + } this.logMessage('重要视频流预创建完成', 'success'); } @@ -988,8 +1065,14 @@ class WebRTCChat { } bindEvents() { - // 开始通话按钮 - this.startButton.onclick = () => this.startCall(); + // 开始通话按钮 - 添加视频准备状态检查 + this.startButton.onclick = () => { + if (!this.isVideoReady) { + this.logMessage('还在加载中,请稍候...', 'warning'); + return; + } + this.startCall(); + }; // 停止通话按钮 - 改为调用 userDisconnect this.stopButton.onclick = () => this.userDisconnect(); @@ -1042,13 +1125,27 @@ class WebRTCChat { async startCall() { try { - // 立即隐藏开始通话按钮 + // 检查所有必要条件 + if (!this.isVideoReady || !this.isDefaultVideoLoaded) { + this.logMessage('视频资源尚未准备就绪,请稍候...', 'warning'); + return; + } + + if (!this.socket || !this.socket.connected) { + this.logMessage('网络连接未就绪,请稍候...', 'warning'); + return; + } + + // 隐藏开始通话按钮 this.startButton.style.display = 'none'; // 显示等待连接提示 this.showConnectionWaiting(); // 切换到通话中图标 this.switchToCallingIcon(); + // 现在才开始显示视频 + await this.startDefaultVideoStream(); + // 添加更详细的错误处理 console.log('开始请求麦克风权限...'); this.localStream = await navigator.mediaDevices.getUserMedia({ @@ -1091,11 +1188,12 @@ class WebRTCChat { this.hideConnectionWaiting(); } catch (error) { + this.logMessage(`开始通话失败: ${error.message}`, 'error'); + // 恢复开始通话按钮 this.startButton.style.display = 'block'; // 如果出错,隐藏等待连接提示并恢复到默认图标 this.hideConnectionWaiting(); this.switchToDefaultIcon(); - this.logMessage('无法访问麦克风: ' + error.message, 'error'); } } @@ -1133,20 +1231,40 @@ class WebRTCChat { this.stopButton.style.display = 'none'; this.stopButton.disabled = true; + // 隐藏视频元素 + if (this.recordedVideo) { + this.recordedVideo.style.display = 'none'; + this.recordedVideo.srcObject = null; + } + if (this.recordedVideoBuffer) { + this.recordedVideoBuffer.style.display = 'none'; + this.recordedVideoBuffer.srcObject = null; + } + // 显示开始通话按钮 - this.startButton.style.display = 'block'; this.startButton.disabled = false; + this.startButton.style.display = 'block'; + this.startButton.style.opacity = '1'; // 移除页面刷新,保持websocket连接 // setTimeout(() => { // window.location.reload(); // }, 300); + // 清理视频缓存和预创建流 + this.clearVideoCache(); + setTimeout(() => { // 显示头像,隐藏视频 if (this.videoContainer) { this.videoContainer.classList.remove('calling'); } + + // 重新初始化重要视频流 + this.precreateImportantVideos().then(() => { + // 重新启动默认视频流 + this.startDefaultVideoStream(); + }); }, 300); } @@ -1164,7 +1282,7 @@ class WebRTCChat { console.log('用户主动断开,300ms后刷新页面清除缓存...'); setTimeout(() => { window.location.reload(); - }, 300); + }, 100); } // 清除视频缓存的方法 @@ -1194,6 +1312,19 @@ class WebRTCChat { // 清除视频流映射缓存 this.videoStreams.clear(); + // 清除预创建的视频流 + if (this.precreatedStreams) { + this.precreatedStreams.forEach((stream, videoFile) => { + if (stream) { + stream.getTracks().forEach(track => track.stop()); + } + }); + this.precreatedStreams.clear(); + } + + // 重置初始化状态,允许重新预创建 + this.isInitialized = false; + // 重置视频相关状态 this.currentVideo = null; this.activeVideoElement = 'main';