Created
August 1, 2018 14:07
-
-
Save gtk2k/9b41ef9d7c00888ecb25f90fa5e739a1 to your computer and use it in GitHub Desktop.
Chromeのサイマルキャストes6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const series = {}; | |
| const graphs = {}; | |
| const dataTypes = ['bytesSent', 'framesEncoded']; | |
| let lastSendResult; | |
| let lastRecvResult; | |
| function show(stream, side) { | |
| const id = side !== 'local' ? stream.id : 'local'; | |
| const container = document.createElement('div'); | |
| container.id = `${id}Container`; | |
| window[side].appendChild(container); | |
| const v = document.createElement('video'); | |
| v.autoplay = true; | |
| v.srcObject = stream; | |
| v.onresize = _ => v.title = `video dimensions: ${v.videoWidth}x${v.videoHeight}`; | |
| container.appendChild(v); | |
| dataTypes.forEach(dataType => { | |
| const cnv = document.createElement('canvas'); | |
| cnv.id = `${id}${dataType}Canvas`; | |
| cnv.title = dataType; | |
| container.appendChild(cnv); | |
| series[dataType] = series[dataType] || {}; | |
| graphs[dataType] = graphs[dataType] || {}; | |
| series[dataType][id] = new TimelineDataSeries(); | |
| graphs[dataType][id] = new TimelineGraphView(`${id}Container`, `${id}${dataType}Canvas`); | |
| graphs[dataType][id].updateEndDate(); | |
| }); | |
| } | |
| const pc1 = new RTCPeerConnection(); | |
| const pc2 = new RTCPeerConnection(); | |
| pc1.onicecandidate = evt => pc2.addIceCandidate(evt.candidate); | |
| pc2.onicecandidate = evt => pc1.addIceCandidate(evt.candidate); | |
| pc2.ontrack = evt => show(evt.streams[0], 'remotes'); | |
| async function getStats(pc) { | |
| const res = pc.getStats(null); | |
| res.filter(report => report.type === 'outbound-rtp').forEach(report => { | |
| const now = report.timestamp; | |
| ['bytesSent', 'framesEncoded'].forEach(dataType => { | |
| if (!lastSendResult) return; | |
| const data = report[dataType]; | |
| const sentResult = lastSendResult.get(report.id); | |
| const sentResultData = sentResult[dataType]; | |
| const timestamp = sentResult.timestamp; | |
| const calcData = { bytesSent: 8000, framesEncoded: 1000 }[dataType] * (data - sendResultData / (now - timestamp)); | |
| series[dataType][id].local.addPoint(now, calcData); | |
| graphs[dataType][id].local.setDataSeries([graphs[dataType][id].local]); | |
| graphs[dataType][id].local.updateEndDate(); | |
| }); | |
| }); | |
| lastSendResult = res; | |
| } | |
| (async _ => { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 1280, height: 720 } }); | |
| pc1.addTrack(stream.getVideoTracks()[0], stream); | |
| show(stream, false); | |
| const offer = await pc1.createOffer(); | |
| const videoPart = SDPUtils.getMediaSections(offer.sdp)[0]; | |
| const match = videoPart.match(/a=ssrc:(\d+) cname:(.*)\r\n/); | |
| const msid = videoPart.match(/a=ssrc:(\d+) msid:(.*)\r\n/); | |
| const lines = offer.sdp.trim().split('\r\n'); | |
| const removed = lines.splice(lines.length - 4, 4); | |
| const videoSSRC1 = +match[1]; | |
| const rtxSSRC1 = SDPUtils.matchPrefix(videoPart, 'a=ssrc-group:FID ')[0].split(' ')[2]; | |
| const videoSSRC2 = videoSSRC1 + 1; | |
| const rtxSSRC2 = videoSSRC1 + 2; | |
| const videoSSRC3 = videoSSRC1 + 3; | |
| const rtxSSRC3 = videoSSRC1 + 4; | |
| offer.sdp = `${[ | |
| removed[0], | |
| removed[1], | |
| `a=ssrc:${videoSSRC2} cname:${match[2]}`, | |
| `a=ssrc:${videoSSRC2} msid:${msid[2]}`, | |
| `a=ssrc:${rtxSSRC2} cname:${match[2]}`, | |
| `a=ssrc:${rtxSSRC2} msid:${msid[2]}`, | |
| `a=ssrc:${videoSSRC3} cname:${match[2]}`, | |
| `a=ssrc:${videoSSRC3} msid:${msid[2]}`, | |
| `a=ssrc:${rtxSSRC3} cname:${match[2]}`, | |
| `a=ssrc:${rtxSSRC3} msid:${msid[2]}`, | |
| `a=ssrc-group:FID ${videoSSRC2} ${rtxSSRC2}`, | |
| `a=ssrc-group:FID ${videoSSRC3} ${rtxSSRC3}`, | |
| `a=ssrc-group:SIM ${videoSSRC1} ${videoSSRC2} ${videoSSRC3}` | |
| ].join('\r\n')}\r\n`; | |
| const offer2 = { | |
| type: 'offer', | |
| sdp: offer.sdp | |
| .replace(`a=ssrc-group:SIM ${videoSSRC1} ${videoSSRC2} ${videoSSRC3}\r\n`, '') | |
| .replace(`a=ssrc:${videoSSRC1} msid:${msid[2]}`, `a=ssrc:${videoSSRC1} msid:low low`) | |
| .replace(`a=ssrc:${rtxSSRC1} msid:${msid[2]}`, `a=ssrc:${rtxSSRC1} msid:low low`) | |
| .replace(`a=ssrc:${videoSSRC2} msid:${msid[2]}`, `a=ssrc:${videoSSRC2} msid:mid mid`) | |
| .replace(`a=ssrc:${rtxSSRC2} msid:${msid[2]}`, `a=ssrc:${rtxSSRC2} msid:mid mid`) | |
| .replace(`a=ssrc:${videoSSRC3} msid:${msid[2]}`, `a=ssrc:${videoSSRC3} msid:hi hi`) | |
| .replace(`a=ssrc:${rtxSSRC3} msid:${msid[2]}`, `a=ssrc:${rtxSSRC3} msid:hi hi`) | |
| }; | |
| await pc1.setLocalDescription(offer); | |
| await pc2.setRemoteDescription(offer2); | |
| const answer = await pc2.createAnswer(); | |
| await pc2.setLocalDescription(answer); | |
| await pc1.setRemoteDescription(answer); | |
| window.setInterval(_ => [pc1, pc2].forEach(pc => getStats(pc)), 2000); | |
| } catch (err) { | |
| console.error(err); | |
| } | |
| })(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment