const isSecure = window.location.protocol === 'https:'; const host = window.location.hostname; const ws0builder = isSecure ? `wss://${host}/ws0` : `ws://${host}:3001`; const ws1builder = isSecure ? `wss://${host}/ws1` : `ws://${host}:3002`; const ws0 = new WebSocket(ws0builder); const ws1 = new WebSocket(ws1builder); const config = { iceServers: [ { urls: ['stun:dwestgate.us:3478','turn:dwestgate.us:3478?transport=udp'], username: 'webrtcuser', credential: 'webrtccred' } ] }; interface IData { channel: string, signal: number, cn: number } const pc0 = new RTCPeerConnection(config); const pc1 = new RTCPeerConnection(config); const video0 = document.getElementById('video-0') as HTMLVideoElement; const video1 = document.getElementById('video-1') as HTMLVideoElement; const station0 = document.getElementById('station-0') as HTMLHeadElement; const station1 = document.getElementById('station-1') as HTMLHeadElement; const signal0 = document.getElementById('signal-0') as HTMLHeadElement; const signal1 = document.getElementById('signal-1') as HTMLHeadElement; // 0 const dataChannel0 = pc0.createDataChannel('meta'); console.log("📡 Data channel created by client"); dataChannel0.onopen = () => { console.log('📬 Client: Data channel opened'); }; dataChannel0.onmessage = (event) => { const json = JSON.parse(event.data) as IData; station0.textContent = json.channel; signal0.textContent = `Signal: ${json.signal}\tC/N: ${json.cn}` }; dataChannel0.onclose = () => { console.log("📴 Client: Data channel closed"); }; // 0 pc0.ontrack = (event) => { console.log("Received track event", event.streams); video0.srcObject = event.streams[0]; }; pc0.onicecandidate = ({ candidate }) => { if (candidate) { ws0.send(JSON.stringify({ type: 'ice-candidate', data: candidate })); } }; ws0.onopen = async () => { pc0.addTransceiver('video', { direction: 'recvonly' }); pc0.addTransceiver('audio', { direction: 'recvonly' }) const offer = await pc0.createOffer(); await pc0.setLocalDescription(offer); ws0.send(JSON.stringify({ type: 'offer', data: offer })); } ws0.onmessage = async (message) => { const msg = JSON.parse(message.data); if (msg.type === 'answer') { await pc0.setRemoteDescription(msg.data); } else if (msg.type === 'ice-candidate') { await pc0.addIceCandidate(msg.data); } }; // 1 const dataChannel1 = pc1.createDataChannel('meta'); console.log("📡 Data channel created by client"); dataChannel1.onopen = () => { console.log('📬 Client: Data channel opened'); }; dataChannel1.onmessage = (event) => { const json = JSON.parse(event.data) as IData; station1.textContent = json.channel; signal1.textContent = `Signal: ${json.signal}\tC/N: ${json.cn}` }; dataChannel1.onclose = () => { console.log("📴 Client: Data channel closed"); }; pc1.ontrack = (event) => { console.log("Received track event", event.streams); video1.srcObject = event.streams[0]; }; pc1.onicecandidate = ({ candidate }) => { if (candidate) { ws1.send(JSON.stringify({ type: 'ice-candidate', data: candidate })); } }; ws1.onopen = async () => { pc1.addTransceiver('video', { direction: 'recvonly' }); pc1.addTransceiver('audio', { direction: 'recvonly' }) const offer = await pc1.createOffer(); await pc1.setLocalDescription(offer); ws1.send(JSON.stringify({ type: 'offer', data: offer })); } ws1.onmessage = async (message) => { const msg = JSON.parse(message.data); if (msg.type === 'answer') { await pc1.setRemoteDescription(msg.data); } else if (msg.type === 'ice-candidate') { await pc1.addIceCandidate(msg.data); } }; // TODO: Not yet working // const dc0 = pc0.createDataChannel("keepalive"); // dc0.onopen = () =>{ // console.log("Data channel 0 open"); // setInterval(()=>{ // dc0.send("ping"); // },10000) // } // const dc1 = pc1.createDataChannel("keepalive"); // dc1.onopen = () =>{ // console.log("Data channel 1 open"); // setInterval(()=>{ // dc1.send("ping"); // },10000) // }