视频切换,切换三个场景,未实现场景人设切换
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 2m18s
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 2m18s
This commit is contained in:
parent
f176818155
commit
c96c49ff3f
104
server.js
104
server.js
@ -85,10 +85,64 @@ app.delete('/api/messages/clear', async (req, res) => {
|
|||||||
// 存储连接的客户端和他们的视频流状态
|
// 存储连接的客户端和他们的视频流状态
|
||||||
const connectedClients = new Map();
|
const connectedClients = new Map();
|
||||||
|
|
||||||
|
// 场景轮询系统
|
||||||
|
let currentSceneIndex = 0;
|
||||||
|
const scenes = [
|
||||||
|
{
|
||||||
|
name: '起床',
|
||||||
|
defaultVideo: '8-4-bd-2.mp4',
|
||||||
|
interactionVideo: '8-4-sh.mp4',
|
||||||
|
tag: 'wakeup'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '开车',
|
||||||
|
defaultVideo: '8-4-kc-bd.mp4', // 根据您的视频文件调整
|
||||||
|
interactionVideo: '8-4-kc-sh.mp4',
|
||||||
|
tag: 'driving'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '喝茶',
|
||||||
|
defaultVideo: '8-4-hc-bd.mp4',
|
||||||
|
interactionVideo: '8-4-hc-sh.mp4',
|
||||||
|
tag: 'tea'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 获取当前场景
|
||||||
|
function getCurrentScene() {
|
||||||
|
return scenes[currentSceneIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换到下一个场景
|
||||||
|
function switchToNextScene() {
|
||||||
|
currentSceneIndex = (currentSceneIndex + 1) % scenes.length;
|
||||||
|
console.log(`切换到场景: ${getCurrentScene().name}`);
|
||||||
|
return getCurrentScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频映射配置 - 动态更新
|
||||||
|
function getVideoMapping() {
|
||||||
|
const currentScene = getCurrentScene();
|
||||||
|
return {
|
||||||
|
'defaultVideo': currentScene.defaultVideo,
|
||||||
|
'interactionVideo': currentScene.interactionVideo,
|
||||||
|
'tag': currentScene.tag
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认视频流配置 - 动态获取
|
||||||
|
function getDefaultVideo() {
|
||||||
|
return getCurrentScene().defaultVideo;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentScene = getCurrentScene();
|
||||||
|
|
||||||
// 视频映射配置
|
// 视频映射配置
|
||||||
const videoMapping = {
|
const videoMapping = {
|
||||||
// 'say-6s-m-e': '1-m.mp4',
|
// 'say-6s-m-e': '1-m.mp4',
|
||||||
'default': 'chang.mp4',
|
'default': currentScene.defaultVideo,
|
||||||
|
'8-4-sh': currentScene.interactionVideo,
|
||||||
|
'tag': currentScene.tag
|
||||||
// 'say-5s-amplitude': '2.mp4',
|
// 'say-5s-amplitude': '2.mp4',
|
||||||
// 'say-5s-m-e': '4.mp4',
|
// 'say-5s-m-e': '4.mp4',
|
||||||
// 'say-5s-m-sw': 'd-0.mp4',
|
// 'say-5s-m-sw': 'd-0.mp4',
|
||||||
@ -96,7 +150,7 @@ const videoMapping = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 默认视频流配置
|
// 默认视频流配置
|
||||||
const DEFAULT_VIDEO = 'chang.mp4';
|
const DEFAULT_VIDEO = currentScene.defaultVideo;
|
||||||
const INTERACTION_TIMEOUT = 10000; // 10秒后回到默认视频
|
const INTERACTION_TIMEOUT = 10000; // 10秒后回到默认视频
|
||||||
|
|
||||||
// 获取视频列表
|
// 获取视频列表
|
||||||
@ -115,13 +169,14 @@ app.get('/api/videos', (req, res) => {
|
|||||||
|
|
||||||
// 获取视频映射
|
// 获取视频映射
|
||||||
app.get('/api/video-mapping', (req, res) => {
|
app.get('/api/video-mapping', (req, res) => {
|
||||||
|
// videoMapping = getCurrentScene()
|
||||||
res.json({ mapping: videoMapping });
|
res.json({ mapping: videoMapping });
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取默认视频
|
// 获取默认视频
|
||||||
app.get('/api/default-video', (req, res) => {
|
app.get('/api/default-video', (req, res) => {
|
||||||
res.json({
|
res.json({
|
||||||
defaultVideo: DEFAULT_VIDEO,
|
defaultVideo: getDefaultVideo(),
|
||||||
autoLoop: true
|
autoLoop: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -131,7 +186,7 @@ io.on('connection', (socket) => {
|
|||||||
console.log('用户连接:', socket.id);
|
console.log('用户连接:', socket.id);
|
||||||
connectedClients.set(socket.id, {
|
connectedClients.set(socket.id, {
|
||||||
socket: socket,
|
socket: socket,
|
||||||
currentVideo: DEFAULT_VIDEO,
|
currentVideo: getDefaultVideo(),
|
||||||
isInInteraction: false
|
isInInteraction: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -185,12 +240,12 @@ io.on('connection', (socket) => {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log(`交互超时,用户 ${socket.id} 回到默认视频`);
|
console.log(`交互超时,用户 ${socket.id} 回到默认视频`);
|
||||||
if (client) {
|
if (client) {
|
||||||
client.currentVideo = DEFAULT_VIDEO;
|
client.currentVideo = getDefaultVideo();
|
||||||
client.isInInteraction = false;
|
client.isInInteraction = false;
|
||||||
}
|
}
|
||||||
// 广播回到默认视频的指令
|
// 广播回到默认视频的指令
|
||||||
io.emit('video-stream-switched', {
|
io.emit('video-stream-switched', {
|
||||||
videoFile: DEFAULT_VIDEO,
|
videoFile: getDefaultVideo(),
|
||||||
type: 'default',
|
type: 'default',
|
||||||
from: socket.id
|
from: socket.id
|
||||||
});
|
});
|
||||||
@ -203,7 +258,7 @@ io.on('connection', (socket) => {
|
|||||||
console.log('通话开始,用户:', socket.id);
|
console.log('通话开始,用户:', socket.id);
|
||||||
const client = connectedClients.get(socket.id);
|
const client = connectedClients.get(socket.id);
|
||||||
if (client) {
|
if (client) {
|
||||||
client.currentVideo = DEFAULT_VIDEO;
|
client.currentVideo = getDefaultVideo();
|
||||||
client.isInInteraction = false;
|
client.isInInteraction = false;
|
||||||
}
|
}
|
||||||
io.emit('call-started', { from: socket.id });
|
io.emit('call-started', { from: socket.id });
|
||||||
@ -262,15 +317,46 @@ io.on('connection', (socket) => {
|
|||||||
console.log('用户请求回到默认视频:', socket.id);
|
console.log('用户请求回到默认视频:', socket.id);
|
||||||
const client = connectedClients.get(socket.id);
|
const client = connectedClients.get(socket.id);
|
||||||
if (client) {
|
if (client) {
|
||||||
client.currentVideo = DEFAULT_VIDEO;
|
client.currentVideo = getDefaultVideo();
|
||||||
client.isInInteraction = false;
|
client.isInInteraction = false;
|
||||||
}
|
}
|
||||||
socket.emit('switch-video-stream', {
|
socket.emit('switch-video-stream', {
|
||||||
videoFile: DEFAULT_VIDEO,
|
videoFile: getDefaultVideo(),
|
||||||
type: 'default'
|
type: 'default'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 处理用户关闭连接事件
|
||||||
|
socket.on('user-disconnect', () => {
|
||||||
|
console.log('用户主动关闭连接:', socket.id);
|
||||||
|
|
||||||
|
// 切换到下一个场景
|
||||||
|
const newScene = switchToNextScene();
|
||||||
|
console.log(`场景已切换到: ${newScene.name}`);
|
||||||
|
|
||||||
|
// 更新videoMapping
|
||||||
|
const newMapping = getVideoMapping();
|
||||||
|
videoMapping['default'] = newMapping.defaultVideo;
|
||||||
|
videoMapping['8-4-sh'] = newMapping.interactionVideo;
|
||||||
|
videoMapping['tag'] = newMapping.tag;
|
||||||
|
|
||||||
|
console.log('videoMapping已更新:', videoMapping);
|
||||||
|
|
||||||
|
// 清理客户端状态
|
||||||
|
const client = connectedClients.get(socket.id);
|
||||||
|
if (client) {
|
||||||
|
client.currentVideo = newMapping.defaultVideo;
|
||||||
|
client.isInInteraction = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 广播场景切换事件给所有客户端
|
||||||
|
io.emit('scene-switched', {
|
||||||
|
scene: newScene,
|
||||||
|
mapping: newMapping,
|
||||||
|
from: socket.id
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 断开连接
|
// 断开连接
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log('用户断开连接:', socket.id);
|
console.log('用户断开连接:', socket.id);
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
class AudioProcessor {
|
class AudioProcessor {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
this.audioContext = null;
|
this.audioContext = null;
|
||||||
|
this.stream = null; // 添加这一行
|
||||||
this.isRecording = false;
|
this.isRecording = false;
|
||||||
this.audioChunks = [];
|
this.audioChunks = [];
|
||||||
|
|
||||||
@ -311,22 +312,29 @@ class AudioProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 开始录音
|
// 开始录音
|
||||||
async startRecording() {
|
async startRecording(existingStream = null) {
|
||||||
try {
|
try {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
// 如果有外部提供的音频流,使用它;否则获取新的
|
||||||
audio: {
|
if (existingStream) {
|
||||||
sampleRate: 16000,
|
this.stream = existingStream;
|
||||||
channelCount: 1,
|
console.log('使用外部提供的音频流');
|
||||||
echoCancellation: true,
|
} else {
|
||||||
noiseSuppression: true
|
this.stream = await navigator.mediaDevices.getUserMedia({
|
||||||
}
|
audio: {
|
||||||
});
|
sampleRate: 16000,
|
||||||
|
channelCount: 1,
|
||||||
|
echoCancellation: true,
|
||||||
|
noiseSuppression: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log('获取新的音频流');
|
||||||
|
}
|
||||||
|
|
||||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
|
this.audioContext = new (window.AudioContext || window.webkitAudioContext)({
|
||||||
sampleRate: 16000
|
sampleRate: 16000
|
||||||
});
|
});
|
||||||
|
|
||||||
const source = this.audioContext.createMediaStreamSource(stream);
|
const source = this.audioContext.createMediaStreamSource(this.stream);
|
||||||
const processor = this.audioContext.createScriptProcessor(4096, 1, 1);
|
const processor = this.audioContext.createScriptProcessor(4096, 1, 1);
|
||||||
|
|
||||||
processor.onaudioprocess = (event) => {
|
processor.onaudioprocess = (event) => {
|
||||||
@ -343,9 +351,13 @@ class AudioProcessor {
|
|||||||
source.connect(processor);
|
source.connect(processor);
|
||||||
processor.connect(this.audioContext.destination);
|
processor.connect(this.audioContext.destination);
|
||||||
|
|
||||||
|
// 保存处理器引用以便后续清理
|
||||||
|
this.processor = processor;
|
||||||
|
this.source = source;
|
||||||
|
|
||||||
this.isRecording = true;
|
this.isRecording = true;
|
||||||
this.onStatusUpdate('等待语音输入...', 'ready');
|
this.onStatusUpdate('等待语音输入...', 'ready');
|
||||||
|
|
||||||
// 在startRecording方法的最后添加
|
// 在startRecording方法的最后添加
|
||||||
if (this.adaptiveThreshold && this.noiseCalibrationSamples.length === 0) {
|
if (this.adaptiveThreshold && this.noiseCalibrationSamples.length === 0) {
|
||||||
this.onStatusUpdate('正在校准背景噪音,请保持安静...', 'calibrating');
|
this.onStatusUpdate('正在校准背景噪音,请保持安静...', 'calibrating');
|
||||||
@ -364,6 +376,17 @@ class AudioProcessor {
|
|||||||
stopRecording() {
|
stopRecording() {
|
||||||
console.log('开始停止录音...');
|
console.log('开始停止录音...');
|
||||||
|
|
||||||
|
// 断开音频节点连接
|
||||||
|
if (this.source) {
|
||||||
|
this.source.disconnect();
|
||||||
|
this.source = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.processor) {
|
||||||
|
this.processor.disconnect();
|
||||||
|
this.processor = null;
|
||||||
|
}
|
||||||
|
|
||||||
// 停止所有音频轨道
|
// 停止所有音频轨道
|
||||||
if (this.stream) {
|
if (this.stream) {
|
||||||
this.stream.getTracks().forEach(track => {
|
this.stream.getTracks().forEach(track => {
|
||||||
@ -400,6 +423,10 @@ class AudioProcessor {
|
|||||||
this.consecutiveFramesCount = 0;
|
this.consecutiveFramesCount = 0;
|
||||||
this.frameBuffer = [];
|
this.frameBuffer = [];
|
||||||
|
|
||||||
|
// 重置校准状态,确保下次启动时重新校准
|
||||||
|
this.noiseCalibrationSamples = [];
|
||||||
|
this.isCalibrated = false;
|
||||||
|
|
||||||
this.onStatusUpdate('录音已完全停止', 'stopped');
|
this.onStatusUpdate('录音已完全停止', 'stopped');
|
||||||
console.log('录音已完全停止,所有资源已释放');
|
console.log('录音已完全停止,所有资源已释放');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -219,6 +219,67 @@
|
|||||||
box-shadow: 0 6px 20px rgba(34, 197, 94, 0.5);
|
box-shadow: 0 6px 20px rgba(34, 197, 94, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#startButton.connecting {
|
||||||
|
background: rgba(255, 193, 7, 0.9);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#startButton.connecting:hover {
|
||||||
|
background: rgba(255, 193, 7, 0.9);
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#startButton.calling {
|
||||||
|
background: rgba(255, 193, 7, 0.9);
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
#startButton.calling:hover {
|
||||||
|
background: rgba(255, 193, 7, 0.95);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 4px 15px rgba(255, 193, 7, 0.3);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 6px 25px rgba(255, 193, 7, 0.6);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 4px 15px rgba(255, 193, 7, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-status {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
z-index: 1000;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-status.connecting {
|
||||||
|
background: rgba(255, 193, 7, 0.9);
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-status.connected {
|
||||||
|
background: rgba(40, 167, 69, 0.9);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-status.error {
|
||||||
|
background: rgba(220, 53, 69, 0.9);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
#startButton svg {
|
#startButton svg {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
@ -251,8 +312,8 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<!-- 隐藏的音频状态显示 -->
|
<!-- 音频状态显示 - 显示状态文本 -->
|
||||||
<div class="audio-status" style="display: none;">
|
<div class="audio-status">
|
||||||
<div class="status-indicator">
|
<div class="status-indicator">
|
||||||
<span id="audioStatus">未连接</span>
|
<span id="audioStatus">未连接</span>
|
||||||
</div>
|
</div>
|
||||||
@ -288,9 +349,22 @@
|
|||||||
<!-- 控制按钮 - 悬浮在视频上方 -->
|
<!-- 控制按钮 - 悬浮在视频上方 -->
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button id="startButton" class="btn btn-primary" title="开始通话">
|
<button id="startButton" class="btn btn-primary" title="开始通话">
|
||||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<!-- 默认通话图标 -->
|
||||||
|
<svg id="callIcon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" fill="white"/>
|
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" fill="white"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<!-- 通话中图标(初始隐藏) -->
|
||||||
|
<svg id="callingIcon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||||
|
<circle cx="12" cy="12" r="3" fill="white">
|
||||||
|
<animate attributeName="r" values="3;5;3" dur="1.5s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="opacity" values="1;0.5;1" dur="1.5s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="12" cy="12" r="8" stroke="white" stroke-width="2" fill="none" opacity="0.6">
|
||||||
|
<animate attributeName="r" values="8;12;8" dur="2s" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="opacity" values="0.6;0.2;0.6" dur="2s" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" fill="white" opacity="0.8"/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button id="stopButton" class="btn btn-danger" disabled title="结束通话">
|
<button id="stopButton" class="btn btn-danger" disabled title="结束通话">
|
||||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|||||||
123
src/index.js
123
src/index.js
@ -9,6 +9,8 @@ class WebRTCChat {
|
|||||||
constructor() {
|
constructor() {
|
||||||
console.log('WebRTCChat 构造函数开始执行');
|
console.log('WebRTCChat 构造函数开始执行');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化历史消息(异步)
|
// 初始化历史消息(异步)
|
||||||
this.initializeHistory();
|
this.initializeHistory();
|
||||||
|
|
||||||
@ -20,6 +22,7 @@ class WebRTCChat {
|
|||||||
this.audioChunks = [];
|
this.audioChunks = [];
|
||||||
this.videoMapping = {};
|
this.videoMapping = {};
|
||||||
this.defaultVideo = 'chang.mp4';
|
this.defaultVideo = 'chang.mp4';
|
||||||
|
this.interactionVideo = 'chang.mp4';
|
||||||
this.currentVideoTag = 'default';
|
this.currentVideoTag = 'default';
|
||||||
this.currentVideo = null;
|
this.currentVideo = null;
|
||||||
this.videoStreams = new Map(); // 存储不同视频的MediaStream
|
this.videoStreams = new Map(); // 存储不同视频的MediaStream
|
||||||
@ -147,6 +150,13 @@ class WebRTCChat {
|
|||||||
this.logMessage('通话已开始', 'success');
|
this.logMessage('通话已开始', 'success');
|
||||||
this.startDefaultVideoStream();
|
this.startDefaultVideoStream();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 场景切换处理
|
||||||
|
this.socket.on('scene-switched', (data) => {
|
||||||
|
this.logMessage(`场景已切换到: ${data.currentScene}`, 'info');
|
||||||
|
// 移除自动清除缓存和播放视频的逻辑
|
||||||
|
// 现在依赖页面刷新来处理缓存清除
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initializeHistory() {
|
async initializeHistory() {
|
||||||
@ -163,6 +173,8 @@ class WebRTCChat {
|
|||||||
const response = await fetch('/api/video-mapping');
|
const response = await fetch('/api/video-mapping');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
this.videoMapping = data.mapping;
|
this.videoMapping = data.mapping;
|
||||||
|
this.interactionVideo = data.mapping['8-4-sh'];
|
||||||
|
this.defaultVideo = data.mapping["default"];
|
||||||
this.logMessage('视频映射加载成功', 'success');
|
this.logMessage('视频映射加载成功', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logMessage('加载视频映射失败: ' + error.message, 'error');
|
this.logMessage('加载视频映射失败: ' + error.message, 'error');
|
||||||
@ -429,7 +441,8 @@ class WebRTCChat {
|
|||||||
// 在应用初始化时预加载常用视频
|
// 在应用初始化时预加载常用视频
|
||||||
async preloadCommonVideos() {
|
async preloadCommonVideos() {
|
||||||
// 获取所有可能需要的视频
|
// 获取所有可能需要的视频
|
||||||
const videosToPreload = new Set(['chang.mp4']);
|
console.log("default video, interaction video", [this.defaultVideo, this.interactionVideo])
|
||||||
|
const videosToPreload = new Set([this.defaultVideo, this.interactionVideo]);
|
||||||
|
|
||||||
// 添加视频映射中的所有视频
|
// 添加视频映射中的所有视频
|
||||||
// Object.values(this.videoMapping).forEach(video => {
|
// Object.values(this.videoMapping).forEach(video => {
|
||||||
@ -801,6 +814,9 @@ class WebRTCChat {
|
|||||||
|
|
||||||
async startCall() {
|
async startCall() {
|
||||||
try {
|
try {
|
||||||
|
// 切换到通话中图标
|
||||||
|
this.switchToCallingIcon();
|
||||||
|
|
||||||
// 添加更详细的错误处理
|
// 添加更详细的错误处理
|
||||||
console.log('开始请求麦克风权限...');
|
console.log('开始请求麦克风权限...');
|
||||||
this.localStream = await navigator.mediaDevices.getUserMedia({
|
this.localStream = await navigator.mediaDevices.getUserMedia({
|
||||||
@ -810,8 +826,8 @@ class WebRTCChat {
|
|||||||
console.log('麦克风权限获取成功');
|
console.log('麦克风权限获取成功');
|
||||||
|
|
||||||
await this.createPeerConnection();
|
await this.createPeerConnection();
|
||||||
this.startVoiceRecording()
|
await this.startVoiceRecording();
|
||||||
// this.audioProcessor.startRecording()
|
|
||||||
this.startButton.disabled = true;
|
this.startButton.disabled = true;
|
||||||
this.stopButton.disabled = false;
|
this.stopButton.disabled = false;
|
||||||
|
|
||||||
@ -831,12 +847,25 @@ class WebRTCChat {
|
|||||||
// 通知服务器通话开始
|
// 通知服务器通话开始
|
||||||
this.socket.emit('call-started');
|
this.socket.emit('call-started');
|
||||||
|
|
||||||
|
// 开始播放当前场景的默认视频
|
||||||
|
await this.startDefaultVideoStream();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// 如果出错,恢复到默认图标
|
||||||
|
this.switchToDefaultIcon();
|
||||||
this.logMessage('无法访问麦克风: ' + error.message, 'error');
|
this.logMessage('无法访问麦克风: ' + error.message, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stopCall() {
|
stopCall() {
|
||||||
|
// 恢复到默认图标
|
||||||
|
this.switchToDefaultIcon();
|
||||||
|
|
||||||
|
// 发送用户关闭连接事件到后端
|
||||||
|
if (this.socket && this.socket.connected) {
|
||||||
|
this.socket.emit('user-disconnect');
|
||||||
|
}
|
||||||
|
|
||||||
// 停止音频处理器
|
// 停止音频处理器
|
||||||
if (this.audioProcessor) {
|
if (this.audioProcessor) {
|
||||||
this.audioProcessor.stopRecording();
|
this.audioProcessor.stopRecording();
|
||||||
@ -855,23 +884,59 @@ class WebRTCChat {
|
|||||||
this.peerConnection = null;
|
this.peerConnection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停止当前视频流
|
// 直接刷新页面清除所有缓存
|
||||||
|
console.log('通话已结束,正在刷新页面清除缓存...');
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除视频缓存的方法
|
||||||
|
clearVideoCache() {
|
||||||
|
// 清除视频元素缓存
|
||||||
|
if (this.recordedVideo) {
|
||||||
|
this.recordedVideo.src = '';
|
||||||
|
this.recordedVideo.srcObject = null;
|
||||||
|
this.recordedVideo.load(); // 重新加载空视频
|
||||||
|
// 暂停视频播放
|
||||||
|
this.recordedVideo.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.recordedVideoBuffer) {
|
||||||
|
this.recordedVideoBuffer.src = '';
|
||||||
|
this.recordedVideoBuffer.srcObject = null;
|
||||||
|
this.recordedVideoBuffer.load();
|
||||||
|
this.recordedVideoBuffer.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除视频流缓存
|
||||||
if (this.currentVideoStream) {
|
if (this.currentVideoStream) {
|
||||||
this.currentVideoStream.getTracks().forEach(track => track.stop());
|
this.currentVideoStream.getTracks().forEach(track => track.stop());
|
||||||
this.currentVideoStream = null;
|
this.currentVideoStream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.recordedVideo.srcObject = null;
|
// 清除视频流映射缓存
|
||||||
|
this.videoStreams.clear();
|
||||||
|
|
||||||
|
// 重置视频相关状态
|
||||||
this.currentVideo = null;
|
this.currentVideo = null;
|
||||||
|
this.activeVideoElement = 'main';
|
||||||
|
this.videoSender = null;
|
||||||
|
|
||||||
this.startButton.disabled = false;
|
this.logMessage('视频缓存已清除,视频已停止', 'info');
|
||||||
this.stopButton.disabled = true;
|
}
|
||||||
|
|
||||||
|
// 添加清除API缓存的方法
|
||||||
|
async clearApiCache() {
|
||||||
|
// 重新加载视频映射
|
||||||
|
this.videoMapping = {};
|
||||||
|
await this.loadVideoMapping();
|
||||||
|
|
||||||
// 隐藏结束通话按钮
|
// 重新加载默认视频
|
||||||
this.stopButton.classList.remove('show');
|
await this.loadDefaultVideo();
|
||||||
|
|
||||||
this.updateAudioStatus('未连接', 'disconnected');
|
// 重新加载视频列表
|
||||||
this.logMessage('音频通话已结束,所有资源已释放', 'info');
|
await this.loadVideoList();
|
||||||
|
|
||||||
|
this.logMessage('API缓存已清除并重新加载', 'info');
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPeerConnection() {
|
async createPeerConnection() {
|
||||||
@ -1041,16 +1106,12 @@ class WebRTCChat {
|
|||||||
|
|
||||||
// 修改:使用音频处理器的语音录制功能
|
// 修改:使用音频处理器的语音录制功能
|
||||||
async startVoiceRecording() {
|
async startVoiceRecording() {
|
||||||
const success = await this.audioProcessor.startRecording();
|
const success = await this.audioProcessor.startRecording(this.localStream);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// this.startVoiceButton.disabled = true;
|
|
||||||
// this.stopVoiceButton.disabled = false;
|
|
||||||
// this.startVoiceButton.classList.add('recording');
|
|
||||||
// this.voiceStatus.textContent = '等待语音输入...';
|
|
||||||
this.logMessage('高级语音录制已启动', 'success');
|
this.logMessage('高级语音录制已启动', 'success');
|
||||||
} else {
|
} else {
|
||||||
// this.voiceStatus.textContent = '录音启动失败';
|
this.logMessage('录音启动失败', 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1154,6 +1215,34 @@ class WebRTCChat {
|
|||||||
this.logMessage(`视频流状态: ${JSON.stringify(status)}`, 'info');
|
this.logMessage(`视频流状态: ${JSON.stringify(status)}`, 'info');
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加图标切换方法
|
||||||
|
switchToCallingIcon() {
|
||||||
|
const callIcon = document.getElementById('callIcon');
|
||||||
|
const callingIcon = document.getElementById('callingIcon');
|
||||||
|
const startButton = this.startButton;
|
||||||
|
|
||||||
|
if (callIcon && callingIcon && startButton) {
|
||||||
|
callIcon.style.display = 'none';
|
||||||
|
callingIcon.style.display = 'block';
|
||||||
|
startButton.classList.add('calling');
|
||||||
|
startButton.title = '通话中...';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToDefaultIcon() {
|
||||||
|
const callIcon = document.getElementById('callIcon');
|
||||||
|
const callingIcon = document.getElementById('callingIcon');
|
||||||
|
const startButton = this.startButton;
|
||||||
|
|
||||||
|
if (callIcon && callingIcon && startButton) {
|
||||||
|
callIcon.style.display = 'block';
|
||||||
|
callingIcon.style.display = 'none';
|
||||||
|
startButton.classList.remove('calling');
|
||||||
|
startButton.title = '开始通话';
|
||||||
|
startButton.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async testAllVideoFiles() {
|
async testAllVideoFiles() {
|
||||||
this.logMessage('开始测试所有视频文件...', 'info');
|
this.logMessage('开始测试所有视频文件...', 'info');
|
||||||
|
|||||||
@ -65,19 +65,19 @@ async function processAudioQueue() {
|
|||||||
// 如果当前没有音频在播放,且队列中有音频
|
// 如果当前没有音频在播放,且队列中有音频
|
||||||
if (!isPlaying && audioQueue.length > 0) {
|
if (!isPlaying && audioQueue.length > 0) {
|
||||||
const audioItem = audioQueue.shift();
|
const audioItem = audioQueue.shift();
|
||||||
const sayName = 'say-5s-m-sw'
|
const sayName = '8-4-sh'
|
||||||
const targetVideo = 'd-0.mp4'
|
const targetVideo = window.webrtcApp.interactionVideo
|
||||||
// 如果是第一个音频片段,触发视频切换
|
// 如果是第一个音频片段,触发视频切换
|
||||||
// if (isFirstChunk && sayName != window.webrtcApp.currentVideoTag && window.webrtcApp && window.webrtcApp.switchVideoWithReplaceTrack) {
|
if (isFirstChunk && sayName != window.webrtcApp.currentVideoTag && window.webrtcApp && window.webrtcApp.switchVideoWithReplaceTrack) {
|
||||||
// try {
|
try {
|
||||||
// console.log('--------------触发视频切换:', sayName);
|
console.log('--------------触发视频切换:', sayName);
|
||||||
// window.webrtcApp.switchVideoWithReplaceTrack(targetVideo, 'audio', 'say-5s-m-sw');
|
window.webrtcApp.switchVideoWithReplaceTrack(targetVideo, 'audio', '8-4-sh');
|
||||||
// isFirstChunk = false;
|
isFirstChunk = false;
|
||||||
// window.webrtcApp.currentVideoTag = sayName;
|
window.webrtcApp.currentVideoTag = sayName;
|
||||||
// } catch (error) {
|
} catch (error) {
|
||||||
// console.error('视频切换失败:', error);
|
console.error('视频切换失败:', error);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
await playAudioData(audioItem.audioData);
|
await playAudioData(audioItem.audioData);
|
||||||
} else {
|
} else {
|
||||||
// 等待一小段时间再检查
|
// 等待一小段时间再检查
|
||||||
@ -89,11 +89,11 @@ async function processAudioQueue() {
|
|||||||
const text = 'default'
|
const text = 'default'
|
||||||
// await new Promise(resolve => setTimeout(resolve, 500));
|
// await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
console.log("音频结束------------------------:", window.webrtcApp.currentVideoTag, isPlaying)
|
console.log("音频结束------------------------:", window.webrtcApp.currentVideoTag, isPlaying)
|
||||||
// if (window.webrtcApp.currentVideoTag != text) {
|
if (window.webrtcApp.currentVideoTag != text) {
|
||||||
// isFirstChunk = true
|
isFirstChunk = true
|
||||||
// window.webrtcApp.currentVideoTag = text
|
window.webrtcApp.currentVideoTag = text
|
||||||
// window.webrtcApp.switchVideoWithReplaceTrack(window.webrtcApp.defaultVideo, 'audio', text);
|
window.webrtcApp.switchVideoWithReplaceTrack(window.webrtcApp.defaultVideo, 'audio', text);
|
||||||
// }
|
}
|
||||||
console.log('音频队列处理完成');
|
console.log('音频队列处理完成');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
videos/0-2.mp4
BIN
videos/0-2.mp4
Binary file not shown.
BIN
videos/0.mp4
BIN
videos/0.mp4
Binary file not shown.
BIN
videos/1-m.mp4
BIN
videos/1-m.mp4
Binary file not shown.
BIN
videos/4-m.mp4
BIN
videos/4-m.mp4
Binary file not shown.
BIN
videos/5.mp4
BIN
videos/5.mp4
Binary file not shown.
BIN
videos/6.mp4
BIN
videos/6.mp4
Binary file not shown.
BIN
videos/8-4-bd-1.mp4
Normal file
BIN
videos/8-4-bd-1.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-bd-2.mp4
Normal file
BIN
videos/8-4-bd-2.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-bd.mp4
Normal file
BIN
videos/8-4-bd.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-hc-bd.mp4
Normal file
BIN
videos/8-4-hc-bd.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-hc-sh.mp4
Normal file
BIN
videos/8-4-hc-sh.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-kc-bd.mp4
Normal file
BIN
videos/8-4-kc-bd.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-kc-sh.mp4
Normal file
BIN
videos/8-4-kc-sh.mp4
Normal file
Binary file not shown.
BIN
videos/8-4-sh.mp4
Normal file
BIN
videos/8-4-sh.mp4
Normal file
Binary file not shown.
BIN
videos/bd-1.mp4
BIN
videos/bd-1.mp4
Binary file not shown.
BIN
videos/chang.mp4
BIN
videos/chang.mp4
Binary file not shown.
BIN
videos/d-0.mp4
BIN
videos/d-0.mp4
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user