diff --git a/docker-compose.yml b/docker-compose.yml index 2f9793f..f91426f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: - BROADCASTER_PASSWORD=${BROADCASTER_PASSWORD} - TURN_USER=${TURN_USER} - TURN_PASSWORD=${TURN_PASSWORD} + - TURN_REALM=${TURN_REALM} coturn: image: coturn/coturn:latest @@ -20,6 +21,6 @@ services: --log-file=stdout --min-port=49152 --max-port=49252 - --realm=yourdomain.com + --realm=${TURN_REALM} --user=${TURN_USER}:${TURN_PASSWORD} --stale-nonce diff --git a/public/viewer.js b/public/viewer.js index d3d74e2..72339db 100644 --- a/public/viewer.js +++ b/public/viewer.js @@ -3,6 +3,7 @@ const remoteVideo = document.getElementById('remoteVideo'); const overlay = document.getElementById('overlay'); let peerConnection; +let broadcasterPeerId = null; // Track who the broadcaster is // Fetch TURN credentials dynamically from the server const turnHost = window.location.hostname; @@ -28,6 +29,9 @@ fetch('/turn-config') .catch(e => console.error('Failed to load TURN config:', e)); socket.on('offer', (id, description) => { + // Track the broadcaster's socket ID so we only react to THEIR disconnect + broadcasterPeerId = id; + // Close any existing connection before creating a new one if (peerConnection) { peerConnection.close(); @@ -51,11 +55,9 @@ socket.on('offer', (id, description) => { peerConnection.oniceconnectionstatechange = () => { console.log('ICE state:', peerConnection.iceConnectionState); if (peerConnection.iceConnectionState === 'failed') { - // Try ICE restart before giving up console.log('ICE failed, attempting restart...'); peerConnection.restartIce(); } else if (peerConnection.iceConnectionState === 'disconnected') { - // Wait briefly then check if it recovered setTimeout(() => { if (peerConnection && peerConnection.iceConnectionState === 'disconnected') { console.log('ICE still disconnected, attempting restart...'); @@ -114,11 +116,15 @@ socket.on('broadcaster', () => { socket.emit('viewer'); }); -socket.on('disconnectPeer', () => { +// CRITICAL: Only react to the BROADCASTER's disconnect, not other viewers +socket.on('disconnectPeer', (id) => { + if (id !== broadcasterPeerId) return; // Ignore other viewers disconnecting + if (peerConnection) { peerConnection.close(); peerConnection = null; } + broadcasterPeerId = null; remoteVideo.classList.remove('active'); remoteVideo.srcObject = null;