import type { ISignalingMessage } from '../interfaces/IWebRTCClient.ts'; export class PublisherRTCManager { private peerConnections: Map = new Map(); private localStream: MediaStream | null = null; private onSubscriberCountChange?: (count: number) => void; private rtcConfiguration: RTCConfiguration = { iceServers: [ { urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' } ] }; constructor(onSubscriberCountChange?: (count: number) => void) { this.onSubscriberCountChange = onSubscriberCountChange; } setLocalStream(stream: MediaStream): void { this.localStream = stream; } async createOfferForSubscriber(subscriberId: string): Promise { const peerConnection = this.createPeerConnection(subscriberId); if (this.localStream) { this.localStream.getTracks().forEach(track => { peerConnection.addTrack(track, this.localStream!); }); } const offer = await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); return offer; } async handleAnswer(subscriberId: string, answer: RTCSessionDescriptionInit): Promise { const peerConnection = this.peerConnections.get(subscriberId); if (peerConnection) { await peerConnection.setRemoteDescription(answer); } } async handleIceCandidate(subscriberId: string, candidate: RTCIceCandidateInit): Promise { const peerConnection = this.peerConnections.get(subscriberId); if (peerConnection && candidate) { await peerConnection.addIceCandidate(candidate); } } removeSubscriber(subscriberId: string): void { const peerConnection = this.peerConnections.get(subscriberId); if (peerConnection) { peerConnection.close(); this.peerConnections.delete(subscriberId); this.updateSubscriberCount(); } } closeAllConnections(): void { this.peerConnections.forEach((pc, id) => { pc.close(); }); this.peerConnections.clear(); this.updateSubscriberCount(); } private createPeerConnection(subscriberId: string): RTCPeerConnection { const peerConnection = new RTCPeerConnection(this.rtcConfiguration); peerConnection.onicecandidate = (event) => { if (event.candidate) { this.onIceCandidate?.(subscriberId, event.candidate); } }; peerConnection.onconnectionstatechange = () => { console.log(`Connection state for ${subscriberId}:`, peerConnection.connectionState); if (peerConnection.connectionState === 'failed' || peerConnection.connectionState === 'disconnected') { this.removeSubscriber(subscriberId); } }; this.peerConnections.set(subscriberId, peerConnection); this.updateSubscriberCount(); return peerConnection; } private updateSubscriberCount(): void { if (this.onSubscriberCountChange) { this.onSubscriberCountChange(this.peerConnections.size); } } private onIceCandidate?: (subscriberId: string, candidate: RTCIceCandidate) => void; setOnIceCandidate(handler: (subscriberId: string, candidate: RTCIceCandidate) => void): void { this.onIceCandidate = handler; } }