|
|
@@ -2,7 +2,8 @@ const VideoConfig = Object.seal({ |
|
|
|
videoOn: true, |
|
|
|
audioOn: true, |
|
|
|
get video() { |
|
|
|
return VideoConfig.videoOn && {width: {ideal: 320}, facingMode: 'user', frameRate: 26} |
|
|
|
return VideoConfig.videoOn |
|
|
|
&& {width: {ideal: 320}, facingMode: 'user', frameRate: 26} |
|
|
|
}, |
|
|
|
get audio() { |
|
|
|
return VideoConfig.audioOn |
|
|
@@ -13,19 +14,22 @@ const VideoConfig = Object.seal({ |
|
|
|
} |
|
|
|
}) |
|
|
|
const VideoSelf = { |
|
|
|
update() { |
|
|
|
const stream = document.querySelector('video.self').srcObject |
|
|
|
stream.getTracks().forEach(track => { |
|
|
|
async update() { |
|
|
|
const video = document.querySelector('video.self') |
|
|
|
video.srcObject.getTracks().forEach(track => { |
|
|
|
track.stop() |
|
|
|
stream.removeTrack(track) |
|
|
|
video.srcObject.removeTrack(track) |
|
|
|
delete track |
|
|
|
}) |
|
|
|
stream = new MediaStream() |
|
|
|
if(VideoConfig.videoOn || VideoConfig.audioOn) { |
|
|
|
navigator.mediaDevices |
|
|
|
await navigator.mediaDevices |
|
|
|
.getUserMedia(VideoConfig) |
|
|
|
.then(s => s.getTracks().forEach(t => stream.addTrack(t))) |
|
|
|
.catch(e => console.error(e)) |
|
|
|
} |
|
|
|
video.srcObject = stream |
|
|
|
wire({kind: 'peerInfo', value: {type: 'request'}}) |
|
|
|
}, |
|
|
|
setUp: ({dom}) => { |
|
|
|
dom.playsinline = true |
|
|
@@ -57,10 +61,73 @@ const VideoSelf = { |
|
|
|
}, |
|
|
|
} |
|
|
|
const VideoOther = { |
|
|
|
setUp: ({dom}) => { |
|
|
|
setUp: (username) => ({dom}) => { |
|
|
|
dom.playsinline = true |
|
|
|
dom.autoplay = true |
|
|
|
dom.srcObject = new MediaStream() |
|
|
|
let rpc = null |
|
|
|
|
|
|
|
const rpcConfig = {iceServers: [{urls: 'stun:stun.sipgate.net:3478'}]} |
|
|
|
|
|
|
|
const stopRpc = () => { |
|
|
|
rpc && rpc.close() |
|
|
|
dom.srcObject.getTracks().forEach(track => { |
|
|
|
track.stop() |
|
|
|
dom.srcObject.removeTrack(track) |
|
|
|
delete track |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
const resetRpc = () => { |
|
|
|
stopRpc() |
|
|
|
rpc = new RTCPeerConnection(rpcConfig) |
|
|
|
dom.srcObject = new MediaStream() |
|
|
|
document.querySelector('video.self').srcObject.getTracks() |
|
|
|
.forEach(t => rpc.addTrack(t)) |
|
|
|
|
|
|
|
rpc.onicecandidate = ({candidate}) => { |
|
|
|
if(candidate && candidate.candidate) { |
|
|
|
wire({kind: 'peerInfo', value: {type: 'candidate', candidate}, target: username}) |
|
|
|
} |
|
|
|
} |
|
|
|
rpc.ontrack = ({track}) => { |
|
|
|
dom.srcObject.addTrack(track) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const onPeerInfo = async ({detail: {source, value}}) => { |
|
|
|
if(source !== username) { |
|
|
|
return |
|
|
|
} |
|
|
|
console.log(source, value.type) |
|
|
|
if(value.type === 'request') { |
|
|
|
resetRpc() |
|
|
|
const localOffer = await rpc.createOffer() |
|
|
|
await rpc.setLocalDescription(localOffer) |
|
|
|
wire({kind: 'peerInfo', value: localOffer, target: username}) |
|
|
|
} |
|
|
|
else if(value.type === 'offer') { |
|
|
|
resetRpc() |
|
|
|
const remoteOffer = new RTCSessionDescription(value) |
|
|
|
await rpc.setRemoteDescription(remoteOffer) |
|
|
|
const localAnswer = await rpc.createAnswer() |
|
|
|
await rpc.setLocalDescription(localAnswer) |
|
|
|
wire({kind: 'peerInfo', value: localAnswer, target: username}) |
|
|
|
} |
|
|
|
else if(value.type === 'answer') { |
|
|
|
const remoteAnswer = new RTCSessionDescription(value) |
|
|
|
await rpc.setRemoteDescription(remoteAnswer) |
|
|
|
} |
|
|
|
else if(value.type === 'candidate') { |
|
|
|
const candidate = new RTCIceCandidate(value.candidate) |
|
|
|
await rpc.addIceCandidate(candidate) |
|
|
|
} |
|
|
|
else if(value.type === 'stop') { |
|
|
|
stopRpc() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
addEventListener('peerInfo', onPeerInfo) |
|
|
|
}, |
|
|
|
view({attrs: {username}}) { |
|
|
|
const styleOuter = { |
|
|
@@ -80,11 +147,12 @@ const VideoOther = { |
|
|
|
m('.video-info', {style: {position: 'absolute', zIndex: 999}}, |
|
|
|
m('span', {style: {padding: '5px'}}, username), |
|
|
|
), |
|
|
|
m('video.self', {style: styleInner, oncreate: this.setUp}), |
|
|
|
m('video', {style: styleInner, oncreate: this.setUp(username)}), |
|
|
|
) |
|
|
|
}, |
|
|
|
} |
|
|
|
const StreamContainer = { |
|
|
|
// screen.width, screen.height |
|
|
|
getColumns() { |
|
|
|
const n = State.online.length |
|
|
|
if(n > 4) return '1fr 1fr 1fr' |
|
|
@@ -119,3 +187,7 @@ const StreamContainer = { |
|
|
|
) |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
const signalPeerStop = (username) => signal({kind: 'peerInfo', value: {type: 'stop'}, source: username}) |
|
|
|
addEventListener('pagehide', () => State.online.forEach(signalPeerStop)) |
|
|
|
addEventListener('logout', () => State.online.forEach(signalPeerStop)) |