| online: [], | online: [], | ||||
| posts: [], | posts: [], | ||||
| rpcs: {}, | rpcs: {}, | ||||
| media: {}, | |||||
| streams: {}, | |||||
| options: {}, | options: {}, | ||||
| } | } | ||||
| const markedOptions = { | const markedOptions = { | ||||
| if(State.username === username) { | if(State.username === username) { | ||||
| return | return | ||||
| } | } | ||||
| const myStream = State.media[State.username] | |||||
| const myStream = State.streams[State.username] | |||||
| if(!State.rpcs[username] && myStream) { | if(!State.rpcs[username] && myStream) { | ||||
| 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)) | myStream.getTracks().forEach(track => rpc.addTrack(track, myStream)) | ||||
| } | } | ||||
| } | } | ||||
| rpc.ontrack = (e) => { | rpc.ontrack = (e) => { | ||||
| State.media[username] = e.streams[0] | |||||
| State.streams[username] = e.streams[0] | |||||
| m.redraw() | m.redraw() | ||||
| } | } | ||||
| rpc.onclose = (e) => { | rpc.onclose = (e) => { | ||||
| rpc.addIceCandidate(candidate) | rpc.addIceCandidate(candidate) | ||||
| } | } | ||||
| else if(message.value.type === 'stop') { | else if(message.value.type === 'stop') { | ||||
| if(State.media[message.source]) { | |||||
| State.media[message.source].getTracks().map(track => track.stop()) | |||||
| delete State.media[message.source] | |||||
| if(State.streams[message.source]) { | |||||
| State.streams[message.source].getTracks().map(track => track.stop()) | |||||
| delete State.streams[message.source] | |||||
| } | } | ||||
| if(State.rpcs[message.source]) { | if(State.rpcs[message.source]) { | ||||
| State.rpcs[message.source].close() | State.rpcs[message.source].close() | ||||
| } | } | ||||
| const Video = { | const Video = { | ||||
| keepRatio: {observe: () => {}}, | keepRatio: {observe: () => {}}, | ||||
| appendStream: ({username, stream}) => ({dom}) => { | |||||
| appendStream: ({username}) => ({dom}) => { | |||||
| dom.autoplay = true | dom.autoplay = true | ||||
| dom.muted = (username === State.username) | dom.muted = (username === State.username) | ||||
| dom.srcObject = stream | |||||
| dom.srcObject = State.streams[username] | |||||
| }, | }, | ||||
| view({attrs}) { | view({attrs}) { | ||||
| const classList = VideoOptions.getClassListFor(attrs.username) | const classList = VideoOptions.getClassListFor(attrs.username) | ||||
| }, | }, | ||||
| turnOn: async () => { | turnOn: async () => { | ||||
| const media = await getSelectedMedia() | const media = await getSelectedMedia() | ||||
| State.media[State.username] = media | |||||
| State.streams[State.username] = media | |||||
| wire({kind: 'peerInfo', value: {type: 'request'}}) | wire({kind: 'peerInfo', value: {type: 'request'}}) | ||||
| m.redraw() | m.redraw() | ||||
| }, | }, | ||||
| view() { | view() { | ||||
| return m('.media', | return m('.media', | ||||
| m('.media-settings', | m('.media-settings', | ||||
| State.media[State.username] | |||||
| State.streams[State.username] | |||||
| ? 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('label', m('input#mute-check', {type: 'checkbox'}), 'mute'), | m('label', m('input#mute-check', {type: 'checkbox'}), 'mute'), | ||||
| ), | ), | ||||
| m('.videos', | m('.videos', | ||||
| Object.entries(State.media).map(([username, stream]) => | |||||
| m(Video, {username, stream}) | |||||
| Object.keys(State.streams).map((username) => | |||||
| m(Video, {username}) | |||||
| ), | ), | ||||
| ), | ), | ||||
| ) | ) |