【m】增加多用户连接

This commit is contained in:
2026-04-23 15:22:24 +08:00
parent cb32582c98
commit 852a169c30
8 changed files with 797 additions and 155 deletions

View File

@@ -174,6 +174,10 @@ class UIRenderer {
// 通话结束 - 渲染通话结束界面
this.renderCallEnded();
break;
case 'PARTICIPANT_LEFT':
// participant离开 - 更新UI但房间仍然存在
this.renderParticipantLeft(changes.connectionId);
break;
}
}
@@ -892,6 +896,19 @@ class UIRenderer {
window.location.href = './endcall/endcall.html';
}
// 渲染participant离开host端房间仍然存在
renderParticipantLeft(connectionId) {
console.log(`Participant left: ${connectionId}, updating UI`);
// 更新远程用户状态显示为离线
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');
}
}
// 获取状态文本
getStatusText(status) {
const statusMap = {

View File

@@ -8,15 +8,9 @@ import { RenderStreaming } from "../../module/renderstreaming.js"; // WebRTC连
import { getServerConfig, getRTCConfiguration } from "../js/config.js";//服务器配置和RTC配置
import { showNotification, generateId } from './utils.js'; // 导入通知函数
import chatMessage from './chatmessage.js';
// 默认视频流尺寸
const defaultStreamWidth = 1280;
const defaultStreamHeight = 720;
class CallStateManager {
constructor() {
const renderstreaming = null; // WebRTC连接管理实例
const useWebSocket = null; // 是否使用WebSocket信令
const connectionId = null; // 连接ID
// 核心状态
this.state = {
id: generateId(),
@@ -332,9 +326,23 @@ class CallStateManager {
]
};
this.renderstreaming = new RenderStreaming(signaling, config); // 创建WebRTC连接管理实例
this.renderstreaming.onNewPeer = (connectionId) => {
console.log(`New peer created for ${connectionId}, adding local tracks`);
if (this.state.localStream) {
const tracks = this.state.localStream.getTracks();
for (const track of tracks) {
this.renderstreaming.addTransceiver(track, { direction: 'sendonly' });
}
this.setCodecPreferences();
}
};
// 连接建立回调
this.renderstreaming.onConnect = () => {
this.renderstreaming.onConnect = (connectionId, data) => {
// 保存角色信息host/participant
if (data && data.role) {
this.role = data.role;
console.log(`Connected as ${this.role}`);
}
// 连接建立后更新状态为ongoing
this.state.session.status = 'ongoing';
this.notify({ type: 'CALL_STATUS_CHANGE', status: 'ongoing' });
@@ -347,21 +355,39 @@ class CallStateManager {
});
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(); // 显示统计信息
// 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');
}
};
// 连接断开回调
// 连接断开回调(收到服务器的 disconnect 消息,通常是 host 离开导致房间关闭)
this.renderstreaming.onDisconnect = () => {
this.hangUp(); // 挂断连接
console.log('Received disconnect from server, host left or room closed');
this.hangUp(); // 房间已关闭,挂断连接
};
// participant离开回调host收到房间仍然存在
this.renderstreaming.onParticipantLeft = (connectionId) => {
console.log(`Participant left: ${connectionId}, room still active`);
// 更新远程用户状态,但不关闭房间
this.updateRemoteUserStatus('offline');
this.updateRemoteUserNetworkQuality('no_signal');
showNotification('对方已离开通话', 'warning');
// 清理远端流重置Peer连接为新participant加入做准备
if (this.state.remoteStream) {
this.state.remoteStream.getTracks().forEach(track => track.stop());
this.state.remoteStream = null;
}
this.notify({ type: 'REMOTE_STREAM_OBTAINED', stream: null });
// 通知UI更新
this.notify({ type: 'PARTICIPANT_LEFT', connectionId: connectionId });
};
// 轨道事件回调
@@ -473,6 +499,8 @@ class CallStateManager {
/**
* 挂断WebRTC连接
* Host挂断房间删除通知所有participants
* Participant挂断仅自己离开房间保留
* @async
* @returns {Promise<void>}
*/
@@ -484,11 +512,17 @@ class CallStateManager {
clearInterval(this.durationInterval);
this.durationInterval = null;
}
console.log(`Disconnect peer on ${this.connectionId}.`);
const isHost = this.role === 'host';
console.log(`Disconnect peer on ${this.connectionId}. Role: ${this.role}`);
// 删除连接并停止WebRTC
if (this.renderstreaming) {
try {
// 发送断开连接信令给服务器
// 服务器会根据角色决定:
// - host断开通知所有participants删除房间
// - participant断开仅通知host保留房间
await this.renderstreaming.deleteConnection();
await this.renderstreaming.stop();
} catch (error) {
@@ -501,8 +535,9 @@ class CallStateManager {
this.updateRemoteUserStatus('offline');
this.updateRemoteUserNetworkQuality('no_signal');
this.connectionId = null;
this.role = null;
this.state.session.status = 'ended';
this.notify({ type: 'CALL_ENDED' });
this.notify({ type: 'CALL_ENDED', reason: isHost ? 'host_hangup' : 'participant_hangup' });
}
/**
@@ -570,18 +605,14 @@ class CallStateManager {
this.state.session.remoteUser.networkQuality = networkQuality;
this.notify({ type: 'REMOTE_MEDIA_CHANGE', localUser: this.state.session.localUser, remoteUser: this.state.session.remoteUser });
}
// 结束通话
endCall() {
if (this.durationInterval) {
clearInterval(this.durationInterval);
this.durationInterval = null;
}
this.state.session.status = 'ended';
this.notify({ type: 'CALL_ENDED' });
// 发送结束通话请求到服务器
// [API_CALL: POST /api/call/:callId/leave]
// [WEBSOCKET_EMIT: leave-call]
// 结束通话(用户主动点击挂断按钮)
async endCall() {
console.log(`endCall called. Role: ${this.role}`);
// 调用 hangUp() 正确关闭 WebRTC 连接并发送断开信令
// hangUp 内部会根据角色区分:
// - host: 通知所有participants删除房间
// - participant: 仅自己离开,房间保留
await this.hangUp();
}
// 加入通话