From bcd55f9dac7ceb1b1c9c2f0c1fad808cc0443411 Mon Sep 17 00:00:00 2001
From: stary <834207172@qq.COM>
Date: Sat, 25 Apr 2026 19:26:39 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
WebApp/client/public/onebyone/index.html | 29 ----
WebApp/client/public/onebyone/renderer.js | 198 +++++++---------------
WebApp/client/public/onebyone/store.js | 31 +++-
3 files changed, 88 insertions(+), 170 deletions(-)
diff --git a/WebApp/client/public/onebyone/index.html b/WebApp/client/public/onebyone/index.html
index 1dad8eb..2a0c5a0 100644
--- a/WebApp/client/public/onebyone/index.html
+++ b/WebApp/client/public/onebyone/index.html
@@ -180,35 +180,6 @@
-
-
-
-
-

-
-
-
-
-
-
-
console.log('Auto-play prevented:', e.message));
+ } else {
+ video.srcObject = stream;
+ video.play().catch(e => console.log('Auto-play prevented:', e.message));
+ console.log(`Set remote stream for participant tile ${connectionId}`);
+ }
}
// 隐藏单路远端视频和占位符
@@ -546,91 +501,66 @@ class UIRenderer {
// 渲染Participant端的单一远端视频(Host画面)
renderSingleRemoteStream(stream) {
- if (this.elements.remoteVideo && stream) {
- console.log('Rendering remote stream:', stream);
-
- // 即使流对象相同,也要重新设置,确保视频元素能够识别轨道变化
- this.elements.remoteVideo.srcObject = null;
-
- // 延迟设置srcObject,确保视频元素能够正确处理
- setTimeout(() => {
- this.elements.remoteVideo.srcObject = stream;
- console.log('Remote stream reset successfully:', stream);
-
- // 确保视频元素的属性正确设置
- this.elements.remoteVideo.autoplay = true;
- this.elements.remoteVideo.playsinline = true;
- this.elements.remoteVideo.muted = false; // 不要静音远程视频,否则听不到对方的声音
-
- // 关键设置:启用硬件加速和最佳质量渲染
- this.elements.remoteVideo.style.transform = 'translateZ(0)'; // 启用硬件加速
- this.elements.remoteVideo.style.imageRendering = 'pixelated'; // 保持像素清晰
- this.elements.remoteVideo.style.objectFit = 'contain'; // 保持比例
-
- // 隐藏断开连接覆盖层
- if (this.elements.disconnectedOverlay) {
- this.elements.disconnectedOverlay.classList.add('hidden');
- }
-
-
-
- // 获取视频轨道并处理分辨率
- const videoTracks = stream.getVideoTracks();
- console.log('Remote video tracks:', videoTracks);
-
- // 检查是否有有效的视频轨道
- const hasValidVideoTrack = videoTracks.length > 0 && videoTracks.some(track => {
- // 检查轨道是否已停止或被禁用
- return track.readyState === 'live';
- });
-
- console.log('Has valid video track:', hasValidVideoTrack);
-
- if (hasValidVideoTrack) {
- console.log('Found valid video tracks, updating resolution');
- const activeVideoTrack = videoTracks.find(track => track.readyState === 'live');
- if (activeVideoTrack) {
- const resolution = this.getVideoResolution(activeVideoTrack);
- this.adjustVideoSize(this.elements.remoteVideo, resolution);
-
- // 监听轨道变化,处理分辨率调整
- activeVideoTrack.addEventListener('resize', () => {
- const newResolution = this.getVideoResolution(activeVideoTrack);
- this.adjustVideoSize(this.elements.remoteVideo, newResolution);
- });
- }
- // 隐藏连接中提示
- if (this.elements.connectingOverlay) {
- this.elements.connectingOverlay.classList.add('hidden');
- }
-
- // 隐藏占位背景
- if (this.elements.remoteVideoPlaceholder) {
- this.elements.remoteVideoPlaceholder.classList.add('hidden');
- }
- } else {
- console.log('No valid video tracks in remote stream');
- // 清空视频元素的源
- this.elements.remoteVideo.srcObject = null;
-
- // 显示占位背景
- if (this.elements.remoteVideoPlaceholder) {
- this.elements.remoteVideoPlaceholder.classList.remove('hidden');
- }
- }
- }, 50); // 增加延迟时间,确保视频元素有足够的时间处理
- } else {
+ if (!this.elements.remoteVideo || !stream) {
console.error('Either remoteVideo element or stream is missing');
+ return;
+ }
- // 清空视频元素的源
- if (this.elements.remoteVideo) {
- this.elements.remoteVideo.srcObject = null;
- }
+ console.log('Rendering remote stream:', stream, 'tracks:', stream.getTracks().map(t => `${t.kind}(${t.readyState})`));
- // 显示占位背景
+ // 关键修复:避免 srcObject = null 的重置模式
+ // 如果 srcObject 已经是同一个 stream 对象,说明是同一流的轨道更新(如音频先到,视频后到)
+ // 浏览器会自动识别新添加的轨道,无需重置 srcObject
+ if (this.elements.remoteVideo.srcObject === stream) {
+ console.log('Same stream object, track added - ensuring playback');
+ this.elements.remoteVideo.play().catch(e => console.log('Auto-play prevented:', e.message));
+ return;
+ }
+
+ // 首次设置或流对象变化:直接设置 srcObject(不使用 null 重置模式)
+ this.elements.remoteVideo.srcObject = stream;
+ this.elements.remoteVideo.autoplay = true;
+ this.elements.remoteVideo.playsinline = true;
+ this.elements.remoteVideo.muted = false;
+
+ // 确保视频开始播放
+ this.elements.remoteVideo.play().catch(e => {
+ console.log('Auto-play prevented, will retry on interaction:', e.message);
+ });
+
+ // 隐藏断开连接覆盖层
+ if (this.elements.disconnectedOverlay) {
+ this.elements.disconnectedOverlay.classList.add('hidden');
+ }
+
+ // 监听视频轨道变化
+ const videoTracks = stream.getVideoTracks();
+ const audioTracks = stream.getAudioTracks();
+ console.log(`Stream has ${videoTracks.length} video tracks, ${audioTracks.length} audio tracks`);
+
+ if (videoTracks.length > 0) {
+ // 有视频轨道:隐藏占位符
if (this.elements.remoteVideoPlaceholder) {
- this.elements.remoteVideoPlaceholder.classList.remove('hidden');
+ this.elements.remoteVideoPlaceholder.classList.add('hidden');
}
+ if (this.elements.connectingOverlay) {
+ this.elements.connectingOverlay.classList.add('hidden');
+ }
+
+ // 监听视频轨道分辨率变化
+ const activeVideoTrack = videoTracks.find(track => track.readyState === 'live');
+ if (activeVideoTrack) {
+ const resolution = this.getVideoResolution(activeVideoTrack);
+ this.adjustVideoSize(this.elements.remoteVideo, resolution);
+ activeVideoTrack.addEventListener('resize', () => {
+ const newResolution = this.getVideoResolution(activeVideoTrack);
+ this.adjustVideoSize(this.elements.remoteVideo, newResolution);
+ });
+ }
+ } else {
+ // 只有音频轨道(视频轨道尚未到达):不显示占位符,等待视频轨道到达
+ // 不设置 srcObject = null,保持音频播放
+ console.log('Audio-only stream, waiting for video track...');
}
}
diff --git a/WebApp/client/public/onebyone/store.js b/WebApp/client/public/onebyone/store.js
index b84dd3b..e1288b7 100644
--- a/WebApp/client/public/onebyone/store.js
+++ b/WebApp/client/public/onebyone/store.js
@@ -480,13 +480,30 @@ class CallStateManager {
}
// 通知UI远程流已更新
- this.notify({
- type: 'REMOTE_STREAM_OBTAINED',
- stream: targetStream,
- connectionId: trackParticipantId,
- isHost: isHost
- });
- console.log('Notified UI about remote stream update');
+ // 关键优化:如果是音频轨道先到达且流中尚无视频轨道,
+ // 延迟通知UI等待视频轨道到达,避免音频先触发的UI更新导致黑屏
+ const notifyStreamUpdate = () => {
+ this.notify({
+ type: 'REMOTE_STREAM_OBTAINED',
+ stream: targetStream,
+ connectionId: trackParticipantId,
+ isHost: isHost
+ });
+ console.log('Notified UI about remote stream update');
+ };
+
+ if (data.track.kind === 'audio' && targetStream.getVideoTracks().length === 0) {
+ // 音频先到,视频尚未到达:延迟200ms通知,给视频轨道到达的机会
+ console.log('Audio track arrived first, delaying stream notification for video track...');
+ setTimeout(() => {
+ const nowHasVideo = targetStream.getVideoTracks().length > 0;
+ console.log(`After delay, stream has video: ${nowHasVideo}`);
+ notifyStreamUpdate();
+ }, 200);
+ } else {
+ // 视频轨道到达,或音频视频同时存在:立即通知
+ notifyStreamUpdate();
+ }
// 只有当收到远程流时才更新远程用户状态为在线
if (this.state.session.remoteUser.status !== 'online') {
this.updateRemoteUserStatus('online');