|
|
@@ -119,6 +119,9 @@ const autoFocus = (vnode) => { |
|
|
|
const scrollIntoView = (vnode) => { |
|
|
|
vnode.dom.scrollIntoView() |
|
|
|
} |
|
|
|
const toggleFullscreen = (el) => (event) => { |
|
|
|
document.fullscreenElement ? document.exitFullscreen() : el.requestFullscreen() |
|
|
|
} |
|
|
|
const prettyTime = (ts) => { |
|
|
|
const dt = new Date(ts) |
|
|
|
const H = `0${dt.getHours()}`.slice(-2) |
|
|
@@ -139,20 +142,30 @@ const Video = { |
|
|
|
dom.autoplay = true |
|
|
|
dom.muted = (username === State.username) |
|
|
|
dom.srcObject = stream |
|
|
|
dom.ondblclick = toggleFullscreen(dom) |
|
|
|
}, |
|
|
|
view({attrs}) { |
|
|
|
const rpc = State.rpcs[attrs.username] || {iceConnectionState: m.trust(' ')} |
|
|
|
return m('.video-container', |
|
|
|
m('.video-source', attrs.username), |
|
|
|
m('.video-state', rpc.iceConnectionState), |
|
|
|
m('video', {playsinline: true, oncreate: Video.appendStream(attrs)}), |
|
|
|
m('video.mirrored', {playsinline: true, oncreate: Video.appendStream(attrs)}), |
|
|
|
) |
|
|
|
}, |
|
|
|
} |
|
|
|
const audio = { |
|
|
|
noiseSuppresion: true, |
|
|
|
echoCancellation: true, |
|
|
|
} |
|
|
|
const Media = { |
|
|
|
audioVideo: {audio: true, video: {width: {ideal: 320}, facingMode: 'user'}}, |
|
|
|
audioOnly: {audio: true, video: false}, |
|
|
|
turnOn: (constraints) => async () => { |
|
|
|
options: { |
|
|
|
video: {audio, video: {width: {ideal: 320}, facingMode: 'user'}}, |
|
|
|
audio: {audio, video: false}, |
|
|
|
screen: {audio, video: {mediaSource: 'screen'}}, |
|
|
|
none: {audio: false, video: false}, |
|
|
|
}, |
|
|
|
turnOn: async () => { |
|
|
|
const constraints = Media.options[document.querySelector('#media-source').value] |
|
|
|
const media = await navigator.mediaDevices.getUserMedia(constraints) |
|
|
|
State.media[State.username] = media |
|
|
|
wire({kind: 'peerInfo', value: {type: 'request'}}) |
|
|
@@ -165,13 +178,15 @@ const Media = { |
|
|
|
view() { |
|
|
|
if(!State.media[State.username]) { |
|
|
|
return m('.media', |
|
|
|
m('button', {onclick: Media.turnOn(Media.audioVideo)}, 'turn media on'), |
|
|
|
m('button', {onclick: Media.turnOn(Media.audioOnly)}, 'turn audio on'), |
|
|
|
m('button', {onclick: Media.turnOn}, 'turn on'), |
|
|
|
m('select#media-source', |
|
|
|
Object.keys(Media.options).map(description => m('option', description)), |
|
|
|
), |
|
|
|
) |
|
|
|
} |
|
|
|
else { |
|
|
|
return m('.media', |
|
|
|
m('button', {onclick: Media.turnOff}, 'turn media off'), |
|
|
|
m('button', {onclick: Media.turnOff}, 'turn off'), |
|
|
|
m('.videos', |
|
|
|
Object.entries(State.media).map(([username, stream]) => |
|
|
|
m(Video, {username, stream}) |