【m】本地视频更新

This commit is contained in:
zhangzheng
2026-03-04 11:19:50 +08:00
parent b15a1ddd0d
commit fd00100808
3 changed files with 114 additions and 11 deletions

View File

@@ -201,11 +201,14 @@
<div class="absolute bottom-6 right-6 w-64 h-48 rounded-2xl overflow-hidden shadow-2xl border-2 border-white/20 video-fade-in z-10">
<!-- [DATA_FIELD: localUser.videoStream] [TYPE: MediaStream | null] [BIND: srcObject] -->
<!-- [FALLBACK: localUser.avatar] [TYPE: string] [URL] -->
<img id="localVideo"
src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=300&fit=crop"
alt="本地视频"
class="w-full h-full object-cover"
data-field="localUser.videoStream">
<video id="localVideo"
src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=300&fit=crop"
alt="本地视频"
class="w-full h-full object-cover"
autoplay
muted
data-field="localUser.videoStream">
</video>
<!-- [CONDITIONAL_RENDER: localUser.mediaState.video === false] -->
<div id="localVideoPlaceholder" class="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-indigo-900 to-purple-900 hidden" data-field="localUser.videoOff">

View File

@@ -70,6 +70,9 @@ class UIRenderer {
this.renderLocalVideo(state.session.localUser);
this.renderLocalUserStatus(state.session.localUser);
break;
case 'LOCAL_STREAM_OBTAINED':
this.renderLocalStream(state.localStream);
break;
case 'REMOTE_MEDIA_CHANGE':
this.renderRemoteVideo(state.session.remoteUser);
break;
@@ -150,6 +153,18 @@ class UIRenderer {
this.renderLocalUserStatus(localUser);
}
// 渲染本地视频流
renderLocalStream(stream) {
if (this.elements.localVideo && stream) {
this.elements.localVideo.srcObject = stream;
this.elements.localVideo.autoplay = true;
this.elements.localVideo.muted = true; // 本地视频静音,避免回声
console.log('srcObject set successfully:', this.elements.localVideo.srcObject);
} else {
console.error('Either localVideo element or stream is missing');
}
}
// 渲染本地用户状态
renderLocalUserStatus(localUser) {
// 更新本地媒体状态文本

View File

@@ -44,6 +44,9 @@ class CallStateManager {
this.notify({ type: 'DURATION_UPDATE', duration: this.state.session.duration });
}, 1000);
// 获取本地摄像头视频流
this.getLocalStream();
// 模拟远端音频活动 (实际应由 WebRTC VAD 检测触发)
this.simulateRemoteActivity();
@@ -51,13 +54,95 @@ class CallStateManager {
this.simulateNetworkChange();
}
// 更新本地媒体状态
updateLocalMedia(mediaType, value) {
this.state.session.localUser.mediaState[mediaType] = value;
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value });
// 获取本地摄像头视频流
async getLocalStream() {
try {
console.log('Requesting camera permission...');
// 发送媒体状态到服务器
this.emitMediaStateChange();
// 检查浏览器是否支持getUserMedia
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
console.error('getUserMedia is not supported');
throw new Error('getUserMedia is not supported');
}
// 请求摄像头权限并获取媒体流
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
console.log('Stream obtained successfully:', stream);
console.log('Video tracks:', stream.getVideoTracks());
console.log('Audio tracks:', stream.getAudioTracks());
this.state.localStream = stream;
this.state.session.localUser.mediaState.video = true;
this.state.session.localUser.mediaState.audio = true;
console.log('Local stream stored, notifying UI...');
// 先通知视频流已获取
this.notify({ type: 'LOCAL_STREAM_OBTAINED', stream });
// 再通知媒体状态变化
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'video', value: true });
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'audio', value: true });
// 发送媒体状态到服务器
this.emitMediaStateChange();
} catch (error) {
console.error('Error getting local stream:', error);
// 如果获取视频失败,保持视频关闭状态
this.state.session.localUser.mediaState.video = false;
this.state.session.localUser.mediaState.audio = false;
// 通知媒体状态变化
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'video', value: false });
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType: 'audio', value: false });
}
}
// 更新本地媒体状态
async updateLocalMedia(mediaType, value) {
// 如果是关闭视频,释放摄像头资源
if (mediaType === 'video' && !value && this.state.localStream) {
this.state.localStream.getTracks().forEach(track => {
if (track.kind === 'video') {
track.stop();
}
});
}
// 如果是音频状态变化,控制本地音频轨道
if (mediaType === 'audio' && this.state.localStream) {
this.state.localStream.getTracks().forEach(track => {
if (track.kind === 'audio') {
track.enabled = value;
}
});
}
// 如果是开启视频,重新获取摄像头资源
if (mediaType === 'video' && value ) {
if(this.state.localStream){
this.state.localStream=null;
}
//if(this.state.localStream.getVideoTracks().length==0){
// 请求摄像头权限并获取媒体流
this.state.localStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
// }
await this.getLocalStream();
} else {
// 直接更新媒体状态
this.state.session.localUser.mediaState[mediaType] = value;
this.notify({ type: 'LOCAL_MEDIA_CHANGE', mediaType, value });
// 发送媒体状态到服务器
this.emitMediaStateChange();
}
}
// 更新远端媒体状态 (由 WebSocket 消息触发)