Allow multiple signal servers (might need rollback)
All checks were successful
Queue Release Build / prepare (push) Successful in 17s
Deploy Web Apps / deploy (push) Successful in 9m58s
Queue Release Build / build-linux (push) Successful in 26m26s
Queue Release Build / build-windows (push) Successful in 25m3s
Queue Release Build / finalize (push) Successful in 1m43s

This commit is contained in:
2026-03-19 01:20:20 +01:00
parent c862c2fe03
commit c3ef8e8800
18 changed files with 1309 additions and 297 deletions

View File

@@ -19,7 +19,12 @@ import {
inject,
OnDestroy
} from '@angular/core';
import { Observable, Subject } from 'rxjs';
import {
Observable,
of,
Subject,
Subscription
} from 'rxjs';
import { v4 as uuidv4 } from 'uuid';
import { SignalingMessage, ChatEvent } from '../models/index';
import { TimeSyncService } from './time-sync.service';
@@ -88,8 +93,13 @@ export class WebRTCService implements OnDestroy {
private readonly logger = new WebRTCLogger(() => this.debugging.enabled());
private lastIdentifyCredentials: IdentifyCredentials | null = null;
private lastJoinedServer: JoinedServerInfo | null = null;
private readonly memberServerIds = new Set<string>();
private readonly lastJoinedServerBySignalUrl = new Map<string, JoinedServerInfo>();
private readonly memberServerIdsBySignalUrl = new Map<string, Set<string>>();
private readonly serverSignalingUrlMap = new Map<string, string>();
private readonly peerSignalingUrlMap = new Map<string, string>();
private readonly signalingManagers = new Map<string, SignalingManager>();
private readonly signalingSubscriptions = new Map<string, Subscription[]>();
private readonly signalingConnectionStates = new Map<string, boolean>();
private activeServerId: string | null = null;
/** The server ID where voice is currently active, or `null` when not in voice. */
private voiceServerId: string | null = null;
@@ -168,20 +178,12 @@ export class WebRTCService implements OnDestroy {
return this.mediaManager.voiceConnected$.asObservable();
}
private readonly signalingManager: SignalingManager;
private readonly peerManager: PeerConnectionManager;
private readonly mediaManager: MediaManager;
private readonly screenShareManager: ScreenShareManager;
constructor() {
// Create managers with null callbacks first to break circular initialization
this.signalingManager = new SignalingManager(
this.logger,
() => this.lastIdentifyCredentials,
() => this.lastJoinedServer,
() => this.memberServerIds
);
this.peerManager = new PeerConnectionManager(this.logger, null!);
this.mediaManager = new MediaManager(this.logger, null!);
@@ -190,7 +192,7 @@ export class WebRTCService implements OnDestroy {
// Now wire up cross-references (all managers are instantiated)
this.peerManager.setCallbacks({
sendRawMessage: (msg: Record<string, unknown>) => this.signalingManager.sendRawMessage(msg),
sendRawMessage: (msg: Record<string, unknown>) => this.sendRawMessage(msg),
getLocalMediaStream: (): MediaStream | null => this.mediaManager.getLocalStream(),
isSignalingConnected: (): boolean => this._isSignalingConnected(),
getVoiceStateSnapshot: (): VoiceStateSnapshot => this.getCurrentVoiceState(),
@@ -231,23 +233,6 @@ export class WebRTCService implements OnDestroy {
}
private wireManagerEvents(): void {
// Signaling → connection status
this.signalingManager.connectionStatus$.subscribe(({ connected, errorMessage }) => {
this._isSignalingConnected.set(connected);
if (connected)
this._hasEverConnected.set(true);
this._hasConnectionError.set(!connected);
this._connectionErrorMessage.set(connected ? null : (errorMessage ?? null));
});
// Signaling → message routing
this.signalingManager.messageReceived$.subscribe((msg) => this.handleSignalingMessage(msg));
// Signaling → heartbeat → broadcast states
this.signalingManager.heartbeatTick$.subscribe(() => this.peerManager.broadcastCurrentStates());
// Internal control-plane messages for on-demand screen-share delivery.
this.peerManager.messageReceived$.subscribe((event) => this.handlePeerControlMessage(event));
@@ -277,6 +262,7 @@ export class WebRTCService implements OnDestroy {
this.peerManager.peerDisconnected$.subscribe((peerId) => {
this.activeRemoteScreenSharePeers.delete(peerId);
this.peerServerMap.delete(peerId);
this.peerSignalingUrlMap.delete(peerId);
this.screenShareManager.clearScreenShareRequest(peerId);
});
@@ -293,37 +279,145 @@ export class WebRTCService implements OnDestroy {
});
}
private handleSignalingMessage(message: IncomingSignalingMessage): void {
private ensureSignalingManager(signalUrl: string): SignalingManager {
const existingManager = this.signalingManagers.get(signalUrl);
if (existingManager) {
return existingManager;
}
const manager = new SignalingManager(
this.logger,
() => this.lastIdentifyCredentials,
() => this.lastJoinedServerBySignalUrl.get(signalUrl) ?? null,
() => this.getMemberServerIdsForSignalUrl(signalUrl)
);
const subscriptions: Subscription[] = [
manager.connectionStatus$.subscribe(({ connected, errorMessage }) =>
this.handleSignalingConnectionStatus(signalUrl, connected, errorMessage)
),
manager.messageReceived$.subscribe((message) => this.handleSignalingMessage(message, signalUrl)),
manager.heartbeatTick$.subscribe(() => this.peerManager.broadcastCurrentStates())
];
this.signalingManagers.set(signalUrl, manager);
this.signalingSubscriptions.set(signalUrl, subscriptions);
return manager;
}
private handleSignalingConnectionStatus(
signalUrl: string,
connected: boolean,
errorMessage?: string
): void {
this.signalingConnectionStates.set(signalUrl, connected);
if (connected)
this._hasEverConnected.set(true);
const anyConnected = this.isAnySignalingConnected();
this._isSignalingConnected.set(anyConnected);
this._hasConnectionError.set(!anyConnected);
this._connectionErrorMessage.set(anyConnected ? null : (errorMessage ?? 'Disconnected from signaling server'));
}
private isAnySignalingConnected(): boolean {
for (const manager of this.signalingManagers.values()) {
if (manager.isSocketOpen()) {
return true;
}
}
return false;
}
private getConnectedSignalingManagers(): { signalUrl: string; manager: SignalingManager }[] {
const connectedManagers: { signalUrl: string; manager: SignalingManager }[] = [];
for (const [signalUrl, manager] of this.signalingManagers.entries()) {
if (!manager.isSocketOpen()) {
continue;
}
connectedManagers.push({ signalUrl,
manager });
}
return connectedManagers;
}
private getOrCreateMemberServerSet(signalUrl: string): Set<string> {
const existingSet = this.memberServerIdsBySignalUrl.get(signalUrl);
if (existingSet) {
return existingSet;
}
const createdSet = new Set<string>();
this.memberServerIdsBySignalUrl.set(signalUrl, createdSet);
return createdSet;
}
private getMemberServerIdsForSignalUrl(signalUrl: string): ReadonlySet<string> {
return this.memberServerIdsBySignalUrl.get(signalUrl) ?? new Set<string>();
}
private isJoinedServer(serverId: string): boolean {
for (const memberServerIds of this.memberServerIdsBySignalUrl.values()) {
if (memberServerIds.has(serverId)) {
return true;
}
}
return false;
}
private getJoinedServerCount(): number {
let joinedServerCount = 0;
for (const memberServerIds of this.memberServerIdsBySignalUrl.values()) {
joinedServerCount += memberServerIds.size;
}
return joinedServerCount;
}
private handleSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
this.signalingMessage$.next(message);
this.logger.info('Signaling message', { type: message.type });
this.logger.info('Signaling message', {
signalUrl,
type: message.type
});
switch (message.type) {
case SIGNALING_TYPE_CONNECTED:
this.handleConnectedSignalingMessage(message);
this.handleConnectedSignalingMessage(message, signalUrl);
return;
case SIGNALING_TYPE_SERVER_USERS:
this.handleServerUsersSignalingMessage(message);
this.handleServerUsersSignalingMessage(message, signalUrl);
return;
case SIGNALING_TYPE_USER_JOINED:
this.handleUserJoinedSignalingMessage(message);
this.handleUserJoinedSignalingMessage(message, signalUrl);
return;
case SIGNALING_TYPE_USER_LEFT:
this.handleUserLeftSignalingMessage(message);
this.handleUserLeftSignalingMessage(message, signalUrl);
return;
case SIGNALING_TYPE_OFFER:
this.handleOfferSignalingMessage(message);
this.handleOfferSignalingMessage(message, signalUrl);
return;
case SIGNALING_TYPE_ANSWER:
this.handleAnswerSignalingMessage(message);
this.handleAnswerSignalingMessage(message, signalUrl);
return;
case SIGNALING_TYPE_ICE_CANDIDATE:
this.handleIceCandidateSignalingMessage(message);
this.handleIceCandidateSignalingMessage(message, signalUrl);
return;
default:
@@ -331,26 +425,40 @@ export class WebRTCService implements OnDestroy {
}
}
private handleConnectedSignalingMessage(message: IncomingSignalingMessage): void {
this.logger.info('Server connected', { oderId: message.oderId });
private handleConnectedSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
this.logger.info('Server connected', {
oderId: message.oderId,
signalUrl
});
if (message.serverId) {
this.serverSignalingUrlMap.set(message.serverId, signalUrl);
}
if (typeof message.serverTime === 'number') {
this.timeSync.setFromServerTime(message.serverTime);
}
}
private handleServerUsersSignalingMessage(message: IncomingSignalingMessage): void {
private handleServerUsersSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
const users = Array.isArray(message.users) ? message.users : [];
this.logger.info('Server users', {
count: users.length,
signalUrl,
serverId: message.serverId
});
if (message.serverId) {
this.serverSignalingUrlMap.set(message.serverId, signalUrl);
}
for (const user of users) {
if (!user.oderId)
continue;
this.peerSignalingUrlMap.set(user.oderId, signalUrl);
if (message.serverId) {
this.trackPeerInServer(user.oderId, message.serverId);
}
@@ -376,21 +484,31 @@ export class WebRTCService implements OnDestroy {
}
}
private handleUserJoinedSignalingMessage(message: IncomingSignalingMessage): void {
private handleUserJoinedSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
this.logger.info('User joined', {
displayName: message.displayName,
oderId: message.oderId
oderId: message.oderId,
signalUrl
});
if (message.serverId) {
this.serverSignalingUrlMap.set(message.serverId, signalUrl);
}
if (message.oderId) {
this.peerSignalingUrlMap.set(message.oderId, signalUrl);
}
if (message.oderId && message.serverId) {
this.trackPeerInServer(message.oderId, message.serverId);
}
}
private handleUserLeftSignalingMessage(message: IncomingSignalingMessage): void {
private handleUserLeftSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
this.logger.info('User left', {
displayName: message.displayName,
oderId: message.oderId,
signalUrl,
serverId: message.serverId
});
@@ -404,17 +522,20 @@ export class WebRTCService implements OnDestroy {
if (!hasRemainingSharedServers) {
this.peerManager.removePeer(message.oderId);
this.peerServerMap.delete(message.oderId);
this.peerSignalingUrlMap.delete(message.oderId);
}
}
}
private handleOfferSignalingMessage(message: IncomingSignalingMessage): void {
private handleOfferSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
const fromUserId = message.fromUserId;
const sdp = message.payload?.sdp;
if (!fromUserId || !sdp)
return;
this.peerSignalingUrlMap.set(fromUserId, signalUrl);
const offerEffectiveServer = this.voiceServerId || this.activeServerId;
if (offerEffectiveServer && !this.peerServerMap.has(fromUserId)) {
@@ -424,23 +545,27 @@ export class WebRTCService implements OnDestroy {
this.peerManager.handleOffer(fromUserId, sdp);
}
private handleAnswerSignalingMessage(message: IncomingSignalingMessage): void {
private handleAnswerSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
const fromUserId = message.fromUserId;
const sdp = message.payload?.sdp;
if (!fromUserId || !sdp)
return;
this.peerSignalingUrlMap.set(fromUserId, signalUrl);
this.peerManager.handleAnswer(fromUserId, sdp);
}
private handleIceCandidateSignalingMessage(message: IncomingSignalingMessage): void {
private handleIceCandidateSignalingMessage(message: IncomingSignalingMessage, signalUrl: string): void {
const fromUserId = message.fromUserId;
const candidate = message.payload?.candidate;
if (!fromUserId || !candidate)
return;
this.peerSignalingUrlMap.set(fromUserId, signalUrl);
this.peerManager.handleIceCandidate(fromUserId, candidate);
}
@@ -467,6 +592,7 @@ export class WebRTCService implements OnDestroy {
this.peerManager.removePeer(peerId);
this.peerServerMap.delete(peerId);
this.peerSignalingUrlMap.delete(peerId);
}
}
@@ -490,7 +616,18 @@ export class WebRTCService implements OnDestroy {
* @returns An observable that emits `true` once connected.
*/
connectToSignalingServer(serverUrl: string): Observable<boolean> {
return this.signalingManager.connect(serverUrl);
const manager = this.ensureSignalingManager(serverUrl);
if (manager.isSocketOpen()) {
return of(true);
}
return manager.connect(serverUrl);
}
/** Returns true when the signaling socket for a given URL is currently open. */
isSignalingConnectedTo(serverUrl: string): boolean {
return this.signalingManagers.get(serverUrl)?.isSocketOpen() ?? false;
}
private trackPeerInServer(peerId: string, serverId: string): void {
@@ -504,7 +641,7 @@ export class WebRTCService implements OnDestroy {
}
private replacePeerSharedServers(peerId: string, serverIds: string[]): boolean {
const sharedServerIds = serverIds.filter((serverId) => this.memberServerIds.has(serverId));
const sharedServerIds = serverIds.filter((serverId) => this.isJoinedServer(serverId));
if (sharedServerIds.length === 0) {
this.peerServerMap.delete(peerId);
@@ -539,7 +676,17 @@ export class WebRTCService implements OnDestroy {
* @returns `true` if connected within the timeout.
*/
async ensureSignalingConnected(timeoutMs?: number): Promise<boolean> {
return this.signalingManager.ensureConnected(timeoutMs);
if (this.isAnySignalingConnected()) {
return true;
}
for (const manager of this.signalingManagers.values()) {
if (await manager.ensureConnected(timeoutMs)) {
return true;
}
}
return false;
}
/**
@@ -548,7 +695,32 @@ export class WebRTCService implements OnDestroy {
* @param message - The signaling message payload (excluding `from` / `timestamp`).
*/
sendSignalingMessage(message: Omit<SignalingMessage, 'from' | 'timestamp'>): void {
this.signalingManager.sendSignalingMessage(message, this._localPeerId());
const targetPeerId = message.to;
if (targetPeerId) {
const targetSignalUrl = this.peerSignalingUrlMap.get(targetPeerId);
if (targetSignalUrl) {
const targetManager = this.ensureSignalingManager(targetSignalUrl);
targetManager.sendSignalingMessage(message, this._localPeerId());
return;
}
}
const connectedManagers = this.getConnectedSignalingManagers();
if (connectedManagers.length === 0) {
this.logger.error('[signaling] No active signaling connection for outbound message', new Error('No signaling manager available'), {
type: message.type
});
return;
}
for (const { manager } of connectedManagers) {
manager.sendSignalingMessage(message, this._localPeerId());
}
}
/**
@@ -557,7 +729,50 @@ export class WebRTCService implements OnDestroy {
* @param message - Arbitrary JSON message.
*/
sendRawMessage(message: Record<string, unknown>): void {
this.signalingManager.sendRawMessage(message);
const targetPeerId = typeof message['targetUserId'] === 'string' ? message['targetUserId'] : null;
if (targetPeerId) {
const targetSignalUrl = this.peerSignalingUrlMap.get(targetPeerId);
if (targetSignalUrl && this.sendRawMessageToSignalUrl(targetSignalUrl, message)) {
return;
}
}
const serverId = typeof message['serverId'] === 'string' ? message['serverId'] : null;
if (serverId) {
const serverSignalUrl = this.serverSignalingUrlMap.get(serverId);
if (serverSignalUrl && this.sendRawMessageToSignalUrl(serverSignalUrl, message)) {
return;
}
}
const connectedManagers = this.getConnectedSignalingManagers();
if (connectedManagers.length === 0) {
this.logger.error('[signaling] No active signaling connection for outbound message', new Error('No signaling manager available'), {
type: typeof message['type'] === 'string' ? message['type'] : 'unknown'
});
return;
}
for (const { manager } of connectedManagers) {
manager.sendRawMessage(message);
}
}
private sendRawMessageToSignalUrl(signalUrl: string, message: Record<string, unknown>): boolean {
const manager = this.signalingManagers.get(signalUrl);
if (!manager) {
return false;
}
manager.sendRawMessage(message);
return true;
}
/**
@@ -576,7 +791,15 @@ export class WebRTCService implements OnDestroy {
/** The last signaling URL used by the client, if any. */
getCurrentSignalingUrl(): string | null {
return this.signalingManager.getLastUrl();
if (this.activeServerId) {
const activeServerSignalUrl = this.serverSignalingUrlMap.get(this.activeServerId);
if (activeServerSignalUrl) {
return activeServerSignalUrl;
}
}
return this.getConnectedSignalingManagers()[0]?.signalUrl ?? null;
}
/**
@@ -587,13 +810,22 @@ export class WebRTCService implements OnDestroy {
* @param oderId - The user's unique order/peer ID.
* @param displayName - The user's display name.
*/
identify(oderId: string, displayName: string): void {
identify(oderId: string, displayName: string, signalUrl?: string): void {
this.lastIdentifyCredentials = { oderId,
displayName };
this.sendRawMessage({ type: SIGNALING_TYPE_IDENTIFY,
const identifyMessage = {
type: SIGNALING_TYPE_IDENTIFY,
oderId,
displayName });
displayName
};
if (signalUrl) {
this.sendRawMessageToSignalUrl(signalUrl, identifyMessage);
return;
}
this.sendRawMessage(identifyMessage);
}
/**
@@ -602,13 +834,27 @@ export class WebRTCService implements OnDestroy {
* @param roomId - The server / room ID to join.
* @param userId - The local user ID.
*/
joinRoom(roomId: string, userId: string): void {
this.lastJoinedServer = { serverId: roomId,
userId };
joinRoom(roomId: string, userId: string, signalUrl?: string): void {
const resolvedSignalUrl = signalUrl
?? this.serverSignalingUrlMap.get(roomId)
?? this.getCurrentSignalingUrl();
this.memberServerIds.add(roomId);
this.sendRawMessage({ type: SIGNALING_TYPE_JOIN_SERVER,
serverId: roomId });
if (!resolvedSignalUrl) {
this.logger.warn('[signaling] Cannot join room without a signaling URL', { roomId });
return;
}
this.serverSignalingUrlMap.set(roomId, resolvedSignalUrl);
this.lastJoinedServerBySignalUrl.set(resolvedSignalUrl, {
serverId: roomId,
userId
});
this.getOrCreateMemberServerSet(resolvedSignalUrl).add(roomId);
this.sendRawMessageToSignalUrl(resolvedSignalUrl, {
type: SIGNALING_TYPE_JOIN_SERVER,
serverId: roomId
});
}
/**
@@ -618,26 +864,46 @@ export class WebRTCService implements OnDestroy {
* @param serverId - The target server ID.
* @param userId - The local user ID.
*/
switchServer(serverId: string, userId: string): void {
this.lastJoinedServer = { serverId,
userId };
switchServer(serverId: string, userId: string, signalUrl?: string): void {
const resolvedSignalUrl = signalUrl
?? this.serverSignalingUrlMap.get(serverId)
?? this.getCurrentSignalingUrl();
if (this.memberServerIds.has(serverId)) {
this.sendRawMessage({ type: SIGNALING_TYPE_VIEW_SERVER,
serverId });
if (!resolvedSignalUrl) {
this.logger.warn('[signaling] Cannot switch server without a signaling URL', { serverId });
return;
}
this.serverSignalingUrlMap.set(serverId, resolvedSignalUrl);
this.lastJoinedServerBySignalUrl.set(resolvedSignalUrl, {
serverId,
userId
});
const memberServerIds = this.getOrCreateMemberServerSet(resolvedSignalUrl);
if (memberServerIds.has(serverId)) {
this.sendRawMessageToSignalUrl(resolvedSignalUrl, {
type: SIGNALING_TYPE_VIEW_SERVER,
serverId
});
this.logger.info('Viewed server (already joined)', {
serverId,
signalUrl: resolvedSignalUrl,
userId,
voiceConnected: this._isVoiceConnected()
});
} else {
this.memberServerIds.add(serverId);
this.sendRawMessage({ type: SIGNALING_TYPE_JOIN_SERVER,
serverId });
memberServerIds.add(serverId);
this.sendRawMessageToSignalUrl(resolvedSignalUrl, {
type: SIGNALING_TYPE_JOIN_SERVER,
serverId
});
this.logger.info('Joined new server via switch', {
serverId,
signalUrl: resolvedSignalUrl,
userId,
voiceConnected: this._isVoiceConnected()
});
@@ -654,25 +920,47 @@ export class WebRTCService implements OnDestroy {
*/
leaveRoom(serverId?: string): void {
if (serverId) {
this.memberServerIds.delete(serverId);
this.sendRawMessage({ type: SIGNALING_TYPE_LEAVE_SERVER,
serverId });
const resolvedSignalUrl = this.serverSignalingUrlMap.get(serverId);
if (resolvedSignalUrl) {
this.getOrCreateMemberServerSet(resolvedSignalUrl).delete(serverId);
this.sendRawMessageToSignalUrl(resolvedSignalUrl, {
type: SIGNALING_TYPE_LEAVE_SERVER,
serverId
});
} else {
this.sendRawMessage({
type: SIGNALING_TYPE_LEAVE_SERVER,
serverId
});
for (const memberServerIds of this.memberServerIdsBySignalUrl.values()) {
memberServerIds.delete(serverId);
}
}
this.serverSignalingUrlMap.delete(serverId);
this.logger.info('Left server', { serverId });
if (this.memberServerIds.size === 0) {
if (this.getJoinedServerCount() === 0) {
this.fullCleanup();
}
return;
}
this.memberServerIds.forEach((sid) => {
this.sendRawMessage({ type: SIGNALING_TYPE_LEAVE_SERVER,
serverId: sid });
});
for (const [signalUrl, memberServerIds] of this.memberServerIdsBySignalUrl.entries()) {
for (const sid of memberServerIds) {
this.sendRawMessageToSignalUrl(signalUrl, {
type: SIGNALING_TYPE_LEAVE_SERVER,
serverId: sid
});
}
}
this.memberServerIds.clear();
this.memberServerIdsBySignalUrl.clear();
this.serverSignalingUrlMap.clear();
this.fullCleanup();
}
@@ -682,12 +970,18 @@ export class WebRTCService implements OnDestroy {
* @param serverId - The server to check.
*/
hasJoinedServer(serverId: string): boolean {
return this.memberServerIds.has(serverId);
return this.isJoinedServer(serverId);
}
/** Returns a read-only set of all currently-joined server IDs. */
getJoinedServerIds(): ReadonlySet<string> {
return this.memberServerIds;
const joinedServerIds = new Set<string>();
for (const memberServerIds of this.memberServerIdsBySignalUrl.values()) {
memberServerIds.forEach((serverId) => joinedServerIds.add(serverId));
}
return joinedServerIds;
}
/**
@@ -942,11 +1236,15 @@ export class WebRTCService implements OnDestroy {
/** Disconnect from the signaling server and clean up all state. */
disconnect(): void {
this.leaveRoom();
this.voiceServerId = null;
this.peerServerMap.clear();
this.leaveRoom();
this.peerSignalingUrlMap.clear();
this.lastJoinedServerBySignalUrl.clear();
this.memberServerIdsBySignalUrl.clear();
this.serverSignalingUrlMap.clear();
this.mediaManager.stopVoiceHeartbeat();
this.signalingManager.close();
this.destroyAllSignalingManagers();
this._isSignalingConnected.set(false);
this._hasEverConnected.set(false);
this._hasConnectionError.set(false);
@@ -962,6 +1260,7 @@ export class WebRTCService implements OnDestroy {
private fullCleanup(): void {
this.voiceServerId = null;
this.peerServerMap.clear();
this.peerSignalingUrlMap.clear();
this.remoteScreenShareRequestsEnabled = false;
this.desiredRemoteScreenSharePeers.clear();
this.activeRemoteScreenSharePeers.clear();
@@ -1040,10 +1339,25 @@ export class WebRTCService implements OnDestroy {
}
}
private destroyAllSignalingManagers(): void {
for (const subscriptions of this.signalingSubscriptions.values()) {
for (const subscription of subscriptions) {
subscription.unsubscribe();
}
}
for (const manager of this.signalingManagers.values()) {
manager.destroy();
}
this.signalingSubscriptions.clear();
this.signalingManagers.clear();
this.signalingConnectionStates.clear();
}
ngOnDestroy(): void {
this.disconnect();
this.serviceDestroyed$.complete();
this.signalingManager.destroy();
this.peerManager.destroy();
this.mediaManager.destroy();
this.screenShareManager.destroy();