|
- addEventListener('rpc-new', ({detail}) => {
- const {uid, kind, target} = detail.value
- if(kind === 'video') {
- const sender = VideoShare.streams[State.username]
- const receiver = VideoShare.resetStream(target)
- signal({kind: 'rpc-setup', value: {uid, sender, receiver}})
- }
- })
- const Video = {
- view({attrs: {username}}) {
- const styleOuter = {
- position: 'relative',
- display: 'block',
- color: 'white',
- overflow: 'hidden',
- }
- const styleMeta = {
- position: 'absolute',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- height: '100%',
- width: '100%',
- fontFamily: 'monospace',
- fontSize: 'xxx-large',
- }
- const styleVideo = {
- objectFit: Settings.get('blackBars') ? 'contain' : 'cover',
- width: '100%',
- height: '100%',
- transform: username === State.username ? 'scaleX(-1)' : 'scaleX(1)',
- }
- return m('.video-container', {style: styleOuter},
- m('.video-info', {style: styleMeta},
- m('.username', username),
- ),
- m('video[playsinline][autoplay]', {
- style: styleVideo,
- srcObject: VideoShare.streams[username],
- oncreate: ({dom}) => {dom.muted = username === State.username},
- onremove: () => VideoShare.resetStream(username),
- }),
- )
- },
- }
- const Toggle = {
- view({attrs: {key, label}}) {
- const onclick = () => {
- VideoShare[key] = !VideoShare[key]
- VideoShare.getStream()
- }
- const checked = VideoShare[key]
- return m('label.styled',
- m('input[type=checkbox]', {onclick, checked}),
- label,
- )
- },
- }
- const VideoShareConfig = {
- get video() {
- return VideoShare.videoOn
- && State.online.length < 10
- && params.get('v') !== '0'
- && {width: {ideal: 320}, facingMode: 'user', frameRate: 26}
- },
- get audio() {
- return VideoShare.audioOn
- && params.get('a') !== '0'
- },
- view() {
- return [
- m(Toggle, {key: 'videoOn', label: 'video'}),
- m(Toggle, {key: 'audioOn', label: 'audio'}),
- ]
- },
- }
- const VideoShare = {
- videoOn: true,
- audioOn: true,
- streams: {},
- oncreate(e) {
- VideoShare.getStream()
- },
- resetStream(target) {
- if(VideoShare.streams[target]) {
- VideoShare.streams[target].getTracks().forEach(tr => tr.stop())
- }
- VideoShare.streams[target] = new MediaStream()
- return VideoShare.streams[target]
- },
- async getStream() {
- VideoShare.resetStream(State.username)
- await navigator.mediaDevices.getUserMedia(VideoShareConfig)
- .catch(error => console.error(error))
- .then(stream => {VideoShare.streams[State.username] = stream})
- m.redraw()
- State.others.forEach(target => {
- signal({kind: 'rpc-needed', value: {kind: 'video', target}})
- })
- },
- reLayout({dom}) {
- const COUNT = dom.children.length || 1
- let [fnx, fny] = [
- Math.floor((1 + Math.sqrt(4 * COUNT - 3)) / 2),
- Math.ceil(Math.sqrt(COUNT)),
- ]
- let max = 0
- for([nx, ny] of [[1, COUNT], [fnx, fny], [COUNT, 1]]) {
- let h = dom.clientHeight / ny
- let w = dom.clientWidth / nx
- if(w > h) {
- w = Math.min(w, h * 4/3)
- }
- else {
- h = Math.min(h, w * 3/4)
- }
- if(h * w > max) {
- max = h * w
- fnx = nx
- fny = ny
- }
- }
- dom.style['grid-template-columns'] = Array(fnx).fill('1fr').join(' ')
- dom.style['grid-template-rows'] = Array(fny).fill('1fr').join(' ')
- },
- view() {
- const style = {
- backgroundColor: 'black',
- overflow: 'hidden',
- display: 'grid',
- }
- const oncreate = VideoShare.reLayout
- const onupdate = VideoShare.reLayout
- return m('.videos', {style, oncreate, onupdate},
- State.online.map(username => m(Video, {key: username, username}))
- )
- },
- }
- addEventListener('load', () => {
- Headers.push([VideoShareConfig])
- Apps.push([VideoShare, {key: 'stream-container'}])
- })
|