【m】测试,便于回退

This commit is contained in:
2026-04-23 16:08:08 +08:00
parent 852a169c30
commit b66b639df0
3 changed files with 232 additions and 17 deletions

View File

@@ -16,6 +16,8 @@ class UIRenderer {
// 头部和底部
header: document.querySelector('header'),
footer: document.querySelector('footer'),
// 多Participant视频网格
participantGrid: document.getElementById('participantGrid'),
// 头部内容
headerTitle: document.getElementById('headerTitle'),
callDuration: document.getElementById('callDuration'),
@@ -144,10 +146,12 @@ class UIRenderer {
// 本地流获取成功 - 更新本地视频显示
this.renderLocalStream(state.localStream); // 渲染本地流
this.renderLocalVideo(state.session.localUser, state.localStream); // 渲染本地视频
// 如果host端有participant tile同步更新它们的视频源
this.updateParticipantGridStreams(state.localStream);
break;
case 'REMOTE_STREAM_OBTAINED':
// 远程流获取成功 - 更新远程视频显示
this.renderRemoteStream(state.remoteStream); // 渲染远程流
this.renderRemoteStream(changes.stream, changes.connectionId, changes.isHost); // 渲染远程流
// 当获取到远程流时,隐藏连接中提示
if (this.elements.connectingOverlay) {
this.elements.connectingOverlay.classList.add('hidden');
@@ -387,7 +391,127 @@ class UIRenderer {
}
// 渲染远程视频流
renderRemoteStream(stream) {
renderRemoteStream(stream, connectionId, isHost) {
if (isHost && connectionId) {
// Host端: 渲染到 participant 视频网格
this.renderParticipantStream(stream, connectionId);
} else {
// Participant端: 渲染到单一远端视频Host的画面
this.renderSingleRemoteStream(stream);
}
}
// 渲染Host端的多Participant视频网格
// 所有participant tile显示host的本地视频流host监控自己广播的画面
renderParticipantStream(stream, connectionId) {
const grid = this.elements.participantGrid;
if (!grid) return;
// 显示网格,隐藏单路远端视频
grid.classList.remove('hidden');
// 查找或创建该 connectionId 的视频格子
let tile = grid.querySelector(`[data-participant-id="${connectionId}"]`);
if (!tile) {
// 创建新的视频格子
tile = document.createElement('div');
tile.className = 'relative bg-black/60 rounded-xl overflow-hidden flex items-center justify-center';
tile.dataset.participantId = connectionId;
const video = document.createElement('video');
video.className = 'w-full h-full object-contain';
video.autoplay = true;
video.playsinline = true;
video.muted = true; // 静音因为显示的是host自己的画面不需要播放自己的声音
video.id = `participantVideo_${connectionId}`;
tile.appendChild(video);
// 添加隐藏的audio元素播放远端音频participant的声音
const audio = document.createElement('audio');
audio.autoplay = true;
audio.id = `participantAudio_${connectionId}`;
audio.style.display = 'none';
tile.appendChild(audio);
// 添加参与者名称标签
const label = document.createElement('div');
label.className = 'absolute bottom-3 left-3 glass px-3 py-1 rounded-full text-xs flex items-center gap-2';
label.innerHTML = `<i class="fas fa-user text-indigo-400"></i><span>Participant ${connectionId.slice(-4)}</span>`;
tile.appendChild(label);
// 添加"直播中"标识
const liveTag = document.createElement('div');
liveTag.className = 'absolute top-3 right-3 bg-red-500/80 px-2 py-0.5 rounded-full text-xs flex items-center gap-1';
liveTag.innerHTML = `<span class="w-1.5 h-1.5 bg-white rounded-full animate-pulse"></span><span>直播</span>`;
tile.appendChild(liveTag);
grid.appendChild(tile);
console.log(`Created participant video tile for ${connectionId}`);
}
if (tile) {
// 视频元素显示host的本地流监控自己广播的画面
const video = tile.querySelector('video');
if (video) {
const localStream = this.stateManager.getState().localStream;
if (localStream) {
video.srcObject = localStream;
console.log(`Set host local stream for participant tile ${connectionId}`);
} else {
console.warn('Host local stream not available for participant tile');
}
}
// 隐藏audio元素播放远端流participant的音频
const audio = tile.querySelector('audio');
if (audio && stream) {
audio.srcObject = stream;
console.log(`Set remote audio for participant tile ${connectionId}`);
}
// 隐藏单路远端视频和占位符
const remoteVideoDiv = this.elements.remoteVideo?.closest('.absolute.inset-0.video-fade-in');
if (remoteVideoDiv) {
remoteVideoDiv.classList.add('hidden');
}
}
// 根据参与者数量调整网格列数
const tileCount = grid.querySelectorAll('[data-participant-id]').length;
if (tileCount <= 1) {
grid.style.gridTemplateColumns = '1fr';
} else if (tileCount <= 4) {
grid.style.gridTemplateColumns = 'repeat(2, 1fr)';
} else {
grid.style.gridTemplateColumns = 'repeat(3, 1fr)';
}
// 隐藏连接中提示
if (this.elements.connectingOverlay) {
this.elements.connectingOverlay.classList.add('hidden');
}
// 隐藏远端视频占位符
if (this.elements.remoteVideoPlaceholder) {
this.elements.remoteVideoPlaceholder.classList.add('hidden');
}
}
// 更新participant网格中所有tile的视频源host本地流变化时调用
updateParticipantGridStreams(localStream) {
const grid = this.elements.participantGrid;
if (!grid || grid.classList.contains('hidden')) return;
grid.querySelectorAll('[data-participant-id]').forEach(tile => {
const video = tile.querySelector('video');
if (video && localStream) {
video.srcObject = localStream;
}
});
}
// 渲染Participant端的单一远端视频Host画面
renderSingleRemoteStream(stream) {
if (this.elements.remoteVideo && stream) {
console.log('Rendering remote stream:', stream);
@@ -892,6 +1016,20 @@ class UIRenderer {
// 渲染通话结束
renderCallEnded() {
console.log('Call ended');
// 清理participant网格
const grid = this.elements.participantGrid;
if (grid) {
grid.querySelectorAll('[data-participant-id]').forEach(tile => {
const video = tile.querySelector('video');
if (video) video.srcObject = null;
const audio = tile.querySelector('audio');
if (audio) audio.srcObject = null;
tile.remove();
});
grid.classList.add('hidden');
}
// 跳转到结束通话界面
window.location.href = './endcall/endcall.html';
}
@@ -899,14 +1037,55 @@ class UIRenderer {
// 渲染participant离开host端房间仍然存在
renderParticipantLeft(connectionId) {
console.log(`Participant left: ${connectionId}, updating UI`);
// 移除该 participant 的视频格子
const grid = this.elements.participantGrid;
if (grid) {
const tile = grid.querySelector(`[data-participant-id="${connectionId}"]`);
if (tile) {
// 清理video元素
const video = tile.querySelector('video');
if (video) {
video.srcObject = null;
}
// 清理audio元素
const audio = tile.querySelector('audio');
if (audio) {
audio.srcObject = null;
}
tile.remove();
console.log(`Removed participant video tile for ${connectionId}`);
}
// 如果没有 participant 了,隐藏网格,显示单路远端视频
const remainingTiles = grid.querySelectorAll('[data-participant-id]');
if (remainingTiles.length === 0) {
grid.classList.add('hidden');
// 显示单路远端视频区域(恢复默认)
const remoteVideoDiv = this.elements.remoteVideo?.closest('.absolute.inset-0.video-fade-in');
if (remoteVideoDiv) {
remoteVideoDiv.classList.remove('hidden');
}
// 显示远端视频占位符
if (this.elements.remoteVideoPlaceholder) {
this.elements.remoteVideoPlaceholder.classList.remove('hidden');
}
} else {
// 调整网格列数
if (remainingTiles.length <= 1) {
grid.style.gridTemplateColumns = '1fr';
} else if (remainingTiles.length <= 4) {
grid.style.gridTemplateColumns = 'repeat(2, 1fr)';
} else {
grid.style.gridTemplateColumns = 'repeat(3, 1fr)';
}
}
}
// 更新远程用户状态显示为离线
if (this.elements.remoteNetworkIndicator) {
this.elements.remoteNetworkIndicator.className = 'w-2 h-2 bg-gray-500 rounded-full';
}
// 显示断开连接的遮罩层(如果存在)
if (this.elements.disconnectedOverlay) {
this.elements.disconnectedOverlay.classList.remove('hidden');
}
}
// 获取状态文本