等待视频加载完成
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 2m53s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 2m53s
This commit is contained in:
parent
a73165a6a1
commit
0478bb4cdd
@ -451,7 +451,7 @@
|
|||||||
<div class="avatar-container" id="avatarContainer">
|
<div class="avatar-container" id="avatarContainer">
|
||||||
<div class="avatar" id="avatar">
|
<div class="avatar" id="avatar">
|
||||||
<!-- 使用相对路径引用图片 -->
|
<!-- 使用相对路径引用图片 -->
|
||||||
<img src="./tx.png" alt="头像" onerror="this.style.display='none'; this.parentElement.innerHTML='AI';">
|
<img src="./tx.png" alt="头像" onerror="this.style.display='none'; this.parentElement.innerHTML='零和一';">
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="avatar-name">AI助手</div> -->
|
<!-- <div class="avatar-name">AI助手</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
169
src/index.js
169
src/index.js
@ -9,8 +9,6 @@ class WebRTCChat {
|
|||||||
constructor() {
|
constructor() {
|
||||||
console.log('WebRTCChat 构造函数开始执行');
|
console.log('WebRTCChat 构造函数开始执行');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化历史消息(异步)
|
// 初始化历史消息(异步)
|
||||||
this.initializeHistory();
|
this.initializeHistory();
|
||||||
|
|
||||||
@ -30,6 +28,10 @@ class WebRTCChat {
|
|||||||
this.precreatedStreams = new Map(); // 预创建的视频流
|
this.precreatedStreams = new Map(); // 预创建的视频流
|
||||||
this.importantVideos = ['d-3s.mp4', 's-1.mp4']; // 重要视频列表
|
this.importantVideos = ['d-3s.mp4', 's-1.mp4']; // 重要视频列表
|
||||||
this.isInitialized = false;
|
this.isInitialized = false;
|
||||||
|
// 添加视频加载状态标志
|
||||||
|
this.isVideoReady = false;
|
||||||
|
this.isDefaultVideoLoaded = false;
|
||||||
|
this.retryCount = 0; // 添加重试计数器
|
||||||
|
|
||||||
// 添加视频相关属性
|
// 添加视频相关属性
|
||||||
this.videoSender = null; // WebRTC视频发送器
|
this.videoSender = null; // WebRTC视频发送器
|
||||||
@ -67,7 +69,9 @@ class WebRTCChat {
|
|||||||
this.initializeSocket();
|
this.initializeSocket();
|
||||||
this.loadVideoMapping();
|
this.loadVideoMapping();
|
||||||
this.loadVideoList();
|
this.loadVideoList();
|
||||||
this.loadDefaultVideo();
|
|
||||||
|
// 只预加载视频资源,不显示视频
|
||||||
|
this.preloadVideoResources();
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|
||||||
// 在初始化完成后预加载常用视频
|
// 在初始化完成后预加载常用视频
|
||||||
@ -76,7 +80,7 @@ class WebRTCChat {
|
|||||||
this.preloadCommonVideos().catch(error => {
|
this.preloadCommonVideos().catch(error => {
|
||||||
this.logMessage(`预加载过程出错: ${error.message}`, 'error');
|
this.logMessage(`预加载过程出错: ${error.message}`, 'error');
|
||||||
});
|
});
|
||||||
}, 500); // 延迟2秒开始预加载,避免影响
|
}, 500);
|
||||||
|
|
||||||
// 预创建重要视频流
|
// 预创建重要视频流
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -88,6 +92,30 @@ class WebRTCChat {
|
|||||||
window.webrtcApp = this;
|
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() {
|
initializeElements() {
|
||||||
// 视频元素
|
// 视频元素
|
||||||
this.localVideo = document.getElementById('localVideo');
|
this.localVideo = document.getElementById('localVideo');
|
||||||
@ -111,6 +139,14 @@ class WebRTCChat {
|
|||||||
this.stopButton = document.getElementById('stopButton');
|
this.stopButton = document.getElementById('stopButton');
|
||||||
this.muteButton = document.getElementById('muteButton');
|
this.muteButton = document.getElementById('muteButton');
|
||||||
this.sendTextButton = document.getElementById('sendTextButton');
|
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.startVoiceButton = document.getElementById('startVoiceButton');
|
||||||
// this.stopVoiceButton = document.getElementById('stopVoiceButton');
|
// this.stopVoiceButton = document.getElementById('stopVoiceButton');
|
||||||
// this.defaultVideoButton = document.getElementById('defaultVideoButton');
|
// this.defaultVideoButton = document.getElementById('defaultVideoButton');
|
||||||
@ -165,7 +201,7 @@ class WebRTCChat {
|
|||||||
// 通话开始处理
|
// 通话开始处理
|
||||||
this.socket.on('call-started', (data) => {
|
this.socket.on('call-started', (data) => {
|
||||||
console.log('通话已开始', 'success');
|
console.log('通话已开始', 'success');
|
||||||
this.startDefaultVideoStream();
|
// 移除这里的视频流启动,因为现在在startCall中处理
|
||||||
});
|
});
|
||||||
|
|
||||||
// 场景切换处理
|
// 场景切换处理
|
||||||
@ -204,7 +240,7 @@ class WebRTCChat {
|
|||||||
this.videoMapping = data.mapping;
|
this.videoMapping = data.mapping;
|
||||||
this.interactionVideo = data.mapping['8-4-sh'];
|
this.interactionVideo = data.mapping['8-4-sh'];
|
||||||
this.defaultVideo = data.mapping["default"];
|
this.defaultVideo = data.mapping["default"];
|
||||||
this.logMessage('视频映射加载成功', 'success');
|
console.log('映射加载成功', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logMessage('加载视频映射失败: ' + error.message, 'error');
|
this.logMessage('加载视频映射失败: ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
@ -264,18 +300,35 @@ class WebRTCChat {
|
|||||||
|
|
||||||
async startDefaultVideoStream() {
|
async startDefaultVideoStream() {
|
||||||
try {
|
try {
|
||||||
this.logMessage('开始创建默认视频流', 'info');
|
console.log('开始创建默认视频流', 'info');
|
||||||
|
|
||||||
|
// 显示视频元素
|
||||||
|
if (this.recordedVideo) {
|
||||||
|
this.recordedVideo.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
// 添加加载状态
|
// 添加加载状态
|
||||||
this.recordedVideo.classList.add('loading');
|
this.recordedVideo.classList.add('loading');
|
||||||
|
|
||||||
// 创建默认视频的MediaStream
|
// 创建默认视频的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));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
// 检查流是否有效
|
// 再次检查流是否有效
|
||||||
if (!defaultStream || defaultStream.getTracks().length === 0) {
|
if (!defaultStream || defaultStream.getTracks().length === 0) {
|
||||||
throw new Error('默认视频流创建失败');
|
throw new Error('默认视频流创建失败');
|
||||||
}
|
}
|
||||||
@ -311,20 +364,39 @@ class WebRTCChat {
|
|||||||
// 确保视频开始播放
|
// 确保视频开始播放
|
||||||
try {
|
try {
|
||||||
await this.recordedVideo.play();
|
await this.recordedVideo.play();
|
||||||
this.logMessage('默认视频开始播放', 'success');
|
// this.logMessage('默认视频开始播放', 'success');
|
||||||
|
|
||||||
// 移除加载状态,添加播放状态
|
// 移除加载状态,添加播放状态
|
||||||
this.recordedVideo.classList.remove('loading');
|
this.recordedVideo.classList.remove('loading');
|
||||||
this.recordedVideo.classList.add('playing');
|
this.recordedVideo.classList.add('playing');
|
||||||
} catch (playError) {
|
} catch (playError) {
|
||||||
this.logMessage(`默认视频播放失败: ${playError.message}`, 'error');
|
// this.logMessage(`默认视频播放失败: ${playError.message}`, 'error');
|
||||||
this.recordedVideo.classList.remove('loading');
|
this.recordedVideo.classList.remove('loading');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 再次检查视频是否准备就绪
|
||||||
|
// this.startButton.disabled = false;
|
||||||
|
|
||||||
this.logMessage('默认视频流创建成功', 'success');
|
console.log('默认流创建成功', 'success');
|
||||||
} catch (error) {
|
} 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');
|
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;
|
this.isInitialized = true;
|
||||||
|
// 启用开始通话按钮
|
||||||
|
if (this.startButton) {
|
||||||
|
this.startButton.disabled = false;
|
||||||
|
this.startButton.style.opacity = '1';
|
||||||
|
}
|
||||||
this.logMessage('重要视频流预创建完成', 'success');
|
this.logMessage('重要视频流预创建完成', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -988,8 +1065,14 @@ class WebRTCChat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
// 开始通话按钮
|
// 开始通话按钮 - 添加视频准备状态检查
|
||||||
this.startButton.onclick = () => this.startCall();
|
this.startButton.onclick = () => {
|
||||||
|
if (!this.isVideoReady) {
|
||||||
|
this.logMessage('还在加载中,请稍候...', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.startCall();
|
||||||
|
};
|
||||||
|
|
||||||
// 停止通话按钮 - 改为调用 userDisconnect
|
// 停止通话按钮 - 改为调用 userDisconnect
|
||||||
this.stopButton.onclick = () => this.userDisconnect();
|
this.stopButton.onclick = () => this.userDisconnect();
|
||||||
@ -1042,13 +1125,27 @@ class WebRTCChat {
|
|||||||
|
|
||||||
async startCall() {
|
async startCall() {
|
||||||
try {
|
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.startButton.style.display = 'none';
|
||||||
// 显示等待连接提示
|
// 显示等待连接提示
|
||||||
this.showConnectionWaiting();
|
this.showConnectionWaiting();
|
||||||
// 切换到通话中图标
|
// 切换到通话中图标
|
||||||
this.switchToCallingIcon();
|
this.switchToCallingIcon();
|
||||||
|
|
||||||
|
// 现在才开始显示视频
|
||||||
|
await this.startDefaultVideoStream();
|
||||||
|
|
||||||
// 添加更详细的错误处理
|
// 添加更详细的错误处理
|
||||||
console.log('开始请求麦克风权限...');
|
console.log('开始请求麦克风权限...');
|
||||||
this.localStream = await navigator.mediaDevices.getUserMedia({
|
this.localStream = await navigator.mediaDevices.getUserMedia({
|
||||||
@ -1091,11 +1188,12 @@ class WebRTCChat {
|
|||||||
this.hideConnectionWaiting();
|
this.hideConnectionWaiting();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.logMessage(`开始通话失败: ${error.message}`, 'error');
|
||||||
|
// 恢复开始通话按钮
|
||||||
this.startButton.style.display = 'block';
|
this.startButton.style.display = 'block';
|
||||||
// 如果出错,隐藏等待连接提示并恢复到默认图标
|
// 如果出错,隐藏等待连接提示并恢复到默认图标
|
||||||
this.hideConnectionWaiting();
|
this.hideConnectionWaiting();
|
||||||
this.switchToDefaultIcon();
|
this.switchToDefaultIcon();
|
||||||
this.logMessage('无法访问麦克风: ' + error.message, 'error');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,20 +1231,40 @@ class WebRTCChat {
|
|||||||
this.stopButton.style.display = 'none';
|
this.stopButton.style.display = 'none';
|
||||||
this.stopButton.disabled = true;
|
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.disabled = false;
|
||||||
|
this.startButton.style.display = 'block';
|
||||||
|
this.startButton.style.opacity = '1';
|
||||||
|
|
||||||
// 移除页面刷新,保持websocket连接
|
// 移除页面刷新,保持websocket连接
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// window.location.reload();
|
// window.location.reload();
|
||||||
// }, 300);
|
// }, 300);
|
||||||
|
|
||||||
|
// 清理视频缓存和预创建流
|
||||||
|
this.clearVideoCache();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 显示头像,隐藏视频
|
// 显示头像,隐藏视频
|
||||||
if (this.videoContainer) {
|
if (this.videoContainer) {
|
||||||
this.videoContainer.classList.remove('calling');
|
this.videoContainer.classList.remove('calling');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重新初始化重要视频流
|
||||||
|
this.precreateImportantVideos().then(() => {
|
||||||
|
// 重新启动默认视频流
|
||||||
|
this.startDefaultVideoStream();
|
||||||
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1164,7 +1282,7 @@ class WebRTCChat {
|
|||||||
console.log('用户主动断开,300ms后刷新页面清除缓存...');
|
console.log('用户主动断开,300ms后刷新页面清除缓存...');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 300);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除视频缓存的方法
|
// 清除视频缓存的方法
|
||||||
@ -1194,6 +1312,19 @@ class WebRTCChat {
|
|||||||
// 清除视频流映射缓存
|
// 清除视频流映射缓存
|
||||||
this.videoStreams.clear();
|
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.currentVideo = null;
|
||||||
this.activeVideoElement = 'main';
|
this.activeVideoElement = 'main';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user