| @@ -49,10 +49,8 @@ const getOrCreateRpc = (username) => { | |||
| if(State.username === username) { | |||
| 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'}]}) | |||
| myStream.getTracks().forEach(track => rpc.addTrack(track, myStream)) | |||
| rpc.onicecandidate = ({candidate}) => { | |||
| if(candidate) { | |||
| wire({kind: 'peerInfo', value: {type: 'candidate', candidate}}) | |||
| @@ -72,9 +70,18 @@ const getOrCreateRpc = (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 | |||
| if(!muted) { | |||
| @@ -97,16 +104,24 @@ const getSelectedMedia = async () => { | |||
| .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 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') { | |||
| 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() | |||
| @@ -286,9 +301,8 @@ const Media = { | |||
| echoCancellation: true, | |||
| }, | |||
| 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() | |||
| }, | |||
| turnOff: () => { | |||
| @@ -302,8 +316,13 @@ const Media = { | |||
| ? m('button', {onclick: Media.turnOff}, 'turn off') | |||
| : 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', | |||
| Object.keys(State.streams).map((username) => | |||