class Config { static peerConnectionConfig = { 'iceServers': [ {'urls': 'stun:stun.stunprotocol.org:3478'}, {'urls': 'stun:stun.l.google.com:19302'}, ] }; static getCookie(cname) { var name = cname + "="; var decodedCookie = decodeURIComponent(document.cookie); var ca = decodedCookie.split(';'); for(var i = 0; i ' + msg + ''; } static info() { View.write(rconsole.info, "gray", arguments); } static warn() { View.write(rconsole.warn, "orange", arguments); } static log() { View.write(rconsole.log, "black", arguments); } static error() { View.write(rconsole.error, "red", arguments); } } class Controller { static serverConnection; constructor(address) { Controller.serverConnection = new WebSocket(address); } } class Entropy { static localVideo = null; static localStream = null; static remoteVideo = null; static peerConnection = null; static pageReady() { Config.getUUID(); Entropy.localVideo = document.getElementById('localVideo'); Entropy.remoteVideo = document.getElementById('remoteVideo'); new Controller('wss://' + window.location.hostname + '/abc'); Controller.serverConnection.onmessage = Entropy.gotMessageFromServer; var constraints = { video: true, audio: false, }; if(navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia(constraints).then(Entropy.getUserMediaSuccess).catch(View.error); } else { alert('Your browser does not support getUserMedia API'); } } static getUserMediaSuccess(stream) { Entropy.localStream = stream; Entropy.localVideo.srcObject = stream; } static start(isCaller) { Entropy.peerConnection = new RTCPeerConnection(Config.peerConnectionConfig); Entropy.peerConnection.onicecandidate = Entropy.gotIceCandidate; Entropy.peerConnection.ontrack = Entropy.gotRemoteStream; Entropy.peerConnection.addStream(Entropy.localStream); if(isCaller) { Entropy.peerConnection .createOffer({ 'iceRestart': true, 'voiceActivityDetection': true, }) .then(Entropy.createdDescription) .catch(View.error); } } static gotMessageFromServer(message) { if(!Entropy.peerConnection) Entropy.start(false); var signal = JSON.parse(message.data); // Ignore messages from ourself if(signal.uuid == Config.getUUID()) return; if(signal.sdp) { Entropy.peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(function() { // Only create answers in response to offers if(signal.sdp.type == 'offer') { Entropy.peerConnection.createAnswer().then(Entropy.createdDescription).catch(View.error); } }).catch(View.error); } else if(signal.ice) { Entropy.peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(View.error); } } static gotIceCandidate(event) { if(event.candidate != null) { Controller.serverConnection.send(JSON.stringify({'ice': event.candidate, 'uuid': Config.getUUID()})); } } static createdDescription(description) { View.log('got description'); Entropy.peerConnection .setLocalDescription(description) .then(function() { Controller.serverConnection.send(JSON.stringify({ 'sdp': Entropy.peerConnection.localDescription, 'uuid': Config.getUUID(), })); }) .catch(View.error); } static gotRemoteStream(event) { View.log('got remote stream'); Entropy.remoteVideo.srcObject = event.streams[0]; } } function pageReady() { Entropy.pageReady(); } function start(b, type = null) { Entropy.start(b); } function toggle(type, caller) { //start(true, type); View.log(type, caller.checked, streams); if (caller.checked && ! type in streams) { View.log("start", type); streams[type] = null; } else if (type in streams) { View.log("stop", type); delete streams[type]; } } var streams = {} var rconsole = console; var console = {} for (var i in ["log", "info", "warn", "error"]) { console[i] = View[i]; } window.console = console;