diff --git a/ui/src/services/classroom/webrtc.tsx b/ui/src/services/classroom/webrtc.tsx index 073dc6f1..05b4cdf4 100644 --- a/ui/src/services/classroom/webrtc.tsx +++ b/ui/src/services/classroom/webrtc.tsx @@ -1,5 +1,10 @@ export class WebRTCService { private peerConnections: Map = new Map() + private retryCounts: Map = new Map() // 🔑 her kullanıcı için retry sayacı + private maxRetries = 3 // 🔑 maksimum yeniden deneme sayısı + private signalRService: any // 👈 dışarıdan set edilecek SignalR servisi + private sessionId: string = '' // oturum için de lazım olabilir + private localStream: MediaStream | null = null private onRemoteStream?: (userId: string, stream: MediaStream) => void private onIceCandidate?: (userId: string, candidate: RTCIceCandidateInit) => void @@ -56,6 +61,7 @@ export class WebRTCService { async createPeerConnection(userId: string): Promise { const peerConnection = new RTCPeerConnection(this.rtcConfiguration) this.peerConnections.set(userId, peerConnection) + this.retryCounts.set(userId, 0) // bağlantı başında sıfırla // Eğer local stream varsa track'leri ekle if (this.localStream) { @@ -75,12 +81,29 @@ export class WebRTCService { } } - peerConnection.onconnectionstatechange = () => { + peerConnection.onconnectionstatechange = async () => { const state = peerConnection.connectionState console.log(`Bağlantı durumu [${userId}]: ${state}`) - if (['failed', 'closed'].includes(state)) { + + if (state === 'closed') { this.closePeerConnection(userId) } + + if (state === 'failed') { + let retries = this.retryCounts.get(userId) ?? 0 + if (retries < this.maxRetries) { + console.warn( + `⚠️ Bağlantı failed oldu, ICE restart deneniyor [${userId}] (Deneme ${retries + 1})`, + ) + this.retryCounts.set(userId, retries + 1) + await this.restartIce(peerConnection, userId) + } else { + console.error( + `❌ Bağlantı ${this.maxRetries} denemede başarısız [${userId}], peer kapatılıyor.`, + ) + this.closePeerConnection(userId) + } + } } // En sona ekle @@ -99,6 +122,11 @@ export class WebRTCService { return peerConnection } + setSignalRService(signalRService: any, sessionId: string) { + this.signalRService = signalRService + this.sessionId = sessionId + } + setIceCandidateHandler(callback: (userId: string, candidate: RTCIceCandidateInit) => void) { this.onIceCandidate = callback } @@ -232,12 +260,31 @@ export class WebRTCService { return this.localStream } + private async restartIce(peerConnection: RTCPeerConnection, userId: string) { + try { + const offer = await peerConnection.createOffer({ iceRestart: true }) + await peerConnection.setLocalDescription(offer) + console.log(`ICE restart başlatıldı [${userId}]`) + + // 🔑 SignalR üzerinden karşı tarafa gönder + if (this.signalRService) { + await this.signalRService.sendOffer(this.sessionId, userId, offer) + console.log(`ICE restart offer karşıya gönderildi [${userId}]`) + } else { + console.warn('⚠️ SignalR servisi bağlı değil, offer gönderilemedi') + } + } catch (err) { + console.error(`ICE restart başarısız [${userId}]:`, err) + } + } + closePeerConnection(userId: string): void { const peerConnection = this.peerConnections.get(userId) if (peerConnection) { peerConnection.getSenders().forEach((sender) => sender.track?.stop()) peerConnection.close() this.peerConnections.delete(userId) + this.retryCounts.delete(userId) // sayaç temizle } } diff --git a/ui/src/views/classroom/RoomDetail.tsx b/ui/src/views/classroom/RoomDetail.tsx index 3000ee3f..0c7ba91c 100644 --- a/ui/src/views/classroom/RoomDetail.tsx +++ b/ui/src/views/classroom/RoomDetail.tsx @@ -240,6 +240,8 @@ const RoomDetail: React.FC = () => { // WebRTC başlat webRTCServiceRef.current = new WebRTCService() + webRTCServiceRef.current.setSignalRService(signalRServiceRef.current, classSession.id) + const stream = await webRTCServiceRef.current.initializeLocalStream(micEnabled, camEnabled) if (stream) { setLocalStream(stream)