【m】视频状态检测
This commit is contained in:
@@ -99,6 +99,9 @@ class CallStateManager {
|
|||||||
|
|
||||||
// 发送媒体状态到服务器
|
// 发送媒体状态到服务器
|
||||||
this.emitMediaStateChange();
|
this.emitMediaStateChange();
|
||||||
|
|
||||||
|
// 启动本地音频活动检测
|
||||||
|
this.startLocalActivityDetection();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error getting local stream:', error);
|
console.error('Error getting local stream:', error);
|
||||||
// 如果获取视频失败,保持视频关闭状态
|
// 如果获取视频失败,保持视频关闭状态
|
||||||
@@ -232,10 +235,15 @@ class CallStateManager {
|
|||||||
// 通知UI远程流已更新
|
// 通知UI远程流已更新
|
||||||
this.notify({ type: 'REMOTE_STREAM_OBTAINED', stream: this.state.remoteStream });
|
this.notify({ type: 'REMOTE_STREAM_OBTAINED', stream: this.state.remoteStream });
|
||||||
|
|
||||||
// 如果是音频轨道,启动音频活动检测
|
// 如果是音频轨道,启动远程音频活动检测
|
||||||
if (data.track.kind === 'audio') {
|
if (data.track.kind === 'audio') {
|
||||||
this.startRemoteActivityDetection();
|
this.startRemoteActivityDetection();
|
||||||
}
|
}
|
||||||
|
} else if (direction == "sendonly") {
|
||||||
|
// 本地发送轨道,启动本地音频活动检测
|
||||||
|
if (data.track.kind === 'audio') {
|
||||||
|
this.startLocalActivityDetection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,6 +253,9 @@ class CallStateManager {
|
|||||||
|
|
||||||
// 启动网络质量检测
|
// 启动网络质量检测
|
||||||
this.startNetworkQualityDetection();
|
this.startNetworkQualityDetection();
|
||||||
|
|
||||||
|
// 启动本地音频活动检测
|
||||||
|
this.startLocalActivityDetection();
|
||||||
// 启动远端音频活动检测
|
// 启动远端音频活动检测
|
||||||
this.startRemoteActivityDetection();
|
this.startRemoteActivityDetection();
|
||||||
//模拟远端活动 (开发测试用)
|
//模拟远端活动 (开发测试用)
|
||||||
@@ -468,7 +479,7 @@ class CallStateManager {
|
|||||||
console.error('Error detecting network quality:', error);
|
console.error('Error detecting network quality:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 真实音频活动检测
|
// 真实音频活动检测 - 远端
|
||||||
startRemoteActivityDetection() {
|
startRemoteActivityDetection() {
|
||||||
// 检查是否有远端音频流
|
// 检查是否有远端音频流
|
||||||
if (!this.state.remoteStream) {
|
if (!this.state.remoteStream) {
|
||||||
@@ -553,6 +564,98 @@ class CallStateManager {
|
|||||||
console.error('Error starting remote activity detection:', error);
|
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() {
|
startNetworkQualityDetection() {
|
||||||
// 每3秒检测一次网络质量
|
// 每3秒检测一次网络质量
|
||||||
|
|||||||
Reference in New Issue
Block a user