From 4baad207d63751e3fd81e130e3e205baeff02c5d Mon Sep 17 00:00:00 2001 From: stary <834207172@qq.COM> Date: Fri, 24 Apr 2026 23:38:17 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90m=E3=80=91=E8=A7=86=E9=A2=91=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=AE=8C=E6=88=90=EF=BC=8C=E6=B5=8B=E8=AF=95=E6=B2=A1?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebApp/client/public/onebyone/renderer.js | 78 ++++------------------- WebApp/src/class/websockethandler.ts | 2 +- WebApp/src/websocket.ts | 18 ++---- 3 files changed, 20 insertions(+), 78 deletions(-) diff --git a/WebApp/client/public/onebyone/renderer.js b/WebApp/client/public/onebyone/renderer.js index 869a267..53a6827 100644 --- a/WebApp/client/public/onebyone/renderer.js +++ b/WebApp/client/public/onebyone/renderer.js @@ -143,11 +143,8 @@ class UIRenderer { this.renderUserList(state.session.localUser, state.session.remoteUser); // 渲染用户列表 break; case 'LOCAL_STREAM_OBTAINED': - // 本地流获取成功 - 更新本地视频显示 - this.renderLocalStream(state.localStream); // 渲染本地流 - this.renderLocalVideo(state.session.localUser, state.localStream); // 渲染本地视频 - // 如果host端有participant tile,同步更新它们的视频源 - this.updateParticipantGridStreams(state.localStream); + this.renderLocalStream(state.localStream); + this.renderLocalVideo(state.session.localUser, state.localStream); break; case 'REMOTE_STREAM_OBTAINED': // 远程流获取成功 - 更新远程视频显示 @@ -402,7 +399,7 @@ class UIRenderer { } // 渲染Host端的多Participant视频网格 - // 所有participant tile显示host的本地视频流(host监控自己广播的画面) + // 每个participant tile显示该participant实际的远端视频流 renderParticipantStream(stream, connectionId) { const grid = this.elements.participantGrid; if (!grid) return; @@ -413,7 +410,6 @@ class UIRenderer { // 查找或创建该 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; @@ -422,27 +418,20 @@ class UIRenderer { video.className = 'w-full h-full object-contain'; video.autoplay = true; video.playsinline = true; - video.muted = true; // 静音,因为显示的是host自己的画面,不需要播放自己的声音 + video.muted = false; // 不静音,播放participant的音频 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 = `Participant ${connectionId.slice(-4)}`; 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 = `直播`; + liveTag.className = 'absolute top-3 right-3 bg-green-500/80 px-2 py-0.5 rounded-full text-xs flex items-center gap-1'; + liveTag.innerHTML = `在线`; tile.appendChild(liveTag); grid.appendChild(tile); @@ -450,23 +439,11 @@ class UIRenderer { } if (tile) { - // 视频元素显示host的本地流(监控自己广播的画面) + // 视频元素显示participant的远端视频流 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}`); + if (video && stream) { + video.srcObject = stream; + console.log(`Set remote stream for participant tile ${connectionId}`); } // 隐藏单路远端视频和占位符 @@ -497,19 +474,6 @@ class UIRenderer { } } - // 更新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) { @@ -1023,8 +987,6 @@ class UIRenderer { 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'); @@ -1038,40 +1000,27 @@ class UIRenderer { 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; - } + if (video) video.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) { @@ -1082,7 +1031,6 @@ class UIRenderer { } } - // 更新远程用户状态显示为离线 if (this.elements.remoteNetworkIndicator) { this.elements.remoteNetworkIndicator.className = 'w-2 h-2 bg-gray-500 rounded-full'; } diff --git a/WebApp/src/class/websockethandler.ts b/WebApp/src/class/websockethandler.ts index e126dba..e8d92e1 100644 --- a/WebApp/src/class/websockethandler.ts +++ b/WebApp/src/class/websockethandler.ts @@ -192,7 +192,7 @@ function onDisconnect(ws: WebSocket, connectionId: string): void { } else { // participant断开连接,从组中移除并通知host(使用participant-left类型,host不会关闭房间) group.participants.delete(ws); - group.host.send(JSON.stringify({ type: "participant-left", connectionId: connectionId })); + group.host.send(JSON.stringify({ type: "participant-left", connectionId: connectionId, participantId: (ws as any).participantId })); console.log(`Participant left connectionId: ${connectionId}, remaining participants: ${group.participants.size}`); } } diff --git a/WebApp/src/websocket.ts b/WebApp/src/websocket.ts index 781499c..f92285c 100644 --- a/WebApp/src/websocket.ts +++ b/WebApp/src/websocket.ts @@ -74,46 +74,40 @@ export default class WSSignaling { // 根据消息类型处理 switch (msg.type) { case "connect": - // 处理连接请求 handler.onConnect(ws, msg.connectionId); break; case "disconnect": - // 处理断开连接请求 handler.onDisconnect(ws, msg.connectionId); break; case "offer": - // 处理offer信令 + if (msg.participantId !== undefined) msg.data.participantId = msg.participantId; handler.onOffer(ws, msg.data); break; case "answer": - // 处理answer信令 + if (msg.participantId !== undefined) msg.data.participantId = msg.participantId; handler.onAnswer(ws, msg.data); break; case "candidate": - // 处理candidate信令 + if (msg.participantId !== undefined) msg.data.participantId = msg.participantId; handler.onCandidate(ws, msg.data); break; case "ping": - // 处理心跳请求,回复pong ws.send(JSON.stringify({ type: "pong" })); break; case "pong": - // 处理心跳响应,更新最后活动时间 (ws as any).lastActivity = Date.now(); break; case "broadcast": handler.onBroadcast(ws, msg.data); break; - case 'call-request'://接受连接ConnectionId - // 处理callConnectionId信令 + case 'call-request': handler.onCallConnectionId(ws, msg.data); break; - case 'on-message'://接受连接ConnectionId - // 处理chat-message信令 + case 'on-message': + if (msg.from) msg.data.connectionId = msg.from; handler.onMessage(ws, msg.data); break; default: - // 忽略未知消息类型 break; } };