From f416db6375b0278355ebc2a8dae7ecce11c53539 Mon Sep 17 00:00:00 2001 From: zhangzheng Date: Fri, 6 Mar 2026 13:59:38 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90m=E3=80=91=E8=A7=86=E9=A2=91=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebApp/client/public/onebyone/store.js | 107 ++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/WebApp/client/public/onebyone/store.js b/WebApp/client/public/onebyone/store.js index a2b644e..131585e 100644 --- a/WebApp/client/public/onebyone/store.js +++ b/WebApp/client/public/onebyone/store.js @@ -99,6 +99,9 @@ class CallStateManager { // 发送媒体状态到服务器 this.emitMediaStateChange(); + + // 启动本地音频活动检测 + this.startLocalActivityDetection(); } catch (error) { console.error('Error getting local stream:', error); // 如果获取视频失败,保持视频关闭状态 @@ -232,10 +235,15 @@ class CallStateManager { // 通知UI远程流已更新 this.notify({ type: 'REMOTE_STREAM_OBTAINED', stream: this.state.remoteStream }); - // 如果是音频轨道,启动音频活动检测 + // 如果是音频轨道,启动远程音频活动检测 if (data.track.kind === 'audio') { this.startRemoteActivityDetection(); } + } else if (direction == "sendonly") { + // 本地发送轨道,启动本地音频活动检测 + if (data.track.kind === 'audio') { + this.startLocalActivityDetection(); + } } }; @@ -245,6 +253,9 @@ class CallStateManager { // 启动网络质量检测 this.startNetworkQualityDetection(); + + // 启动本地音频活动检测 + this.startLocalActivityDetection(); // 启动远端音频活动检测 this.startRemoteActivityDetection(); //模拟远端活动 (开发测试用) @@ -468,7 +479,7 @@ class CallStateManager { console.error('Error detecting network quality:', error); } } - // 真实音频活动检测 + // 真实音频活动检测 - 远端 startRemoteActivityDetection() { // 检查是否有远端音频流 if (!this.state.remoteStream) { @@ -553,6 +564,98 @@ class CallStateManager { console.error('Error starting remote activity detection:', error); } } + + // 真实音频活动检测 - 本地 + startLocalActivityDetection() { + // 检查是否有本地音频流 + if (!this.state.localStream) { + return; + } + + // 获取音频轨道 + const audioTracks = this.state.localStream.getAudioTracks(); + if (audioTracks.length === 0) { + return; + } + + try { + // 创建音频上下文 + const audioContext = new (window.AudioContext || window.webkitAudioContext)(); + + // 创建媒体流源 + const source = audioContext.createMediaStreamSource(this.state.localStream); + + // 创建音频分析器 + const analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; + + // 连接音频节点 + source.connect(analyser); + + // 创建数据缓冲区 + const dataArray = new Uint8Array(analyser.frequencyBinCount); + + // 检测参数 + const threshold = 15; // 音频电平阈值 + const debounceTime = 500; // 防抖时间 + let isSpeaking = false; + let lastActivityTime = 0; + + // 音频活动检测循环 + const detectActivity = () => { + if (!this.state.localStream || !this.renderstreaming) { + return; + } + + // 获取时域数据 + analyser.getByteTimeDomainData(dataArray); + + // 计算音频电平 + let sum = 0; + for (let i = 0; i < dataArray.length; i++) { + // 转换为振幅 (0-255 → -128-127) + const amplitude = dataArray[i] - 128; + sum += amplitude * amplitude; + } + const rms = Math.sqrt(sum / dataArray.length); + const level = rms / 128; // 归一化到 0-1 + + // 检测说话状态 + const currentTime = Date.now(); + if (level > threshold / 100) { + // 检测到说话 + lastActivityTime = currentTime; + if (!isSpeaking) { + isSpeaking = true; + this.state.session.localUser.mediaState.isSpeaking = true; + this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'isSpeaking', value: true }); + // 发送媒体状态到服务器 + this.emitMediaStateChange(); + } + } else if (isSpeaking && currentTime - lastActivityTime > debounceTime) { + // 停止说话 + isSpeaking = false; + this.state.session.localUser.mediaState.isSpeaking = false; + this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'isSpeaking', value: false }); + // 发送媒体状态到服务器 + this.emitMediaStateChange(); + } + + // 继续检测 + if (this.state.session.status === 'ongoing') { + requestAnimationFrame(detectActivity); + } + }; + + // 开始检测 + detectActivity(); + + console.log('Local activity detection started'); + + } catch (error) { + console.error('Error starting local activity detection:', error); + } + } // 启动网络质量检测 startNetworkQualityDetection() { // 每3秒检测一次网络质量