稳定切换视频

This commit is contained in:
Song367 2025-07-28 13:47:03 +08:00
parent fe2876f09c
commit b600114ed9
6 changed files with 74 additions and 289 deletions

View File

@ -1,272 +1 @@
[
{
"role": "user",
"content": "你好。",
"timestamp": "2025-07-27T14:31:39.908Z"
},
{
"role": "assistant",
"content": "哟,还这么客气呢?周末打算干啥呀? ",
"timestamp": "2025-07-27T14:31:39.908Z"
},
{
"role": "user",
"content": "不打算干啥。",
"timestamp": "2025-07-27T14:31:52.513Z"
},
{
"role": "assistant",
"content": "得嘞,那咱宅家咋样,我发现个超搞笑综艺。 ",
"timestamp": "2025-07-27T14:31:52.513Z"
},
{
"role": "user",
"content": "哎,我不知道。",
"timestamp": "2025-07-27T14:32:00.781Z"
},
{
"role": "assistant",
"content": "不知道啥呀?综艺还是宅家咋安排?要不咱叫点烧烤边吃边看? ",
"timestamp": "2025-07-27T14:32:00.781Z"
},
{
"role": "user",
"content": "到咯,佢到咯,我做饭啊。",
"timestamp": "2025-07-27T14:32:06.301Z"
},
{
"role": "assistant",
"content": "哟,背着我悄悄准备做饭啦!做啥好吃的,有我份不? ",
"timestamp": "2025-07-27T14:32:06.301Z"
},
{
"role": "user",
"content": "到咯佢到咯我做饭啊。要3秒要开大。",
"timestamp": "2025-07-27T14:32:08.465Z"
},
{
"role": "assistant",
"content": "啥跟啥啊?说的我一头雾水。你是准备做饭吗?需不需要我帮你弄点啥,冰箱里还有鸡蛋和青菜呢。 ",
"timestamp": "2025-07-27T14:32:08.465Z"
},
{
"role": "user",
"content": "Hello, hello.",
"timestamp": "2025-07-27T14:39:43.062Z"
},
{
"role": "assistant",
"content": "别搁那hello了快跟我说说你做饭啥情况要开啥大是火开大点吗 ",
"timestamp": "2025-07-27T14:39:43.062Z"
},
{
"role": "user",
"content": "你说话啊。",
"timestamp": "2025-07-27T14:40:45.074Z"
},
{
"role": "assistant",
"content": "我一直说呢!你做饭开啥大快说清楚,别搁这“挤牙膏”啦! ",
"timestamp": "2025-07-27T14:40:45.074Z"
},
{
"role": "user",
"content": "挤牙膏。",
"timestamp": "2025-07-27T14:40:56.087Z"
},
{
"role": "assistant",
"content": "服了你这个老六,别老重复我话呀,好好跟我讲讲做饭到底咋回事。 ",
"timestamp": "2025-07-27T14:40:56.087Z"
},
{
"role": "user",
"content": "你好啊!",
"timestamp": "2025-07-27T14:44:25.948Z"
},
{
"role": "assistant",
"content": "别装礼貌啦快接着说做饭那事儿咋个“要3秒要开大”法 ",
"timestamp": "2025-07-27T14:44:25.948Z"
},
{
"role": "user",
"content": "开大招。",
"timestamp": "2025-07-27T14:46:10.186Z"
},
{
"role": "assistant",
"content": "靠!还开大招,你做饭当打游戏呢?说清楚要干啥! ",
"timestamp": "2025-07-27T14:46:10.186Z"
},
{
"role": "user",
"content": "在干嘛呢?",
"timestamp": "2025-07-27T14:47:35.754Z"
},
{
"role": "assistant",
"content": "等你说清楚做饭的事儿呢!你到底要把啥开大,火还是调料量? ",
"timestamp": "2025-07-27T14:47:35.754Z"
},
{
"role": "user",
"content": "火呀!",
"timestamp": "2025-07-27T14:47:47.933Z"
},
{
"role": "assistant",
"content": "妥,炒菜火大才香。炒啥呢?",
"timestamp": "2025-07-27T14:47:47.933Z"
},
{
"role": "user",
"content": "瞅你长得好看。",
"timestamp": "2025-07-27T14:48:00.643Z"
},
{
"role": "assistant",
"content": "少来转移话题啊,先把你炒的菜说清楚,我好给你参谋参谋这火咋开。 ",
"timestamp": "2025-07-27T14:48:00.643Z"
},
{
"role": "user",
"content": "我喜欢你。",
"timestamp": "2025-07-27T14:48:18.743Z"
},
{
"role": "assistant",
"content": "哟,嘴甜起来了。不过先别搁这哄我,菜还等着呢,到底炒啥? ",
"timestamp": "2025-07-27T14:48:18.743Z"
},
{
"role": "user",
"content": "现在的孩子,真的没得到头,全部拿福祝了。",
"timestamp": "2025-07-27T14:48:32.883Z"
},
{
"role": "assistant",
"content": "啥意思啊?没太懂,先把你炒菜的事儿弄明白,这火还开着呢! ",
"timestamp": "2025-07-27T14:48:32.883Z"
},
{
"role": "user",
"content": "那个",
"timestamp": "2025-07-27T14:48:45.683Z"
},
{
"role": "assistant",
"content": "别“那个”了,急死我,菜名快说! ",
"timestamp": "2025-07-27T14:48:45.683Z"
},
{
"role": "user",
"content": "say-3s-m-sw",
"timestamp": "2025-07-27T14:53:33.356Z"
},
{
"role": "assistant",
"content": "这啥暗语啊?别整这些花里胡哨的,赶紧说炒啥菜,火大了糊锅啦! ",
"timestamp": "2025-07-27T14:53:33.356Z"
},
{
"role": "user",
"content": "Hello.",
"timestamp": "2025-07-27T14:56:12.312Z"
},
{
"role": "assistant",
"content": "别搁这整洋文了,快告诉我炒啥,再不说菜没法要了! ",
"timestamp": "2025-07-27T14:56:12.312Z"
},
{
"role": "user",
"content": "我喜欢你。",
"timestamp": "2025-07-27T14:56:26.151Z"
},
{
"role": "assistant",
"content": "知道啦知道啦,先解决炒菜这事儿,到底炒啥呀! ",
"timestamp": "2025-07-27T14:56:26.151Z"
},
{
"role": "user",
"content": "也还行啊。",
"timestamp": "2025-07-27T14:56:38.392Z"
},
{
"role": "assistant",
"content": "啥也还行?你就痛痛快快说炒啥菜,别在这打哑谜了! ",
"timestamp": "2025-07-27T14:56:38.392Z"
},
{
"role": "user",
"content": "你好啊!",
"timestamp": "2025-07-27T15:56:10.807Z"
},
{
"role": "assistant",
"content": "哟,整这么客气干啥,快把炒菜的事儿说明白。 ",
"timestamp": "2025-07-27T15:56:10.807Z"
},
{
"role": "user",
"content": "哈哈。",
"timestamp": "2025-07-27T15:56:20.531Z"
},
{
"role": "assistant",
"content": "别光笑啊,菜名呢?我都等半天了! ",
"timestamp": "2025-07-27T15:56:20.531Z"
},
{
"role": "user",
"content": "是不会哦。",
"timestamp": "2025-07-27T15:56:22.209Z"
},
{
"role": "assistant",
"content": "啥不会?炒菜步骤不会还是菜名没定啊?别磨叽! ",
"timestamp": "2025-07-27T15:56:22.210Z"
},
{
"role": "user",
"content": "你好。",
"timestamp": "2025-07-27T15:57:59.101Z"
},
{
"role": "assistant",
"content": "别老“你好”啦,咱快把炒菜这事儿搞定,你到底要炒啥! ",
"timestamp": "2025-07-27T15:57:59.101Z"
},
{
"role": "user",
"content": "你好啊!",
"timestamp": "2025-07-27T16:07:56.242Z"
},
{
"role": "assistant",
"content": "靠!别老说这俩字,赶紧跟我讲菜,不然我要发火啦! ",
"timestamp": "2025-07-27T16:07:56.242Z"
},
{
"role": "user",
"content": "吃什么菜?",
"timestamp": "2025-07-27T16:08:10.403Z"
},
{
"role": "assistant",
"content": "这就对了嘛!你可以试试炒个酸辣土豆丝,简单又好吃。你觉得咋样? ",
"timestamp": "2025-07-27T16:08:10.403Z"
},
{
"role": "user",
"content": "我就是响应速度太长了。我没有问过他。我也不晓得。",
"timestamp": "2025-07-27T16:08:30.548Z"
},
{
"role": "assistant",
"content": "啥响应速度?越说越迷糊。你就说对酸辣土豆丝感不感兴趣嘛! ",
"timestamp": "2025-07-27T16:08:30.548Z"
}
]
[]

View File

@ -145,7 +145,7 @@ async function chatWithAudioStream(userInput) {
console.log('音频已添加到队列,队列长度:', audioQueue.length);
// this.socket.emit('audio-queue-updated', audioQueue.map(item => ({ text: item.text, hasAudio:!!item.audioHex })));
// 开始处理队列
processAudioQueue();
// processAudioQueue();
}
} catch (error) {
console.error('生成音频失败:', error);

View File

@ -60,6 +60,11 @@ class WebRTCChat {
this.loadDefaultVideo();
this.bindEvents();
// 在初始化完成后预加载常用视频
setTimeout(() => {
this.preloadCommonVideos();
}, 500); // 延迟2秒开始预加载避免影响
window.webrtcApp = this;
}
@ -355,10 +360,14 @@ class WebRTCChat {
});
// 绘制视频到canvas
let lastDrawTime = 0;
let isDrawing = false;
const drawFrame = () => {
if (video.readyState >= video.HAVE_CURRENT_DATA && !isDrawing) {
const now = performance.now();
// 限制绘制频率,确保平滑过渡
if (video.readyState >= video.HAVE_CURRENT_DATA && !isDrawing && (now - lastDrawTime > 16)) { // 约60fps
isDrawing = true;
lastDrawTime = now;
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
isDrawing = false;
}
@ -388,18 +397,51 @@ class WebRTCChat {
}
}
// 在应用初始化时预加载常用视频
// 在WebRTCChat类中添加预加载方法
async preloadCommonVideos() {
// 获取所有可能需要的视频
const videosToPreload = new Set([]);
// // 添加视频映射中的所有视频
// Object.values(this.videoMapping).forEach(video => {
// videosToPreload.add(video);
// });
// 特别确保添加了5.mp4从日志看这是常用视频
videosToPreload.add('5.mp4');
// 开始预加载
for (const videoFile of videosToPreload) {
try {
this.logMessage(`预加载视频: ${videoFile}`, 'info');
await this.createVideoStream(videoFile);
this.logMessage(`预加载完成: ${videoFile}`, 'success');
} catch (error) {
this.logMessage(`预加载失败: ${videoFile}: ${error.message}`, 'error');
}
}
}
async switchVideoStream(videoFile, type = '', text = '') {
try {
this.logMessage(`开始切换视频流: ${videoFile} (${type})`, 'info');
// 如果视频已缓存减少loading时间
const isCached = this.videoStreams.has(videoFile);
if (!isCached) {
this.recordedVideo.classList.add('loading');
}
// 添加加载状态
this.recordedVideo.classList.add('loading');
// // 添加加载状态
// this.recordedVideo.classList.add('loading');
// 先创建新的视频流,不立即停止旧的
const newStream = await this.createVideoStream(videoFile);
// 等待流稳定
await new Promise(resolve => setTimeout(resolve, 200));
// 缓存的视频减少等待时间
const waitTime = isCached ? 50 : 200;
await new Promise(resolve => setTimeout(resolve, waitTime));
// 检查流是否有效
if (!newStream || newStream.getTracks().length === 0) {
@ -635,7 +677,7 @@ class WebRTCChat {
async sendText() {
// const text = this.textInput.value.trim();
const text = 'say-3s-m-sw';
const text = 'say-5s-m-sw';
if (text) {
this.socket.emit('text-input', { text });
this.logMessage(`发送文本: ${text}`, 'info');

View File

@ -31,15 +31,13 @@ class MessageHistory {
// 添加用户消息
this.messages.push({
role: 'user',
content: userInput,
timestamp: new Date().toISOString()
content: userInput
});
// 添加助手回复
this.messages.push({
role: 'assistant',
content: assistantResponse,
timestamp: new Date().toISOString()
content: assistantResponse
});
// 限制消息数量(保留最近的对话)

View File

@ -1,5 +1,7 @@
// 以流式或非流式方式请求 minimaxi 大模型接口,并打印/返回内容
// import { text } from "express";
// 在文件顶部添加音频播放相关的变量和函数
let audioContext = null;
let audioQueue = []; // 音频队列
@ -84,9 +86,12 @@ async function processAudioQueue() {
isProcessingQueue = false;
const text = 'default'
window.webrtcApp.currentVideoTag = text;
await window.webrtcApp.socket.emit('text-input', { text });
await window.webrtcApp.handleTextInput(text);
await window.webrtcApp.socket.emit('voice-input', { text });
if (window.webrtcApp.currentVideoTag != text) {
window.webrtcApp.currentVideoTag = text
await window.webrtcApp.switchVideoStream(window.webrtcApp.defaultVideo, 'audio', text);
}
console.log('音频队列处理完成');
}

View File

@ -116,12 +116,12 @@ header p {
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
object-fit: cover; /* 确保视频填充容器 */
background: #000; /* 视频背景色 */
transition: opacity 0.3s ease; /* 添加透明度过渡效果 */
transition: opacity 0.15s; /* 添加透明度过渡效果 */
}
/* 视频加载时的样式 */
#recordedVideo.loading {
opacity: 0.5;
opacity: 0.8;
}
/* 视频播放时的样式 */
@ -434,4 +434,15 @@ header p {
.recording {
animation: pulse 1s infinite;
}
}
/* 视频加载时的样式 */
#recordedVideo.loading {
opacity: 0.5;
}
/* 视频播放时的样式 */
#recordedVideo.playing {
opacity: 1;
}