Browse Source

Concentrate state

master
Roderic Day 5 years ago
parent
commit
63b3368b11
1 changed files with 34 additions and 32 deletions
  1. +34
    -32
      pico.js

+ 34
- 32
pico.js View File

const State = { const State = {
username: null,
websocket: null, websocket: null,
online: [], online: [],
posts: [], posts: [],
media: null,
username: null,
rpcs: {},
media: {},
} }


/* /*
*/ */
const wire = (message) => State.websocket.send(JSON.stringify(message)) const wire = (message) => State.websocket.send(JSON.stringify(message))
const signal = (message) => dispatchEvent(new CustomEvent(message.kind, {detail: message})) const signal = (message) => dispatchEvent(new CustomEvent(message.kind, {detail: message}))
const signalPeerStop = (username) => signal({kind: 'peerInfo', value: {type: 'stop'}, source: username})
const listen = (kind, handler) => addEventListener(kind, handler) const listen = (kind, handler) => addEventListener(kind, handler)
listen('login', ({detail}) => State.username = detail.value) listen('login', ({detail}) => State.username = detail.value)
listen('post', ({detail}) => State.posts.push(detail)) listen('post', ({detail}) => State.posts.push(detail))
listen('peerInfo', (e) => onPeerInfo(e)) listen('peerInfo', (e) => onPeerInfo(e))
const doNotLog = new Set(['login', 'post', 'peerInfo']) const doNotLog = new Set(['login', 'post', 'peerInfo'])



/* /*
* *
* WEBRTC * WEBRTC
* *
*/ */
const rpcs = {}
const streams = {}
const getOrCreateRpc = (username) => { const getOrCreateRpc = (username) => {
if(!rpcs[username] && State.media) {
if(State.username === username) {
return
}
const myStream = State.media[State.username]
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'}]})
State.media.getTracks().forEach(track => rpc.addTrack(track, State.media))
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}})
} }
} }
rpc.ontrack = (e) => { rpc.ontrack = (e) => {
streams[username] = e.streams[0]
State.media[username] = e.streams[0]
m.redraw() m.redraw()
} }
rpc.onclose = (e) => { rpc.onclose = (e) => {
console.log(username, e) console.log(username, e)
} }
rpcs[username] = rpc
State.rpcs[username] = rpc
} }
return rpcs[username]
return State.rpcs[username]
} }
const onPeerInfo = async ({detail: message}) => { const onPeerInfo = async ({detail: message}) => {
if(State.username === message.source) {
return
}
const rpc = getOrCreateRpc(message.source) const rpc = getOrCreateRpc(message.source)
if(rpc && message.value.type === 'request') { if(rpc && message.value.type === 'request') {
const localOffer = await rpc.createOffer() const localOffer = await rpc.createOffer()
rpc.addIceCandidate(candidate) rpc.addIceCandidate(candidate)
} }
else if(message.value.type === 'stop') { else if(message.value.type === 'stop') {
if(streams[message.source]) {
streams[message.source].getTracks().map(track => track.stop())
delete streams[message.source]
if(State.media[message.source]) {
State.media[message.source].getTracks().map(track => track.stop())
delete State.media[message.source]
} }
if(rpcs[message.source]) {
rpcs[message.source].close()
delete rpcs[message.source]
if(State.rpcs[message.source]) {
State.rpcs[message.source].close()
delete State.rpcs[message.source]
} }
} }
else {
else if(rpc) {
console.log('uncaught', message) console.log('uncaught', message)
} }
} }
return ts.slice(11, 19) return ts.slice(11, 19)
} }
const Video = { const Video = {
appendStream: (stream) => ({dom}) => {
appendStream: ({username, stream}) => ({dom}) => {
dom.autoplay = true dom.autoplay = true
dom.muted = true
dom.muted = (username === State.username)
dom.srcObject = stream dom.srcObject = stream
}, },
view({attrs}) { view({attrs}) {
const rpc = State.rpcs[attrs.username] || {iceConnectionState: m.trust(' ')}
return m('.video-container', return m('.video-container',
m('.video-source', attrs.username), m('.video-source', attrs.username),
m('video', {playsinline: true, oncreate: Video.appendStream(attrs.stream)}),
m('.video-state', rpc.iceConnectionState),
m('video', {playsinline: true, oncreate: Video.appendStream(attrs)}),
) )
}, },
} }
const Media = { const Media = {
constraints: {audio: true, video: {width: {ideal: 320}, facingMode: 'user'}}, constraints: {audio: true, video: {width: {ideal: 320}, facingMode: 'user'}},
turnOn: async () => { turnOn: async () => {
State.media = await navigator.mediaDevices.getUserMedia(Media.constraints)
const media = await navigator.mediaDevices.getUserMedia(Media.constraints)
State.media[State.username] = media
wire({kind: 'peerInfo', value: {type: 'request'}}) wire({kind: 'peerInfo', value: {type: 'request'}})
m.redraw() m.redraw()
}, },
turnOff: () => { turnOff: () => {
wire({kind: 'peerInfo', value: {type: 'stop'}}) wire({kind: 'peerInfo', value: {type: 'stop'}})
// signal internally
Object.keys(rpcs).forEach(source => signal({kind: 'peerInfo', value: {type: 'stop'}, source}))
State.media.getTracks().map(track => track.stop())
delete State.media
State.online.forEach(signalPeerStop)
}, },
view() { view() {
if(!State.media) {
if(!State.media[State.username]) {
return m('.media', return m('.media',
m('button', {onclick: Media.turnOn}, 'turn media on'), m('button', {onclick: Media.turnOn}, 'turn media on'),
) )
return m('.media', return m('.media',
m('button', {onclick: Media.turnOff}, 'turn media off'), m('button', {onclick: Media.turnOff}, 'turn media off'),
m('.videos', m('.videos',
m(Video, {username: State.username, stream: State.media}),
Object.entries(streams).map(([username, stream]) =>
Object.entries(State.media).map(([username, stream]) =>
m(Video, {username, stream}) m(Video, {username, stream})
), ),
), ),
connect(username) connect(username)
}, },
sendLogout: (e) => { sendLogout: (e) => {
State.media && Media.turnOff()
Media.turnOff()
wire({kind: 'logout'}) wire({kind: 'logout'})
State.posts = [] State.posts = []
}, },
m('input', attrs), m('input', attrs),
m('button', 'Login'), m('button', 'Login'),
), ),
State.kind === 'error' && m('.error', State.info),
m('.error', State.info),
) )
}, },
} }
if(localStorage.username) { if(localStorage.username) {
connect(localStorage.username) connect(localStorage.username)
} }
addEventListener('pagehide', Media.turnOff)

Loading…
Cancel
Save