【m】远端占位背景添加

This commit is contained in:
zhangzheng
2026-03-05 11:30:27 +08:00
parent dfc75172b8
commit 425c85dd9c
3 changed files with 110 additions and 7 deletions

View File

@@ -155,6 +155,17 @@
data-field="remoteUser.videoStream"> data-field="remoteUser.videoStream">
</video> </video>
<!-- 远端未连接时的占位背景 -->
<div id="remoteVideoPlaceholder" class="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-indigo-900/80 to-purple-900/80">
<div class="text-center">
<div class="w-32 h-32 rounded-full bg-indigo-700/50 flex items-center justify-center mx-auto mb-4">
<i class="fas fa-user text-4xl text-white/70"></i>
</div>
<p class="text-white text-lg font-medium">等待对方连接...</p>
<p class="text-sm text-gray-400 mt-2">请确保对方已加入通话</p>
</div>
</div>
<!-- 远端信息覆盖层 --> <!-- 远端信息覆盖层 -->
<div class="absolute top-6 left-6 glass px-4 py-2 rounded-full flex items-center gap-3"> <div class="absolute top-6 left-6 glass px-4 py-2 rounded-full flex items-center gap-3">
<div class="relative"> <div class="relative">

View File

@@ -22,6 +22,7 @@ class UIRenderer {
// 远端视频 // 远端视频
remoteVideo: document.getElementById('remoteVideo'), remoteVideo: document.getElementById('remoteVideo'),
remoteVideoPlaceholder: document.getElementById('remoteVideoPlaceholder'),
remoteAvatar: document.getElementById('remoteAvatar'), remoteAvatar: document.getElementById('remoteAvatar'),
remoteName: document.getElementById('remoteName'), remoteName: document.getElementById('remoteName'),
remoteStatus: document.getElementById('remoteStatus'), remoteStatus: document.getElementById('remoteStatus'),
@@ -57,6 +58,17 @@ class UIRenderer {
this.unsubscribe = stateManager.subscribe(this.render.bind(this)); this.unsubscribe = stateManager.subscribe(this.render.bind(this));
// 初始化渲染 // 初始化渲染
this.render(this.stateManager.getState(), { type: 'INIT' }); this.render(this.stateManager.getState(), { type: 'INIT' });
window.addEventListener('resize', () => {
if (this.elements.remoteVideo && this.elements.remoteVideo.srcObject) {
const stream = this.elements.remoteVideo.srcObject;
const videoTracks = stream.getVideoTracks();
if (videoTracks.length > 0) {
const resolution = this.getVideoResolution(videoTracks[0]);
this.adjustVideoSize(this.elements.remoteVideo, resolution);
}
}
});
} }
// 绑定事件监听器 // 绑定事件监听器
@@ -75,6 +87,15 @@ class UIRenderer {
this.renderControlButtons(state.session.localUser.mediaState); this.renderControlButtons(state.session.localUser.mediaState);
this.renderChatMessages(state.messages); this.renderChatMessages(state.messages);
this.renderUserList(state.session.localUser, state.session.remoteUser); this.renderUserList(state.session.localUser, state.session.remoteUser);
// 初始化时检查远程流状态,显示或隐藏占位背景
if (this.elements.remoteVideoPlaceholder) {
if (state.remoteStream) {
this.elements.remoteVideoPlaceholder.classList.add('hidden');
} else {
this.elements.remoteVideoPlaceholder.classList.remove('hidden');
}
}
break; break;
case 'DURATION_UPDATE': case 'DURATION_UPDATE':
this.renderCallDuration(changes.duration); this.renderCallDuration(changes.duration);
@@ -215,14 +236,41 @@ class UIRenderer {
if (this.elements.remoteVideo && stream) { if (this.elements.remoteVideo && stream) {
this.elements.remoteVideo.srcObject = stream; this.elements.remoteVideo.srcObject = stream;
this.elements.remoteVideo.autoplay = true; this.elements.remoteVideo.autoplay = true;
// 关键设置:启用硬件加速和最佳质量渲染
this.elements.remoteVideo.style.transform = 'translateZ(0)'; // 启用硬件加速
this.elements.remoteVideo.style.imageRendering = 'pixelated'; // 保持像素清晰
this.elements.remoteVideo.style.objectFit = 'contain'; // 保持比例
console.log('Remote stream set successfully:', this.elements.remoteVideo.srcObject); console.log('Remote stream set successfully:', this.elements.remoteVideo.srcObject);
// 隐藏断开连接覆盖层 // 隐藏断开连接覆盖层
if (this.elements.disconnectedOverlay) { if (this.elements.disconnectedOverlay) {
this.elements.disconnectedOverlay.classList.add('hidden'); this.elements.disconnectedOverlay.classList.add('hidden');
} }
// 隐藏占位背景
if (this.elements.remoteVideoPlaceholder) {
this.elements.remoteVideoPlaceholder.classList.add('hidden');
}
// 获取视频轨道并处理分辨率
const videoTracks = stream.getVideoTracks();
if (videoTracks.length > 0) {
const resolution = this.getVideoResolution(videoTracks[0]);
this.adjustVideoSize(this.elements.remoteVideo, resolution);
// 监听轨道变化,处理分辨率调整
videoTracks[0].addEventListener('resize', () => {
const newResolution = this.getVideoResolution(videoTracks[0]);
this.adjustVideoSize(this.elements.remoteVideo, newResolution);
});
}
} else { } else {
console.error('Either remoteVideo element or stream is missing'); console.error('Either remoteVideo element or stream is missing');
// 显示占位背景
if (this.elements.remoteVideoPlaceholder) {
this.elements.remoteVideoPlaceholder.classList.remove('hidden');
}
} }
} }
@@ -287,7 +335,44 @@ class UIRenderer {
} }
} }
} }
// 在renderer.js中添加方法
// 获取视频流分辨率
getVideoResolution(track) {
if (track && track.getSettings) {
const settings = track.getSettings();
return {
width: settings.width || 640,
height: settings.height || 480
};
}
return { width: 640, height: 480 }; // 默认值
}
// 调整视频元素大小并居中显示
adjustVideoSize(videoElement, resolution) {
if (!videoElement) return;
const { width, height } = resolution;
const aspectRatio = width / height;
// 根据容器大小和视频宽高比调整视频显示
const container = videoElement.parentElement;
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
// 启用硬件加速
videoElement.style.transform = 'translateZ(0)';
videoElement.style.willChange = 'transform';
// 设置容器为flex布局使视频居中
container.style.display = 'flex';
container.style.alignItems = 'center';
container.style.justifyContent = 'center';
// 优化图像渲染
videoElement.style.imageRendering = 'pixelated';
// 确保视频元素在容器内正确显示
videoElement.style.maxWidth = '100%';
videoElement.style.maxHeight = '100%';
videoElement.style.objectFit = 'contain';
}
// 渲染控制按钮 // 渲染控制按钮
renderControlButtons(mediaState) { renderControlButtons(mediaState) {
if (this.elements.micBtn) { if (this.elements.micBtn) {

View File

@@ -194,6 +194,13 @@ class CallStateManager {
// 创建信令实例 // 创建信令实例
const signaling = this.useWebSocket ? new WebSocketSignaling() : new Signaling(); const signaling = this.useWebSocket ? new WebSocketSignaling() : new Signaling();
const config = getRTCConfiguration(); // 获取RTC配置 const config = getRTCConfiguration(); // 获取RTC配置
// 优化RTC配置确保支持高分辨率
config.peerConnectionOptions = {
optional: [
{ googCpuOveruseDetection: false }, // 禁用CPU过度使用检测
{ googScreencastMinBitrate: 3000 } // 设置最小比特率
]
};
this.renderstreaming = new RenderStreaming(signaling, config); // 创建WebRTC连接管理实例 this.renderstreaming = new RenderStreaming(signaling, config); // 创建WebRTC连接管理实例
// 连接建立回调 // 连接建立回调