优化衔接处bug
This commit is contained in:
parent
a96fc86d42
commit
f0bf3b6184
14
server.js
14
server.js
@ -87,16 +87,16 @@ const connectedClients = new Map();
|
||||
|
||||
// 视频映射配置
|
||||
const videoMapping = {
|
||||
'say-6s-m-e': '1-m.mp4',
|
||||
'default': '0.mp4',
|
||||
'say-5s-amplitude': '2.mp4',
|
||||
'say-5s-m-e': '4.mp4',
|
||||
'say-5s-m-sw': '5.mp4',
|
||||
'say-3s-m-sw': '6.mp4',
|
||||
// 'say-6s-m-e': '1-m.mp4',
|
||||
'default': 'd-3s.mp4',
|
||||
// 'say-5s-amplitude': '2.mp4',
|
||||
// 'say-5s-m-e': '4.mp4',
|
||||
// 'say-5s-m-sw': '5.mp4',
|
||||
'say-3s-m-sw': 's-1.mp4',
|
||||
};
|
||||
|
||||
// 默认视频流配置
|
||||
const DEFAULT_VIDEO = '0.mp4';
|
||||
const DEFAULT_VIDEO = 'd-3s.mp4';
|
||||
const INTERACTION_TIMEOUT = 10000; // 10秒后回到默认视频
|
||||
|
||||
// 获取视频列表
|
||||
|
||||
211
src/index.js
211
src/index.js
@ -19,11 +19,14 @@ class WebRTCChat {
|
||||
this.mediaRecorder = null;
|
||||
this.audioChunks = [];
|
||||
this.videoMapping = {};
|
||||
this.defaultVideo = '0.mp4';
|
||||
this.defaultVideo = 'd-3s.mp4';
|
||||
this.currentVideoTag = 'default';
|
||||
this.currentVideo = null;
|
||||
this.videoStreams = new Map(); // 存储不同视频的MediaStream
|
||||
this.currentVideoStream = null;
|
||||
this.audioEndedListenerAdded = false; // 标志位,避免重复添加监听器
|
||||
this.animationFrameIds = new Map(); // 存储每个视频的动画帧ID
|
||||
this.canvasElements = new Map(); // 存储每个视频的canvas元素
|
||||
|
||||
// 添加视频相关属性
|
||||
this.videoSender = null; // WebRTC视频发送器
|
||||
@ -299,6 +302,9 @@ class WebRTCChat {
|
||||
try {
|
||||
this.logMessage(`开始创建视频流: ${videoFile}`, 'info');
|
||||
|
||||
// 清理之前的动画循环和canvas
|
||||
this.cleanupVideoResources(videoFile);
|
||||
|
||||
// 先测试视频文件是否存在
|
||||
await this.testVideoFile(videoFile);
|
||||
|
||||
@ -367,6 +373,8 @@ class WebRTCChat {
|
||||
// 绘制视频到canvas
|
||||
let lastDrawTime = 0;
|
||||
let isDrawing = false;
|
||||
let animationId;
|
||||
|
||||
const drawFrame = () => {
|
||||
const now = performance.now();
|
||||
if (video.readyState >= video.HAVE_CURRENT_DATA && !isDrawing && (now - lastDrawTime > 16)) {
|
||||
@ -375,12 +383,16 @@ class WebRTCChat {
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
isDrawing = false;
|
||||
}
|
||||
requestAnimationFrame(drawFrame);
|
||||
animationId = requestAnimationFrame(drawFrame);
|
||||
};
|
||||
|
||||
// 开始绘制帧
|
||||
drawFrame();
|
||||
|
||||
// 存储动画帧ID和canvas引用
|
||||
this.animationFrameIds.set(videoFile, animationId);
|
||||
this.canvasElements.set(videoFile, canvas);
|
||||
|
||||
// 从canvas创建MediaStream
|
||||
const stream = canvas.captureStream(30);
|
||||
|
||||
@ -391,68 +403,56 @@ class WebRTCChat {
|
||||
|
||||
this.logMessage(`视频流创建成功: ${videoFile}`, 'success');
|
||||
|
||||
if (videoFile === this.defaultVideo) {
|
||||
let lastCurrentTime = 0;
|
||||
video.addEventListener('timeupdate', async () => {
|
||||
if (videoFile === this.defaultVideo && !this.audioEndedListenerAdded) {
|
||||
this.audioEndedListenerAdded = true;
|
||||
|
||||
// 清理之前可能存在的监听器
|
||||
if (this.currentTimeUpdateHandler) {
|
||||
video.removeEventListener('timeupdate', this.currentTimeUpdateHandler);
|
||||
}
|
||||
// 在handleTimeUpdate中使用节流
|
||||
const throttledTimeUpdate = this.throttle(handleTimeUpdate, 100);
|
||||
const handleTimeUpdate = async () => {
|
||||
const currentTime = video.currentTime;
|
||||
const duration = video.duration;
|
||||
|
||||
// 方法2a:检查是否接近结束(最后0.1秒)
|
||||
// 检查是否接近结束(最后0.1秒)
|
||||
if (duration - currentTime <= 0.1) {
|
||||
console.log('视频即将播放完成');
|
||||
// 处理即将结束的逻辑
|
||||
if (videoFile === this.defaultVideo) {
|
||||
let lastCurrentTime = 0;
|
||||
video.addEventListener('timeupdate', async () => {
|
||||
const currentTime = video.currentTime;
|
||||
const duration = video.duration;
|
||||
|
||||
// 检查音频是否正在播放(从minimaxi_stream.js获取isPlaying状态)
|
||||
const isAudioPlaying = window.isPlaying || false;
|
||||
|
||||
// 如果音频没有播放,且当前不是默认视频,则切换到默认视频
|
||||
if (!isAudioPlaying) {
|
||||
const currentVideoFile = this.currentVideo;
|
||||
|
||||
if (currentVideoFile !== this.defaultVideo) {
|
||||
console.log('音频已停止,当前视频不是默认视频,准备切换到默认视频');
|
||||
|
||||
// 检查音频是否正在播放(从minimaxi_stream.js获取isPlaying状态)
|
||||
const isAudioPlaying = window.isPlaying || false; // 需要确保isPlaying是全局可访问的
|
||||
// 移除监听器
|
||||
this.cleanupTimeUpdateListener();
|
||||
|
||||
// 如果音频没有播放,且当前不是默认视频,则切换到默认视频
|
||||
if (!isAudioPlaying) {
|
||||
const currentVideoFile = this.currentVideo; // 获取当前播放的视频文件名
|
||||
|
||||
if (currentVideoFile !== this.defaultVideo) {
|
||||
console.log('音频已停止,当前视频不是默认视频,准备切换到默认视频');
|
||||
|
||||
// 停止当前视频的循环
|
||||
if (this.recordedVideo) {
|
||||
this.recordedVideo.loop = false;
|
||||
}
|
||||
|
||||
// 切换到默认视频
|
||||
try {
|
||||
await this.switchVideoWithReplaceTrack(this.defaultVideo, 'auto-switch', 'audio-ended');
|
||||
console.log('已自动切换到默认视频');
|
||||
} catch (error) {
|
||||
console.error('自动切换到默认视频失败:', error);
|
||||
}
|
||||
}
|
||||
// 停止当前视频的循环
|
||||
if (this.recordedVideo) {
|
||||
this.recordedVideo.loop = false;
|
||||
}
|
||||
|
||||
lastCurrentTime = currentTime;
|
||||
});
|
||||
// 切换到默认视频
|
||||
try {
|
||||
await this.switchVideoWithReplaceTrack(this.defaultVideo, 'auto', 'default');
|
||||
console.log('已自动切换到默认视频');
|
||||
} catch (error) {
|
||||
console.error('自动切换到默认视频失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 方法2b:检查是否已经到达结尾
|
||||
if (currentTime >= duration) {
|
||||
console.log('视频播放完成');
|
||||
// 处理播放完成的逻辑
|
||||
}
|
||||
|
||||
// 方法2c:检查时间是否停止更新(可能表示播放结束)
|
||||
if (Math.abs(currentTime - lastCurrentTime) < 0.01) {
|
||||
// 时间没有更新,可能播放结束或暂停
|
||||
if (currentTime >= duration) {
|
||||
console.log('视频播放完成(通过时间停止检测)');
|
||||
}
|
||||
}
|
||||
|
||||
lastCurrentTime = currentTime;
|
||||
});
|
||||
};
|
||||
|
||||
// 保存监听器引用以便后续清理
|
||||
this.currentTimeUpdateHandler = throttledTimeUpdate;
|
||||
video.addEventListener('timeupdate', throttledTimeUpdate);
|
||||
}
|
||||
|
||||
// 使用有限缓存策略(最多缓存3个视频流)
|
||||
@ -469,6 +469,8 @@ class WebRTCChat {
|
||||
|
||||
return stream;
|
||||
} catch (error) {
|
||||
// 确保在错误情况下也清理资源
|
||||
this.cleanupVideoResources(videoFile);
|
||||
this.logMessage(`创建视频流失败 ${videoFile}: ${error.message}`, 'error');
|
||||
throw error;
|
||||
}
|
||||
@ -486,7 +488,7 @@ class WebRTCChat {
|
||||
// });
|
||||
|
||||
// 特别确保添加了5.mp4(从日志看这是常用视频)
|
||||
videosToPreload.add('6.mp4');
|
||||
videosToPreload.add('s-1.mp4');
|
||||
|
||||
// 开始预加载
|
||||
for (const videoFile of videosToPreload) {
|
||||
@ -546,6 +548,7 @@ class WebRTCChat {
|
||||
// 现在停止旧的视频流
|
||||
if (this.currentVideoStream !== newStream) {
|
||||
const oldStream = this.currentVideoStream;
|
||||
const oldVideo = this.currentVideo;
|
||||
setTimeout(() => {
|
||||
if (oldStream) {
|
||||
oldStream.getTracks().forEach(track => {
|
||||
@ -553,6 +556,10 @@ class WebRTCChat {
|
||||
this.logMessage(`已停止旧轨道: ${track.kind}`, 'info');
|
||||
});
|
||||
}
|
||||
// 清理旧视频的资源
|
||||
if (oldVideo) {
|
||||
this.cleanupVideoResources(oldVideo);
|
||||
}
|
||||
}, 1000); // 延迟1秒停止旧流,确保新流已经稳定
|
||||
}
|
||||
|
||||
@ -602,9 +609,11 @@ class WebRTCChat {
|
||||
|
||||
// 同时更新本地视频显示
|
||||
if (this.recordedVideo) {
|
||||
// 停止当前视频流
|
||||
// 停止当前视频流和动画循环
|
||||
if (this.currentVideoStream) {
|
||||
this.currentVideoStream.getTracks().forEach(track => track.stop());
|
||||
// 清理当前视频的资源
|
||||
this.cleanupVideoResources(this.currentVideo);
|
||||
}
|
||||
|
||||
// 设置新的视频流
|
||||
@ -993,6 +1002,29 @@ class WebRTCChat {
|
||||
this.messageLog.scrollTop = this.messageLog.scrollHeight;
|
||||
}
|
||||
|
||||
// 添加清理监听器的方法
|
||||
cleanupTimeUpdateListener() {
|
||||
if (this.currentTimeUpdateHandler && this.recordedVideo) {
|
||||
this.recordedVideo.removeEventListener('timeupdate', this.currentTimeUpdateHandler);
|
||||
this.currentTimeUpdateHandler = null;
|
||||
this.audioEndedListenerAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加节流函数
|
||||
throttle(func, limit) {
|
||||
let inThrottle;
|
||||
return function() {
|
||||
const args = arguments;
|
||||
const context = this;
|
||||
if (!inThrottle) {
|
||||
func.apply(context, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkVideoStreamStatus() {
|
||||
const status = {
|
||||
hasStream: !!this.currentVideoStream,
|
||||
@ -1026,6 +1058,68 @@ class WebRTCChat {
|
||||
// 检查当前视频流状态
|
||||
this.checkVideoStreamStatus();
|
||||
}
|
||||
|
||||
// 清理视频资源的方法
|
||||
cleanupVideoResources(videoFile) {
|
||||
// 停止动画循环
|
||||
if (this.animationFrameIds.has(videoFile)) {
|
||||
const animationId = this.animationFrameIds.get(videoFile);
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
this.animationFrameIds.delete(videoFile);
|
||||
}
|
||||
|
||||
// 清理canvas
|
||||
if (this.canvasElements.has(videoFile)) {
|
||||
const canvas = this.canvasElements.get(videoFile);
|
||||
if (canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
this.canvasElements.delete(videoFile);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理所有视频资源
|
||||
cleanupAllVideoResources() {
|
||||
// 停止所有动画循环
|
||||
for (const [videoFile, animationId] of this.animationFrameIds) {
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
}
|
||||
}
|
||||
this.animationFrameIds.clear();
|
||||
|
||||
// 清理所有canvas
|
||||
for (const [videoFile, canvas] of this.canvasElements) {
|
||||
if (canvas) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
this.canvasElements.clear();
|
||||
}
|
||||
|
||||
// 添加销毁方法
|
||||
destroy() {
|
||||
this.cleanupAllVideoResources();
|
||||
this.cleanupTimeUpdateListener();
|
||||
|
||||
// 清理其他资源
|
||||
if (this.localStream) {
|
||||
this.localStream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
if (this.currentVideoStream) {
|
||||
this.currentVideoStream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
// 清理视频流缓存
|
||||
for (const [key, stream] of this.videoStreams) {
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
this.videoStreams.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化应用
|
||||
@ -1037,3 +1131,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
console.error('WebRTCChat 初始化失败:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 在页面卸载时清理资源
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (window.webrtcApp) {
|
||||
window.webrtcApp.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
@ -65,7 +65,7 @@ async function processAudioQueue() {
|
||||
if (!window.isPlaying && audioQueue.length > 0) {
|
||||
const audioItem = audioQueue.shift();
|
||||
const sayName = 'say-3s-m-sw'
|
||||
const targetVideo = '6.mp4'
|
||||
const targetVideo = 's-1.mp4'
|
||||
// 如果是第一个音频片段,触发视频切换
|
||||
if (sayName != window.webrtcApp.currentVideoTag && window.webrtcApp && window.webrtcApp.handleTextInput) {
|
||||
try {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user