【m】本地视频更新
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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) {
|
||||
// 更新本地媒体状态文本
|
||||
|
||||
@@ -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 消息触发)
|
||||
|
||||
Reference in New Issue
Block a user