const getOrCreateRpc = (username) => { if(State.username === username) { return } if(!State.rpcs[username]) { rpc.onicecandidate = ({candidate}) => { if(candidate) { wire({kind: 'peerInfo', value: {type: 'candidate', candidate}}) } } rpc.ontrack = (e) => { State.streams[username] = e.streams[0] m.redraw() } rpc.onclose = (e) => { console.log(username, e) } rpc.oniceconnectionstatechange = (e) => { m.redraw() } State.rpcs[username] = rpc } return State.rpcs[username] } const onPeerInfo = async ({detail: message}) => { const localStream = State.streams[State.username] const rpc = localStream && getOrCreateRpc(message.source) const resetStreams = () => { rpc.getSenders().forEach(sender => rpc.removeTrack(sender)) localStream.getTracks().forEach(track => rpc.addTrack(track, localStream)) } if(rpc && message.value.type === 'request') { resetStreams() const localOffer = await rpc.createOffer() await rpc.setLocalDescription(localOffer) wire({kind: 'peerInfo', value: localOffer, target: message.source}) } else if(rpc && message.value.type === 'offer') { resetStreams() const remoteOffer = new RTCSessionDescription(message.value) await rpc.setRemoteDescription(remoteOffer) const localAnswer = await rpc.createAnswer() await rpc.setLocalDescription(localAnswer) wire({kind: 'peerInfo', value: localAnswer, target: message.source}) } else if(rpc && message.value.type === 'answer') { const remoteAnswer = new RTCSessionDescription(message.value) await rpc.setRemoteDescription(remoteAnswer) } else if(rpc && message.value.type === 'candidate') { const candidate = new RTCIceCandidate(message.value.candidate) rpc.addIceCandidate(candidate) } else if(message.value.type === 'stop') { if(State.streams[message.source]) { State.streams[message.source].getTracks().map(track => track.stop()) delete State.streams[message.source] } if(State.rpcs[message.source]) { State.rpcs[message.source].close() delete State.rpcs[message.source] } } else if(rpc) { console.log('uncaught', message) } }