From 957ab561bf97536af8c71f3dedcfd026af013f33 Mon Sep 17 00:00:00 2001
From: stary <834207172@qq.COM>
Date: Wed, 4 Mar 2026 22:29:10 +0800
Subject: [PATCH] =?UTF-8?q?=E3=80=90m=E3=80=91=E8=BF=9C=E7=AB=AF=E8=A7=86?=
=?UTF-8?q?=E9=A2=91=E5=BC=80=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
WebApp/client/public/onebyone/index.html | 11 +--
WebApp/client/public/onebyone/main.js | 20 ++---
WebApp/client/public/onebyone/renderer.js | 19 ++++
WebApp/client/public/onebyone/store.js | 102 ++++++++++++++--------
WebApp/src/class/httphandler.ts | 37 +++++++-
WebApp/src/class/websockethandler.ts | 19 +++-
WebApp/src/signaling.ts | 5 ++
7 files changed, 156 insertions(+), 57 deletions(-)
diff --git a/WebApp/client/public/onebyone/index.html b/WebApp/client/public/onebyone/index.html
index e81f48f..cf418b7 100644
--- a/WebApp/client/public/onebyone/index.html
+++ b/WebApp/client/public/onebyone/index.html
@@ -148,11 +148,12 @@
-

+
diff --git a/WebApp/client/public/onebyone/main.js b/WebApp/client/public/onebyone/main.js
index 361e183..823a698 100644
--- a/WebApp/client/public/onebyone/main.js
+++ b/WebApp/client/public/onebyone/main.js
@@ -317,13 +317,6 @@ function bindDomEvents() {
}
}
-/**
- * 初始化WebRTC
- */
-function initWebRTC() {
- // 这里可以添加WebRTC初始化代码
- console.log('Initializing WebRTC...');
-}
// 页面加载完成后初始化
window.addEventListener('DOMContentLoaded', async () => {
@@ -338,13 +331,16 @@ window.addEventListener('DOMContentLoaded', async () => {
}
// 初始化 store
- store.init();
-
- // 加入通话
- store.joinCall(connectionId);
+ //await store.init();
// 初始化渲染器
- const renderer = new UIRenderer(store);
+ new UIRenderer(store);
+
+ // 加入通话
+ await store.joinCall(connectionId);
+
+ // 设置WebRTC连接
+ await store.setUp(connectionId);
// 绑定DOM事件
bindDomEvents();
diff --git a/WebApp/client/public/onebyone/renderer.js b/WebApp/client/public/onebyone/renderer.js
index 56a5bfc..bcc0e54 100644
--- a/WebApp/client/public/onebyone/renderer.js
+++ b/WebApp/client/public/onebyone/renderer.js
@@ -89,6 +89,9 @@ class UIRenderer {
this.renderLocalStream(state.localStream);
this.renderLocalVideo(state.session.localUser, state.localStream);
break;
+ case 'REMOTE_STREAM_OBTAINED':
+ this.renderRemoteStream(state.remoteStream);
+ break;
case 'REMOTE_MEDIA_CHANGE':
this.renderRemoteVideo(state.session.remoteUser);
this.renderUserList(state.session.localUser, state.session.remoteUser);
@@ -189,6 +192,22 @@ class UIRenderer {
}
}
+ // 渲染远程视频流
+ renderRemoteStream(stream) {
+ if (this.elements.remoteVideo && stream) {
+ this.elements.remoteVideo.srcObject = stream;
+ this.elements.remoteVideo.autoplay = true;
+ console.log('Remote stream set successfully:', this.elements.remoteVideo.srcObject);
+
+ // 隐藏断开连接覆盖层
+ if (this.elements.disconnectedOverlay) {
+ this.elements.disconnectedOverlay.classList.add('hidden');
+ }
+ } else {
+ console.error('Either remoteVideo element or stream is missing');
+ }
+ }
+
// 渲染本地用户状态
renderLocalUserStatus(localUser) {
// 更新本地媒体状态文本
diff --git a/WebApp/client/public/onebyone/store.js b/WebApp/client/public/onebyone/store.js
index 8794365..46517f1 100644
--- a/WebApp/client/public/onebyone/store.js
+++ b/WebApp/client/public/onebyone/store.js
@@ -15,9 +15,9 @@ const defaultStreamHeight = 720;
class CallStateManager {
constructor() {
- let renderstreaming; // WebRTC连接管理实例
- let useWebSocket; // 是否使用WebSocket信令
- let connectionId; // 连接ID
+ const renderstreaming=null; // WebRTC连接管理实例
+ const useWebSocket=null; // 是否使用WebSocket信令
+ const connectionId=null; // 连接ID
// 核心状态
this.state = {
session: {
@@ -33,9 +33,6 @@ class CallStateManager {
// 监听器数组
this.listeners = [];
-
- // 初始化
- //this.init();
}
// 订阅状态变化
@@ -52,20 +49,19 @@ class CallStateManager {
}
// 初始化
- init() {
+ async init() {
// 启动通话时长计时器
this.durationInterval = setInterval(() => {
this.state.session.duration++;
this.notify({ type: 'DURATION_UPDATE', duration: this.state.session.duration });
}, 1000);
// 初始化配置
- this.setupConfig();
+ await this.setupConfig();
// 获取本地摄像头视频流
- this.getLocalStream();
-
+ await this.getLocalStream();
// 模拟远端音频活动 (实际应由 WebRTC VAD 检测触发)
- this.simulateRemoteActivity();
+ //this.simulateRemoteActivity();
// 模拟网络质量变化
this.simulateNetworkChange();
@@ -176,26 +172,46 @@ class CallStateManager {
async setUp(connectionId) {
//TODO
this.connectionId = connectionId; // 获取连接ID
- codecPreferences.disabled = true; // 禁用编解码器选择
+
+ // 确保本地流已经初始化
+ if (!this.state.localStream) {
+ console.log('Local stream not available, waiting for initialization...');
+ // 等待localStream初始化
+ await new Promise((resolve) => {
+ const checkStream = () => {
+ if (this.state.localStream) {
+ resolve();
+ } else {
+ setTimeout(checkStream, 100);
+ }
+ };
+ checkStream();
+ });
+ }
// 创建信令实例
- const signaling = useWebSocket ? new WebSocketSignaling() : new Signaling();
+ const signaling = this.useWebSocket ? new WebSocketSignaling() : new Signaling();
const config = getRTCConfiguration(); // 获取RTC配置
this.renderstreaming = new RenderStreaming(signaling, config); // 创建WebRTC连接管理实例
// 连接建立回调
this.renderstreaming.onConnect = () => {
- const tracks = this.state.localStream.getTracks(); // 获取本地媒体轨道
- for (const track of tracks) {
- this.renderstreaming.addTransceiver(track, { direction: 'sendonly' }); // 添加发送轨道
+ if (this.state.localStream) {
+ const tracks = this.state.localStream.getTracks(); // 获取本地媒体轨道
+ for (const track of tracks) {
+ this.renderstreaming.addTransceiver(track, { direction: 'sendonly' }); // 添加发送轨道
+ }
+ this.setCodecPreferences(); // 设置编解码器偏好
+ this.showStatsMessage(); // 显示统计信息
+ } else {
+ console.error('Local stream is not available');
+ showNotification('本地视频流不可用', 'error');
}
- setCodecPreferences(); // 设置编解码器偏好
- showStatsMessage(); // 显示统计信息
};
// 连接断开回调
this.renderstreaming.onDisconnect = () => {
- hangUp(); // 挂断连接
+ this.hangUp(); // 挂断连接
};
// 轨道事件回调
@@ -206,6 +222,8 @@ class CallStateManager {
this.state.remoteStream = new MediaStream();
}
this.state.remoteStream.addTrack(data.track);
+ // 通知UI远程流已更新
+ this.notify({ type: 'REMOTE_STREAM_OBTAINED', stream: this.state.remoteStream });
}
};
@@ -221,21 +239,21 @@ class CallStateManager {
* @returns {Promise}
*/
async hangUp() {
- clearStatsMessage(); // 清除统计信息
- messageDiv.style.display = 'block';
- messageDiv.innerText = `Disconnect peer on ${connectionId}.`;
+ this.clearStatsMessage(); // 清除统计信息
+ this.messageDiv.style.display = 'block';
+ this.messageDiv.innerText = `Disconnect peer on ${this.connectionId}.`;
// 删除连接并停止WebRTC
- await renderstreaming.deleteConnection();
- await renderstreaming.stop();
- renderstreaming = null;
- remoteVideo.srcObject = null; // 清除远程视频源
+ await this.renderstreaming.deleteConnection();
+ await this.renderstreaming.stop();
+ this.renderstreaming = null;
+ this.remoteVideo.srcObject = null; // 清除远程视频源
- connectionId = null;
+ this.connectionId = null;
// 启用编解码器选择
- if (supportsSetCodecPreferences) {
- codecPreferences.disabled = false;
+ if (this.supportsSetCodecPreferences) {
+ this.codecPreferences.disabled = false;
}
}
/**
@@ -245,8 +263,8 @@ class CallStateManager {
/** @type {RTCRtpCodecCapability[] | null} */
let selectedCodecs = null;
- if (supportsSetCodecPreferences) {
- const preferredCodec = codecPreferences.options[codecPreferences.selectedIndex];
+ if (this.supportsSetCodecPreferences) {
+ const preferredCodec = this.codecPreferences.options[this.codecPreferences.selectedIndex];
if (preferredCodec.value !== '') {
const [mimeType, sdpFmtpLine] = preferredCodec.value.split(' ');
const { codecs } = RTCRtpSender.getCapabilities('video');
@@ -261,7 +279,7 @@ class CallStateManager {
}
// 获取视频收发器并设置编解码器偏好
- const transceivers = renderstreaming.getTransceivers().filter(t => t.receiver.track.kind == "video");
+ const transceivers = this.renderstreaming.getTransceivers().filter(t => t.receiver.track.kind == "video");
if (transceivers && transceivers.length > 0) {
transceivers.forEach(t => t.setCodecPreferences(selectedCodecs));
}
@@ -310,26 +328,26 @@ class CallStateManager {
}
// 加入通话
- joinCall(connectionId) {
+ async joinCall(connectionId) {
this.state.session.status = 'connecting';
this.notify({ type: 'CALL_STATUS_CHANGE', status: 'connecting' });
showNotification(`正在加入通话 (${connectionId})`);
// 初始化
- this.init();
+ await this.init();
// 保存连接ID
this.connectionId = connectionId;
}
// 创建通话
- createCall() {
+ async createCall() {
this.state.session.status = 'connecting';
this.notify({ type: 'CALL_STATUS_CHANGE', status: 'connecting' });
showNotification('正在创建通话...');
// 初始化
- this.init();
+ await this.init();
}
// 模拟远端活动 (开发测试用)
@@ -364,6 +382,18 @@ class CallStateManager {
// socket.emit('media-state-changed', payload);
}
+ // 显示统计信息
+ showStatsMessage() {
+ console.log('Showing stats message');
+ // 这里可以添加显示统计信息的逻辑
+ }
+
+ // 清除统计信息
+ clearStatsMessage() {
+ console.log('Clearing stats message');
+ // 这里可以添加清除统计信息的逻辑
+ }
+
// Getters
getState() { return this.state; }
getLocalUser() { return this.state.session.localUser; }
diff --git a/WebApp/src/class/httphandler.ts b/WebApp/src/class/httphandler.ts
index 04897ac..c5b65b0 100644
--- a/WebApp/src/class/httphandler.ts
+++ b/WebApp/src/class/httphandler.ts
@@ -7,7 +7,7 @@ import Offer from './offer';
import Answer from './answer';
import Candidate from './candidate';
import { v4 as uuid } from 'uuid';
-
+import { onGetAllConnectionIds } from './websockethandler';
/**
* 断开连接记录类
* 用于记录断开连接的信息
@@ -1073,6 +1073,38 @@ function onGetConnections(req: Request, res: Response): void {
res.json({ rooms: rooms, totalRooms: rooms.length });
}
+
+/**
+ * @swagger
+ * /signaling/connection-ids:
+ * get:
+ * summary: 获取所有连接ID
+ * description: 获取所有当前活跃的连接ID
+ * security:
+ * - sessionAuth: []
+ * responses:
+ * 200:
+ * description: 成功获取连接ID列表
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * connectionIds:
+ * type: array
+ * items:
+ * type: string
+ * description: 连接ID
+ * totalCount:
+ * type: number
+ * description: 总连接数
+ */
+function getAllConnectionIds(req: Request, res: Response): void {
+ // 获取所有连接ID
+ const connectionIds = onGetAllConnectionIds();
+ // 返回JSON响应,包含连接ID列表和总数量
+ res.json({ connectionIds: connectionIds, totalCount: connectionIds.length });
+}
/**
* 导出HTTP处理器函数
*/
@@ -1091,5 +1123,6 @@ export {
postOffer, // 处理offer信令消息
postAnswer, // 处理answer信令消息
postCandidate, // 处理candidate信令消息
- onGetConnections // 获取房间和用户信息
+ onGetConnections, // 获取房间和用户信息
+ getAllConnectionIds // 获取所有连接ID
};
diff --git a/WebApp/src/class/websockethandler.ts b/WebApp/src/class/websockethandler.ts
index fb55ff4..5ffc5dc 100644
--- a/WebApp/src/class/websockethandler.ts
+++ b/WebApp/src/class/websockethandler.ts
@@ -60,7 +60,7 @@ function reset(mode: string): void {
*/
function add(ws: WebSocket): void {
// 为新连接创建空的连接ID集合
- var id = new Set();
+ const id = new Set();
clients.set(ws, id);
// 记录添加WebSocket连接的日志
console.log(`Add WebSocket: ${id}`);
@@ -347,7 +347,22 @@ function onCandidate(ws: WebSocket, message: any): void {
clearInterval((ws as any).heartbeatTimer);
}
}
+ /**
+ * 处理获取所有连接ID的请求
+ * @param ws WebSocket连接实例
+ */
+ function onGetAllConnectionIds(): string[] {
+ // 获取所有connectionId
+ const connectionIds = Array.from(connectionPair.keys());
+ // 发送连接ID列表给客户端
+ // ws.send(JSON.stringify({
+ // type: "connection-ids",
+ // connectionIds: connectionIds
+ // }));
+ return connectionIds;
+ }
+
/**
* 导出WebSocket处理器函数
*/
- export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate,onCallConnectionId, onBroadcast, AddHeartbeat, RemoveHeartbeat };
+ export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId, onBroadcast, onGetAllConnectionIds, AddHeartbeat, RemoveHeartbeat };
diff --git a/WebApp/src/signaling.ts b/WebApp/src/signaling.ts
index 636ed47..c523e82 100644
--- a/WebApp/src/signaling.ts
+++ b/WebApp/src/signaling.ts
@@ -2,6 +2,11 @@ import * as express from 'express';
import * as handler from'./class/httphandler';
const router: express.Router = express.Router();
+
+// 不需要会话ID的路由
+router.get('/connection-ids', handler.getAllConnectionIds);
+
+// 需要会话ID的路由
router.use(handler.checkSessionId);
router.get('/connection', handler.getConnection);
router.get('/offer', handler.getOffer);