| if(State.username === username) { | if(State.username === username) { | ||||
| return | return | ||||
| } | } | ||||
| const myStream = State.streams[State.username] | |||||
| if(!State.rpcs[username] && myStream) { | |||||
| if(!State.rpcs[username]) { | |||||
| const rpc = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.sipgate.net:3478'}]}) | const rpc = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.sipgate.net:3478'}]}) | ||||
| myStream.getTracks().forEach(track => rpc.addTrack(track, myStream)) | |||||
| rpc.onicecandidate = ({candidate}) => { | rpc.onicecandidate = ({candidate}) => { | ||||
| if(candidate) { | if(candidate) { | ||||
| wire({kind: 'peerInfo', value: {type: 'candidate', candidate}}) | wire({kind: 'peerInfo', value: {type: 'candidate', candidate}}) | ||||
| } | } | ||||
| return State.rpcs[username] | return State.rpcs[username] | ||||
| } | } | ||||
| const getSelectedMedia = async () => { | |||||
| const stream = new MediaStream() | |||||
| const addTrack = stream.addTrack.bind(stream) | |||||
| const setSelectedMedia = async () => { | |||||
| const localStream = State.streams[State.username] | |||||
| if(!localStream) { | |||||
| return | |||||
| } | |||||
| localStream.getTracks().forEach(track => { | |||||
| track.stop() | |||||
| localStream.removeTrack(track) | |||||
| }) | |||||
| const addTrack = localStream.addTrack.bind(localStream) | |||||
| const muted = document.querySelector('#mute-check').checked | const muted = document.querySelector('#mute-check').checked | ||||
| if(!muted) { | if(!muted) { | ||||
| .catch(e => console.error(e)) | .catch(e => console.error(e)) | ||||
| } | } | ||||
| return stream | |||||
| document.querySelectorAll('video').forEach(video => video.srcObject = video.srcObject) | |||||
| wire({kind: 'peerInfo', value: {type: 'request'}}) | |||||
| } | } | ||||
| const onPeerInfo = async ({detail: message}) => { | const onPeerInfo = async ({detail: message}) => { | ||||
| const rpc = getOrCreateRpc(message.source) | |||||
| 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') { | if(rpc && message.value.type === 'request') { | ||||
| resetStreams() | |||||
| const localOffer = await rpc.createOffer() | const localOffer = await rpc.createOffer() | ||||
| await rpc.setLocalDescription(localOffer) | await rpc.setLocalDescription(localOffer) | ||||
| wire({kind: 'peerInfo', value: localOffer, target: message.source}) | wire({kind: 'peerInfo', value: localOffer, target: message.source}) | ||||
| } | } | ||||
| else if(rpc && message.value.type === 'offer') { | else if(rpc && message.value.type === 'offer') { | ||||
| resetStreams() | |||||
| const remoteOffer = new RTCSessionDescription(message.value) | const remoteOffer = new RTCSessionDescription(message.value) | ||||
| await rpc.setRemoteDescription(remoteOffer) | await rpc.setRemoteDescription(remoteOffer) | ||||
| const localAnswer = await rpc.createAnswer() | const localAnswer = await rpc.createAnswer() | ||||
| echoCancellation: true, | echoCancellation: true, | ||||
| }, | }, | ||||
| turnOn: async () => { | turnOn: async () => { | ||||
| const media = await getSelectedMedia() | |||||
| State.streams[State.username] = media | |||||
| wire({kind: 'peerInfo', value: {type: 'request'}}) | |||||
| State.streams[State.username] = new MediaStream() | |||||
| await setSelectedMedia() | |||||
| m.redraw() | m.redraw() | ||||
| }, | }, | ||||
| turnOff: () => { | turnOff: () => { | ||||
| ? m('button', {onclick: Media.turnOff}, 'turn off') | ? m('button', {onclick: Media.turnOff}, 'turn off') | ||||
| : m('button', {onclick: Media.turnOn}, 'turn on') | : m('button', {onclick: Media.turnOn}, 'turn on') | ||||
| , | , | ||||
| m('select#media-source', Media.videoSources.map(option => m('option', option))), | |||||
| m('label', m('input#mute-check', {type: 'checkbox'}), 'mute'), | |||||
| m('select#media-source', {onchange: setSelectedMedia}, | |||||
| Media.videoSources.map(option => m('option', option)) | |||||
| ), | |||||
| m('label', | |||||
| m('input#mute-check', {onchange: setSelectedMedia, type: 'checkbox'}), | |||||
| 'mute' | |||||
| ), | |||||
| ), | ), | ||||
| m('.videos', | m('.videos', | ||||
| Object.keys(State.streams).map((username) => | Object.keys(State.streams).map((username) => |