From 22f0577ab5b357068b6b98b98fc2a5e64ef3476b Mon Sep 17 00:00:00 2001 From: Muaz Khan Date: Wed, 2 Mar 2016 15:03:14 +0500 Subject: [PATCH] Added rtcmulticonnection-v3@3.2.93 v3.2.93 now supports cordova based ios/android apps; as well as it is now compatible with latest socket.io versions. --- Gruntfile.js | 4 +- README.md | 137 +- RTCMultiConnection.js | 3817 +++++++++++++++++++------------------ RTCMultiConnection.min.js | 8 +- Signaling-Server.js | 35 +- dev/DetectRTC.js | 28 +- dev/MultiPeersHandler.js | 2 +- dev/RTCPeerConnection.js | 4 - dev/SocketConnection.js | 8 +- dev/getUserMedia.js | 10 - dev/globals.js | 2 + dev/ios-hacks.js | 13 +- dist/rmc3.fbr.min.js | 2 +- dist/rmc3.js | 3817 +++++++++++++++++++------------------ dist/rmc3.min.js | 8 +- package.json | 13 +- 16 files changed, 4027 insertions(+), 3881 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 05a49802..2c557ae2 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,9 +28,10 @@ module.exports = function(grunt) { 'dev/globals.js', 'dev/Plugin.EveryWhere.js', + 'dev/DetectRTC.js', - 'dev/RTCPeerConnection.js', 'dev/ios-hacks.js', // to support ios + 'dev/RTCPeerConnection.js', 'dev/CodecsHandler.js', // to force H264 or codecs other than opus 'dev/OnIceCandidateHandler.js', @@ -40,7 +41,6 @@ module.exports = function(grunt) { 'dev/getUserMedia.js', 'dev/StreamsHandler.js', - 'dev/DetectRTC.js', 'dev/getScreenId.js', // or getScreenId.js or Screen-Capturing.js 'dev/TextSenderReceiver.js', diff --git a/README.md b/README.md index 916a4765..ab58372b 100755 --- a/README.md +++ b/README.md @@ -27,9 +27,13 @@ Or: ``` sudo npm install rtcmulticonnection-v3 +cd node_modules +cd rtcmulticonnection-v3 # you MUST go to this directory +node server.js # or MOST preferred one -mkdir RTCMultiConnection-v3.0 && cd RTCMultiConnection-v3.0 +mkdir RTCMultiConnection-v3.0 +cd RTCMultiConnection-v3.0 wget http://dl.webrtc-experiment.com/rtcmulticonnection-v3.tar.gz tar -zxvf rtcmulticonnection-v3.tar.gz ls -a @@ -76,7 +80,7 @@ All files from `/dist` directory are available on CDN: `https://cdn.webrtc-exper - + ``` If you're sharing files, you also need to link: @@ -88,7 +92,7 @@ If you're sharing files, you also need to link: - + ``` > You can link multiple files from `dev` directory. Order doesn't matters. @@ -379,7 +383,7 @@ connection.getUserMediaHandler(options); Its defined here: -* [getUserMedia.js#L20](https://github.com/muaz-khan/RTCMultiConnection/tree/master/devgetUserMedia.js#L20) +* [getUserMedia.js#L20](https://github.com/muaz-khan/RTCMultiConnection/tree/master/dev/getUserMedia.js#L20) ## `becomePublicModerator` @@ -593,7 +597,7 @@ This method allows you skip Socket.io and force Firebase or PubNub or WebSockets connection.setCustomSocketHandler(FirebaseConnection); ``` -Please check [`FirebaseConnection`](https://github.com/muaz-khan/RTCMultiConnection/tree/master/devFirebaseConnection.js) or [`PubNubConnection.js`](https://github.com/muaz-khan/RTCMultiConnection/tree/master/devPubNubConnection.js) to understand how it works. +Please check [`FirebaseConnection`](https://github.com/muaz-khan/RTCMultiConnection/tree/master/dev/FirebaseConnection.js) or [`PubNubConnection.js`](https://github.com/muaz-khan/RTCMultiConnection/tree/master/dev/PubNubConnection.js) to understand how it works. ## `enableLogs` @@ -1242,6 +1246,129 @@ if(connection.DetectRTC.browser.name === 'Firefox') { ``` +## iOS/Android + +RTCMultiConnection-v3.0 supports `cordova` based iOS/android apps. + +``` +sudo npm install cordova -g +sudo npm install xcode -g + +cordova create ./mobileApp org.mobileApp mobileApp +cd mobileApp + +cordova plugin add cordova-plugin-iosrtc +cd hooks +wget https://raw.githubusercontent.com/eface2face/cordova-plugin-iosrtc/master/extra/hooks/iosrtc-swift-support.js + +sudo chmod +x iosrtc-swift-support.js + +cd .. +``` + +Now modify `config.xml` for this section: + +```xml + + + +``` + +Further commands: + +``` +cordova platform add ios@3.9.2 + +cordova build ios +cordova build android +``` + +Prerequisites: + +1. xcode `7.2.1` (required) +2. cordova android plugin `5.1.0` (suggested) +3. cordova ios plugin `3.9.2` --- note: MUST be this version (don't use newer ones) + +Check xcode-build-version: `xcodebuild -version` + +Make sure that terminal is using latest xcode: + +``` +xcode-select --print-path + +sudo xcode-select -switch /Applications/Xcode5.1.1/Xcode.app +``` + +`config.xml` hints: + +Modify `platform/android/AndroidManifest.xml` for ``. Now getUserMedia API will work in Android. + +An example `AndroidManifest.xml` file: + +```xml + + + + + + + + + + + + + + + + + + +``` + +An example `config.xml` file (make sure that `icon.png` has valid path): + +```xml + + + yourApp + + yourApp + You + + + + + + + + + + + + + + + + + + + + NSAppTransportSecurity + NSAllowsArbitraryLoads + + + + + + + + + + + +``` + ## RTCMultiConnection v2.2.2 Demos | Experiment Name | Demo | Source Code | diff --git a/RTCMultiConnection.js b/RTCMultiConnection.js index e080345f..f315bc11 100644 --- a/RTCMultiConnection.js +++ b/RTCMultiConnection.js @@ -1,4 +1,4 @@ -// Last time updated: 2016-02-25 12:41:18 PM UTC +// Last time updated: 2016-03-02 9:37:16 AM UTC // ______________________________ // RTCMultiConnection-v3.0 (Beta) @@ -1523,7 +1523,13 @@ parameters += '&maxRelayLimitPerUser=' + (connection.maxRelayLimitPerUser || 2); } - var socket = io.connect((connection.socketURL || '/') + parameters, connection.socketOptions); + var socket; + + try { + socket = io((connection.socketURL || '/') + parameters); + } catch (e) { + socket = io.connect((connection.socketURL || '/') + parameters, connection.socketOptions); + } var mPeer = connection.multiPeersHandler; @@ -2069,7 +2075,7 @@ } if (message.enableMedia) { - if (connection.attachStreams.length) { + if (connection.attachStreams.length || connection.dontCaptureUserMedia) { var streamsToShare = {}; connection.attachStreams.forEach(function(stream) { streamsToShare[stream.streamid] = { @@ -2281,10 +2287,12 @@ if (typeof cordova !== 'undefined') { isMobileDevice = true; + isChrome = true; } if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { isMobileDevice = true; + isChrome = true; } var isPluginRTC = !isMobileDevice && (isSafari || isIE); @@ -2765,2359 +2773,2354 @@ window.addEventListener('load', LoadPluginRTC, false); })(); - // RTCPeerConnection.js + // Last time updated: 2016-02-26 11:47:17 AM UTC - var defaults = {}; + // Latest file can be found here: https://cdn.webrtc-experiment.com/DetectRTC.js - function setSdpConstraints(config) { - var sdpConstraints; + // Muaz Khan - www.MuazKhan.com + // MIT License - www.WebRTC-Experiment.com/licence + // Documentation - github.com/muaz-khan/DetectRTC + // ____________ + // DetectRTC.js - var sdpConstraints_mandatory = { - OfferToReceiveAudio: !!config.OfferToReceiveAudio, - OfferToReceiveVideo: !!config.OfferToReceiveVideo - }; + // DetectRTC.hasWebcam (has webcam device!) + // DetectRTC.hasMicrophone (has microphone device!) + // DetectRTC.hasSpeakers (has speakers!) - sdpConstraints = { - mandatory: sdpConstraints_mandatory, - optional: [{ - VoiceActivityDetection: false - }] - }; + (function() { - if (!!navigator.mozGetUserMedia && firefoxVersion > 34) { - sdpConstraints = { - OfferToReceiveAudio: !!config.OfferToReceiveAudio, - OfferToReceiveVideo: !!config.OfferToReceiveVideo - }; - } + 'use strict'; - return sdpConstraints; - } + var navigator = window.navigator; - var RTCPeerConnection; - if (typeof mozRTCPeerConnection !== 'undefined') { - RTCPeerConnection = mozRTCPeerConnection; - } else if (typeof webkitRTCPeerConnection !== 'undefined') { - RTCPeerConnection = webkitRTCPeerConnection; - } else if (typeof window.RTCPeerConnection !== 'undefined') { - RTCPeerConnection = window.RTCPeerConnection; - } else { - console.error('WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.'); - RTCPeerConnection = window.RTCSessionDescription = window.RTCIceCandidate = function() {}; - } + if (typeof navigator !== 'undefined') { + if (typeof navigator.webkitGetUserMedia !== 'undefined') { + navigator.getUserMedia = navigator.webkitGetUserMedia; + } - var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription; - var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; - var MediaStreamTrack = window.MediaStreamTrack; + if (typeof navigator.mozGetUserMedia !== 'undefined') { + navigator.getUserMedia = navigator.mozGetUserMedia; + } + } else { + navigator = { + getUserMedia: function() {}, + userAgent: 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45' + }; + } - var Plugin = {}; + var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); + var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob); - function onPluginRTCInitialized(pluginRTCObject) { - Plugin = pluginRTCObject; - MediaStreamTrack = Plugin.MediaStreamTrack; - RTCPeerConnection = Plugin.RTCPeerConnection; - RTCIceCandidate = Plugin.RTCIceCandidate; - RTCSessionDescription = Plugin.RTCSessionDescription; - } - if (typeof PluginRTC !== 'undefined') onPluginRTCInitialized(PluginRTC); + var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; + var isFirefox = typeof window.InstallTrigger !== 'undefined'; + var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; + var isChrome = !!window.chrome && !isOpera; + var isIE = !!document.documentMode && !isEdge; - var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; - var isIE = !!document.documentMode; - var isPluginRTC = isSafari || isIE; + // this one can also be used: + // https://www.websocket.org/js/stuff.js (DetectBrowser.js) - function PeerInitiator(config) { - var connection = config.rtcMultiConnection; + function getBrowserInfo() { + var nVer = navigator.appVersion; + var nAgt = navigator.userAgent; + var browserName = navigator.appName; + var fullVersion = '' + parseFloat(navigator.appVersion); + var majorVersion = parseInt(navigator.appVersion, 10); + var nameOffset, verOffset, ix; - this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra; - this.remoteUserId = config.remoteUserId; - this.streams = []; - this.channels = []; - this.connectionDescription = config.connectionDescription; + // In Opera, the true version is after 'Opera' or after 'Version' + if (isOpera) { + browserName = 'Opera'; + try { + fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0]; + majorVersion = fullVersion.split('.')[0]; + } catch (e) { + fullVersion = '0.0.0.0'; + majorVersion = 0; + } + } + // In MSIE, the true version is after 'MSIE' in userAgent + else if (isIE) { + verOffset = nAgt.indexOf('MSIE'); + browserName = 'IE'; + fullVersion = nAgt.substring(verOffset + 5); + } + // In Chrome, the true version is after 'Chrome' + else if (isChrome) { + verOffset = nAgt.indexOf('Chrome'); + browserName = 'Chrome'; + fullVersion = nAgt.substring(verOffset + 7); + } + // In Safari, the true version is after 'Safari' or after 'Version' + else if (isSafari) { + verOffset = nAgt.indexOf('Safari'); + browserName = 'Safari'; + fullVersion = nAgt.substring(verOffset + 7); - var that = this; + if ((verOffset = nAgt.indexOf('Version')) !== -1) { + fullVersion = nAgt.substring(verOffset + 8); + } + } + // In Firefox, the true version is after 'Firefox' + else if (isFirefox) { + verOffset = nAgt.indexOf('Firefox'); + browserName = 'Firefox'; + fullVersion = nAgt.substring(verOffset + 8); + } - if (config.remoteSdp) { - this.connectionDescription = config.remoteSdp.connectionDescription; - } + // In most other browsers, 'name/version' is at the end of userAgent + else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) { + browserName = nAgt.substring(nameOffset, verOffset); + fullVersion = nAgt.substring(verOffset + 1); - var allRemoteStreams = {}; + if (browserName.toLowerCase() === browserName.toUpperCase()) { + browserName = navigator.appName; + } + } - if (Object.observe) { - var that = this; - Object.observe(this.channels, function(changes) { - changes.forEach(function(change) { - if (change.type === 'add') { - change.object[change.name].addEventListener('close', function() { - delete that.channels[that.channels.indexOf(change.object[change.name])]; - that.channels = removeNullEntries(that.channels); - }, false); - } - if (change.type === 'remove' || change.type === 'delete') { - if (that.channels.indexOf(change.object[change.name]) !== -1) { - delete that.channels.indexOf(change.object[change.name]); - } - } + if (isEdge) { + browserName = 'Edge'; + // fullVersion = navigator.userAgent.split('Edge/')[1]; + fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString(); + } - that.channels = removeNullEntries(that.channels); - }); - }); - } + // trim the fullVersion string at semicolon/space if present + if ((ix = fullVersion.indexOf(';')) !== -1) { + fullVersion = fullVersion.substring(0, ix); + } - defaults.sdpConstraints = setSdpConstraints({ - OfferToReceiveAudio: true, - OfferToReceiveVideo: true - }); + if ((ix = fullVersion.indexOf(' ')) !== -1) { + fullVersion = fullVersion.substring(0, ix); + } - var peer; + majorVersion = parseInt('' + fullVersion, 10); - var renegotiatingPeer = !!config.renegotiatingPeer; - if (config.remoteSdp) { - renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer; - } + if (isNaN(majorVersion)) { + fullVersion = '' + parseFloat(navigator.appVersion); + majorVersion = parseInt(navigator.appVersion, 10); + } - var localStreams = []; - connection.attachStreams.forEach(function(stream) { - if (!!stream) localStreams.push(stream); - }); + return { + fullVersion: fullVersion, + version: majorVersion, + name: browserName, + isPrivateBrowsing: false + }; + } - if (!renegotiatingPeer) { - peer = new RTCPeerConnection(navigator.onLine ? { - iceServers: connection.iceServers, - iceTransports: 'all' - } : null, connection.optionalArgument); - } else { - peer = config.peerRef; + // via: https://gist.github.com/cou929/7973956 - peer.getLocalStreams().forEach(function(stream) { - localStreams.forEach(function(localStream, index) { - if (stream == localStream) { - delete localStreams[index]; + function retry(isDone, next) { + var currentTrial = 0, + maxRetry = 50, + interval = 10, + isTimeout = false; + var id = window.setInterval( + function() { + if (isDone()) { + window.clearInterval(id); + next(isTimeout); } - }); - - connection.removeStreams.forEach(function(streamToRemove, index) { - if (stream === streamToRemove) { - stream = connection.beforeRemovingStream(stream); - if (stream && !!peer.removeStream) { - peer.removeStream(stream); - } - - localStreams.forEach(function(localStream, index) { - if (streamToRemove == localStream) { - delete localStreams[index]; - } - }); + if (currentTrial++ > maxRetry) { + window.clearInterval(id); + isTimeout = true; + next(isTimeout); } - }); - }); + }, + 10 + ); } - if (connection.DetectRTC.browser.name === 'Firefox') { - peer.removeStream = function(stream) { - stream.mute(); - connection.StreamsHandler.onSyncNeeded(stream.streamid, 'stream-removed'); - }; - } - - peer.onicecandidate = function(event) { - if (!event.candidate) return; - config.onLocalCandidate({ - candidate: event.candidate.candidate, - sdpMid: event.candidate.sdpMid, - sdpMLineIndex: event.candidate.sdpMLineIndex - }); - }; - - var isFirefoxOffered = !isFirefox; - if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered) { - isFirefoxOffered = true; - } - - localStreams.forEach(function(localStream) { - if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) { - return; - } - - if (config.dontAttachLocalStream) { - return; - } - - localStream = connection.beforeAddingStream(localStream); - if (localStream) { - peer.addStream(localStream); - } - }); - - peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() { - var extra = that.extra; - if (connection.peers[that.remoteUserId]) { - extra = connection.peers[that.remoteUserId].extra || extra; - } - - if (!peer) { - return; + function isIE10OrLater(userAgent) { + var ua = userAgent.toLowerCase(); + if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) { + return false; } - - config.onPeerStateChanged({ - iceConnectionState: peer.iceConnectionState, - iceGatheringState: peer.iceGatheringState, - signalingState: peer.signalingState, - extra: extra, - userid: that.remoteUserId - }); - }; - - var sdpConstraints = { - OfferToReceiveAudio: !!localStreams.length, - OfferToReceiveVideo: !!localStreams.length - }; - - if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints; - - defaults.sdpConstraints = setSdpConstraints(sdpConstraints); - - peer.onaddstream = function(event) { - var streamsToShare = {}; - if (config.remoteSdp && config.remoteSdp.streamsToShare) { - streamsToShare = config.remoteSdp.streamsToShare; - } else if (config.streamsToShare) { - streamsToShare = config.streamsToShare; + var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua); + if (match && parseInt(match[1], 10) >= 10) { + return true; } + return false; + } - var streamToShare = streamsToShare[event.stream.id]; - if (streamToShare) { - event.stream.isAudio = streamToShare.isAudio; - event.stream.isVideo = streamToShare.isVideo; - event.stream.isScreen = streamToShare.isScreen; - } + function detectPrivateMode(callback) { + var isPrivate; - event.stream.streamid = event.stream.id; - if (!event.stream.stop) { - event.stream.stop = function() { - if (isFirefox) { - fireEvent(this, 'ended'); + if (window.webkitRequestFileSystem) { + window.webkitRequestFileSystem( + window.TEMPORARY, 1, + function() { + isPrivate = false; + }, + function(e) { + console.log(e); + isPrivate = true; } - }; - } - allRemoteStreams[event.stream.id] = event.stream; - config.onRemoteStream(event.stream); - }; + ); + } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) { + var db; + try { + db = window.indexedDB.open('test'); + } catch (e) { + isPrivate = true; + } - peer.onremovestream = function(event) { - event.stream.streamid = event.stream.id; + if (typeof isPrivate === 'undefined') { + retry( + function isDone() { + return db.readyState === 'done' ? true : false; + }, + function next(isTimeout) { + if (!isTimeout) { + isPrivate = db.result ? false : true; + } + } + ); + } + } else if (isIE10OrLater(window.navigator.userAgent)) { + isPrivate = false; + try { + if (!window.indexedDB) { + isPrivate = true; + } + } catch (e) { + isPrivate = true; + } + } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) { + try { + window.localStorage.setItem('test', 1); + } catch (e) { + isPrivate = true; + } - if (allRemoteStreams[event.stream.id]) { - delete allRemoteStreams[event.stream.id]; + if (typeof isPrivate === 'undefined') { + isPrivate = false; + window.localStorage.removeItem('test'); + } } - config.onRemoteStreamRemoved(event.stream); - }; - - this.addRemoteCandidate = function(remoteCandidate) { - peer.addIceCandidate(new RTCIceCandidate(remoteCandidate)); - }; - - this.addRemoteSdp = function(remoteSdp) { - remoteSdp.sdp = connection.processSdp(remoteSdp.sdp); - peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), function() {}, function(error) { - if (!!connection.enableLogs) { - console.error(JSON.stringify(error, null, '\t')); + retry( + function isDone() { + return typeof isPrivate !== 'undefined' ? true : false; + }, + function next(isTimeout) { + callback(isPrivate); } - }); - }; - - var isOfferer = true; - - if (config.remoteSdp) { - isOfferer = false; - } - - if (connection.session.data === true) { - createDataChannel(); - } - - if (config.remoteSdp) { - if (config.remoteSdp.remotePeerSdpConstraints) { - sdpConstraints = config.remoteSdp.remotePeerSdpConstraints; - } - defaults.sdpConstraints = setSdpConstraints(sdpConstraints); - this.addRemoteSdp(config.remoteSdp); + ); } - function createDataChannel() { - if (!isOfferer) { - peer.ondatachannel = function(event) { - var channel = event.channel; - setChannelEvents(channel); - }; - return; - } + var isMobile = { + Android: function() { + return navigator.userAgent.match(/Android/i); + }, + BlackBerry: function() { + return navigator.userAgent.match(/BlackBerry/i); + }, + iOS: function() { + return navigator.userAgent.match(/iPhone|iPad|iPod/i); + }, + Opera: function() { + return navigator.userAgent.match(/Opera Mini/i); + }, + Windows: function() { + return navigator.userAgent.match(/IEMobile/i); + }, + any: function() { + return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); + }, + getOsName: function() { + var osName = 'Unknown OS'; + if (isMobile.Android()) { + osName = 'Android'; + } - var channel = peer.createDataChannel('RTCDataChannel', {}); - setChannelEvents(channel); - } + if (isMobile.BlackBerry()) { + osName = 'BlackBerry'; + } - function setChannelEvents(channel) { - // force ArrayBuffer in Firefox; which uses "Blob" by default. - channel.binaryType = 'arraybuffer'; + if (isMobile.iOS()) { + osName = 'iOS'; + } - channel.onmessage = function(event) { - config.onDataChannelMessage(event.data); - }; + if (isMobile.Opera()) { + osName = 'Opera Mini'; + } - channel.onopen = function() { - config.onDataChannelOpened(channel); - }; + if (isMobile.Windows()) { + osName = 'Windows'; + } - channel.onerror = function(error) { - config.onDataChannelError(error); - }; + return osName; + } + }; - channel.onclose = function(event) { - config.onDataChannelClosed(event); - }; + // via: http://jsfiddle.net/ChristianL/AVyND/ + function detectDesktopOS() { + var unknown = '-'; - channel.internalSend = channel.send; - channel.send = function(data) { - if (channel.readyState !== 'open') { - return; - } + var nVer = navigator.appVersion; + var nAgt = navigator.userAgent; - channel.internalSend(data); - }; + var os = unknown; + var clientStrings = [{ + s: 'Windows 10', + r: /(Windows 10.0|Windows NT 10.0)/ + }, { + s: 'Windows 8.1', + r: /(Windows 8.1|Windows NT 6.3)/ + }, { + s: 'Windows 8', + r: /(Windows 8|Windows NT 6.2)/ + }, { + s: 'Windows 7', + r: /(Windows 7|Windows NT 6.1)/ + }, { + s: 'Windows Vista', + r: /Windows NT 6.0/ + }, { + s: 'Windows Server 2003', + r: /Windows NT 5.2/ + }, { + s: 'Windows XP', + r: /(Windows NT 5.1|Windows XP)/ + }, { + s: 'Windows 2000', + r: /(Windows NT 5.0|Windows 2000)/ + }, { + s: 'Windows ME', + r: /(Win 9x 4.90|Windows ME)/ + }, { + s: 'Windows 98', + r: /(Windows 98|Win98)/ + }, { + s: 'Windows 95', + r: /(Windows 95|Win95|Windows_95)/ + }, { + s: 'Windows NT 4.0', + r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ + }, { + s: 'Windows CE', + r: /Windows CE/ + }, { + s: 'Windows 3.11', + r: /Win16/ + }, { + s: 'Android', + r: /Android/ + }, { + s: 'Open BSD', + r: /OpenBSD/ + }, { + s: 'Sun OS', + r: /SunOS/ + }, { + s: 'Linux', + r: /(Linux|X11)/ + }, { + s: 'iOS', + r: /(iPhone|iPad|iPod)/ + }, { + s: 'Mac OS X', + r: /Mac OS X/ + }, { + s: 'Mac OS', + r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ + }, { + s: 'QNX', + r: /QNX/ + }, { + s: 'UNIX', + r: /UNIX/ + }, { + s: 'BeOS', + r: /BeOS/ + }, { + s: 'OS/2', + r: /OS\/2/ + }, { + s: 'Search Bot', + r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ + }]; + for (var id in clientStrings) { + var cs = clientStrings[id]; + if (cs.r.test(nAgt)) { + os = cs.s; + break; + } + } - peer.channel = channel; + var osVersion = unknown; + + if (/Windows/.test(os)) { + if (/Windows (.*)/.test(os)) { + osVersion = /Windows (.*)/.exec(os)[1]; + } + os = 'Windows'; + } + + switch (os) { + case 'Mac OS X': + if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) { + osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; + } + break; + case 'Android': + if (/Android ([\.\_\d]+)/.test(nAgt)) { + osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; + } + break; + case 'iOS': + if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) { + osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); + osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); + } + break; + } + + return { + osName: os, + osVersion: osVersion + }; } - if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') { - defaults.sdpConstraints = setSdpConstraints({ - OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio), - OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio) - }); + var osName = 'Unknown OS'; + var osVersion = 'Unknown OS Version'; + + if (isMobile.any()) { + osName = isMobile.getOsName(); + } else { + var osInfo = detectDesktopOS(); + osName = osInfo.osName; + osVersion = osInfo.osVersion; } - var streamsToShare = {}; - peer.getLocalStreams().forEach(function(stream) { - streamsToShare[stream.streamid] = { - isAudio: !!stream.isAudio, - isVideo: !!stream.isVideo, - isScreen: !!stream.isScreen - }; + var isCanvasSupportsStreamCapturing = false; + var isVideoSupportsStreamCapturing = false; + ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) { + if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) { + isCanvasSupportsStreamCapturing = true; + } + + if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) { + isVideoSupportsStreamCapturing = true; + } }); - peer[isOfferer ? 'createOffer' : 'createAnswer'](function(localSdp) { - localSdp.sdp = connection.processSdp(localSdp.sdp); - peer.setLocalDescription(localSdp); - config.onLocalSdp({ - type: localSdp.type, - sdp: localSdp.sdp, - remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, - renegotiatingPeer: !!config.renegotiatingPeer || false, - connectionDescription: that.connectionDescription, - dontGetRemoteStream: !!config.dontGetRemoteStream, - extra: connection ? connection.extra : {}, - streamsToShare: streamsToShare, - isFirefoxOffered: isFirefox - }); - }, function(error) { - if (!!connection.enableLogs) { - console.error('sdp-error', error); + // via: https://github.com/diafygi/webrtc-ips + function DetectLocalIPAddress(callback) { + if (!DetectRTC.isWebRTCSupported) { + return; } - }, defaults.sdpConstraints); - peer.nativeClose = peer.close; - peer.close = function() { - if (!peer) { + if (DetectRTC.isORTCSupported) { return; } - try { - if (peer.iceConnectionState.search(/closed|failed/gi) === -1) { - peer.getRemoteStreams().forEach(function(stream) { - stream.stop(); - }); + getIPs(function(ip) { + //local IPs + if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) { + callback('Local: ' + ip); } - peer.nativeClose(); - } catch (e) {} - peer = null; - that.peer = null; - }; + //assume the rest are public IPs + else { + callback('Public: ' + ip); + } + }); + } - this.peer = peer; - } + //get the IP addresses associated with an account + function getIPs(callback) { + var ipDuplicates = {}; - // ios-hacks.js + //compatibility for firefox and chrome + var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; + var useWebKit = !!window.webkitRTCPeerConnection; - function setCordovaAPIs() { - if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return; - if (!window.device || window.device.platform !== 'iOS') return; + // bypass naive webrtc blocking using an iframe + if (!RTCPeerConnection) { + var iframe = document.getElementById('iframe'); + if (!iframe) { + // + throw 'NOTE: you need to have an iframe in the page right above the script tag.'; + } + var win = iframe.contentWindow; + RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection; + useWebKit = !!win.webkitRTCPeerConnection; + } - var iosrtc = cordova.plugins.iosrtc; - RTCPeerConnection = iosrtc.RTCPeerConnection; - RTCSessionDescription = iosrtc.RTCSessionDescription; - RTCIceCandidate = iosrtc.RTCIceCandidate; - MediaStream = iosrtc.MediaStream; - MediaStreamTrack = iosrtc.MediaStreamTrack; + // if still no RTCPeerConnection then it is not supported by the browser so just return + if (!RTCPeerConnection) { + return; + } - iosrtc.debug.enable('iosrtc*'); - iosrtc.registerGlobals(); - } + //minimal requirements for data connection + var mediaConstraints = { + optional: [{ + RtpDataChannels: true + }] + }; - document.addEventListener('deviceready', setCordovaAPIs, false); - setCordovaAPIs(); + //firefox already has a default stun server in about:config + // media.peerconnection.default_iceservers = + // [{"url": "stun:stun.services.mozilla.com"}] + var servers; - // CodecsHandler.js + //add same stun server for chrome + if (useWebKit) { + servers = { + iceServers: [{ + urls: 'stun:stun.services.mozilla.com' + }] + }; - var CodecsHandler = (function() { - var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); - if (typeof cordova !== 'undefined') { - isMobileDevice = true; - } + if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isFirefox && DetectRTC.browser.version <= 38) { + servers[0] = { + url: servers[0].urls + }; + } + } - if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { - isMobileDevice = true; - } + //construct a new RTCPeerConnection + var pc = new RTCPeerConnection(servers, mediaConstraints); - // "removeVPX" and "removeNonG722" methods are taken from github/mozilla/webrtc-landing - function removeVPX(sdp) { - if (!sdp || typeof sdp !== 'string') { - throw 'Invalid arguments.'; + function handleCandidate(candidate) { + //match just the IP address + var ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/; + var match = ipRegex.exec(candidate); + if (!match) { + console.warn('Could not match IP address in', candidate); + return; + } + var ipAddress = match[1]; + + //remove duplicates + if (ipDuplicates[ipAddress] === undefined) { + callback(ipAddress); + } + + ipDuplicates[ipAddress] = true; } - // this method is NOT reliable + //listen for candidate events + pc.onicecandidate = function(ice) { + //skip non-candidate events + if (ice.candidate) { + handleCandidate(ice.candidate.candidate); + } + }; - sdp = sdp.replace('a=rtpmap:100 VP8/90000\r\n', ''); - sdp = sdp.replace('a=rtpmap:101 VP9/90000\r\n', ''); + //create a bogus data channel + pc.createDataChannel(''); - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g, 'm=video $1 RTP\/SAVPF $2'); - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g, 'm=video $1 RTP\/SAVPF $2'); + //create an offer sdp + pc.createOffer(function(result) { - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); + //trigger the stun server request + pc.setLocalDescription(result, function() {}, function() {}); - sdp = sdp.replace('a=rtcp-fb:120 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:120 nack pli\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:120 ccm fir\r\n', ''); + }, function() {}); - sdp = sdp.replace('a=rtcp-fb:101 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:101 nack pli\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:101 ccm fir\r\n', ''); + //wait for a while to let everything done + setTimeout(function() { + //read candidate info from local description + var lines = pc.localDescription.sdp.split('\n'); - return sdp; + lines.forEach(function(line) { + if (line.indexOf('a=candidate:') === 0) { + handleCandidate(line); + } + }); + }, 1000); } - function disableNACK(sdp) { - if (!sdp || typeof sdp !== 'string') { - throw 'Invalid arguments.'; - } + var MediaDevices = []; - sdp = sdp.replace('a=rtcp-fb:126 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:126 nack pli\r\n', 'a=rtcp-fb:126 pli\r\n'); - sdp = sdp.replace('a=rtcp-fb:97 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:97 nack pli\r\n', 'a=rtcp-fb:97 pli\r\n'); + var audioInputDevices = []; + var audioOutputDevices = []; + var videoInputDevices = []; - return sdp; + if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { + // Firefox 38+ seems having support of enumerateDevices + // Thanks @xdumaine/enumerateDevices + navigator.enumerateDevices = function(callback) { + navigator.mediaDevices.enumerateDevices().then(callback); + }; } - function prioritize(codecMimeType, peer) { - if (!peer || !peer.getSenders || !peer.getSenders().length) { - return; - } - - if (!codecMimeType || typeof codecMimeType !== 'string') { - throw 'Invalid arguments.'; - } + // ---------- Media Devices detection + var canEnumerate = false; - peer.getSenders().forEach(function(sender) { - var params = sender.getParameters(); - for (var i = 0; i < params.codecs.length; i++) { - if (params.codecs[i].mimeType == codecMimeType) { - params.codecs.unshift(params.codecs.splice(i, 1)); - break; - } - } - sender.setParameters(params); - }); + /*global MediaStreamTrack:true */ + if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) { + canEnumerate = true; + } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) { + canEnumerate = true; } - function removeNonG722(sdp) { - return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, 'm=audio $1 RTP\/SAVPF 9'); - } + var hasMicrophone = false; + var hasSpeakers = false; + var hasWebcam = false; - function setBAS(sdp, bandwidth, isScreen) { - if (!bandwidth) { - return sdp; - } + var isWebsiteHasMicrophonePermissions = false; + var isWebsiteHasWebcamPermissions = false; - if (typeof isFirefox !== 'undefined' && isFirefox) { - return sdp; + // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices + // todo: switch to enumerateDevices when landed in canary. + function checkDeviceSupport(callback) { + if (!canEnumerate) { + return; } - if (isMobileDevice) { - return sdp; - } + // This method is useful only for Chrome! - if (isScreen) { - if (!bandwidth.screen) { - console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.'); - } else if (bandwidth.screen < 300) { - console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.'); - } + if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) { + navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack); } - // if screen; must use at least 300kbs - if (bandwidth.screen && isScreen) { - sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); - sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n'); + if (!navigator.enumerateDevices && navigator.enumerateDevices) { + navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator); } - // remove existing bandwidth lines - if (bandwidth.audio || bandwidth.video || bandwidth.data) { - sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + if (!navigator.enumerateDevices) { + if (callback) { + callback(); + } + return; } - if (bandwidth.audio) { - sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n'); - } + MediaDevices = []; - if (bandwidth.video) { - sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + (isScreen ? bandwidth.screen : bandwidth.video) + '\r\n'); - } + audioInputDevices = []; + audioOutputDevices = []; + videoInputDevices = []; - return sdp; - } + navigator.enumerateDevices(function(devices) { + devices.forEach(function(_device) { + var device = {}; + for (var d in _device) { + device[d] = _device[d]; + } - // Find the line in sdpLines that starts with |prefix|, and, if specified, - // contains |substr| (case-insensitive search). - function findLine(sdpLines, prefix, substr) { - return findLineInRange(sdpLines, 0, -1, prefix, substr); - } + // if it is MediaStreamTrack.getSources + if (device.kind === 'audio') { + device.kind = 'audioinput'; + } - // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix| - // and, if specified, contains |substr| (case-insensitive search). - function findLineInRange(sdpLines, startLine, endLine, prefix, substr) { - var realEndLine = endLine !== -1 ? endLine : sdpLines.length; - for (var i = startLine; i < realEndLine; ++i) { - if (sdpLines[i].indexOf(prefix) === 0) { - if (!substr || - sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) { - return i; + if (device.kind === 'video') { + device.kind = 'videoinput'; } - } - } - return null; - } - // Gets the codec payload type from an a=rtpmap:X line. - function getCodecPayloadType(sdpLine) { - var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); - var result = sdpLine.match(pattern); - return (result && result.length === 2) ? result[1] : null; - } + var skip; + MediaDevices.forEach(function(d) { + if (d.id === device.id && d.kind === device.kind) { + skip = true; + } + }); - function setVideoBitrates(sdp, params) { - if (isMobileDevice) { - return sdp; - } + if (skip) { + return; + } - params = params || {}; - var xgoogle_min_bitrate = params.min; - var xgoogle_max_bitrate = params.max; + if (!device.deviceId) { + device.deviceId = device.id; + } - var sdpLines = sdp.split('\r\n'); + if (!device.id) { + device.id = device.deviceId; + } - // VP8 - var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000'); - var vp8Payload; - if (vp8Index) { - vp8Payload = getCodecPayloadType(sdpLines[vp8Index]); - } + if (!device.label) { + device.label = 'Please invoke getUserMedia once.'; + if (location.protocol !== 'https:') { + if (document.domain.search && document.domain.search(/localhost|127.0./g) === -1) { + device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; + } + } + } else { + if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) { + isWebsiteHasWebcamPermissions = true; + } - if (!vp8Payload) { - return sdp; - } + if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) { + isWebsiteHasMicrophonePermissions = true; + } + } - var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000'); - var rtxPayload; - if (rtxIndex) { - rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]); - } + if (device.kind === 'audioinput') { + hasMicrophone = true; - if (!rtxIndex) { - return sdp; - } + if (audioInputDevices.indexOf(device) === -1) { + audioInputDevices.push(device); + } + } - var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString()); - if (rtxFmtpLineIndex !== null) { - var appendrtxNext = '\r\n'; - appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228'); - sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext); - sdp = sdpLines.join('\r\n'); - } + if (device.kind === 'audiooutput') { + hasSpeakers = true; - return sdp; - } + if (audioOutputDevices.indexOf(device) === -1) { + audioOutputDevices.push(device); + } + } - function setOpusAttributes(sdp, params) { - if (isMobileDevice) { - return sdp; - } + if (device.kind === 'videoinput') { + hasWebcam = true; - params = params || {}; + if (videoInputDevices.indexOf(device) === -1) { + videoInputDevices.push(device); + } + } - var sdpLines = sdp.split('\r\n'); + // there is no 'videoouput' in the spec. - // Opus - var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000'); - var opusPayload; - if (opusIndex) { - opusPayload = getCodecPayloadType(sdpLines[opusIndex]); - } + if (MediaDevices.indexOf(device) === -1) { + MediaDevices.push(device); + } + }); - if (!opusPayload) { - return sdp; - } + if (typeof DetectRTC !== 'undefined') { + // to sync latest outputs + DetectRTC.MediaDevices = MediaDevices; + DetectRTC.hasMicrophone = hasMicrophone; + DetectRTC.hasSpeakers = hasSpeakers; + DetectRTC.hasWebcam = hasWebcam; - var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString()); - if (opusFmtpLineIndex === null) { - return sdp; - } + DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; + DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; - var appendOpusNext = ''; - appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1'); - appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1'); + DetectRTC.audioInputDevices = audioInputDevices; + DetectRTC.audioOutputDevices = audioOutputDevices; + DetectRTC.videoInputDevices = videoInputDevices; + } - if (typeof params.maxaveragebitrate != 'undefined') { - appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8); - } + if (callback) { + callback(); + } + }); + } - if (typeof params.maxplaybackrate != 'undefined') { - appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8); - } + // check for microphone/camera support! + checkDeviceSupport(); - if (typeof params.cbr != 'undefined') { - appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1'); - } + var DetectRTC = window.DetectRTC || {}; - if (typeof params.useinbandfec != 'undefined') { - appendOpusNext += '; useinbandfec=' + params.useinbandfec; - } + // ---------- + // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion + DetectRTC.browser = getBrowserInfo(); - if (typeof params.usedtx != 'undefined') { - appendOpusNext += '; usedtx=' + params.usedtx; + detectPrivateMode(function(isPrivateBrowsing) { + DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing; + }); + + // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge + DetectRTC.browser['is' + DetectRTC.browser.name] = true; + + var isNodeWebkit = !!(window.process && (typeof window.process === 'object') && window.process.versions && window.process.versions['node-webkit']); + + // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1. + var isWebRTCSupported = false; + ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) { + if (isWebRTCSupported) { + return; } - if (typeof params.maxptime != 'undefined') { - appendOpusNext += '\r\na=maxptime:' + params.maxptime; + if (item in window) { + isWebRTCSupported = true; } + }); + DetectRTC.isWebRTCSupported = isWebRTCSupported; - sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext); + //------- + DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined'; - sdp = sdpLines.join('\r\n'); - return sdp; + // --------- Detect if system supports screen capturing API + var isScreenCapturingSupported = false; + if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) { + isScreenCapturingSupported = true; + } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) { + isScreenCapturingSupported = true; } - function preferVP9(sdp) { - if (sdp.indexOf('SAVPF 100 101') === -1 || sdp.indexOf('VP9/90000') === -1) { - return sdp; - } - - return sdp.replace('SAVPF 100 101', 'SAVPF 101 100'); + if (location.protocol !== 'https:') { + isScreenCapturingSupported = false; } + DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported; - return { - removeVPX: removeVPX, - disableNACK: disableNACK, - prioritize: prioritize, - removeNonG722: removeNonG722, - setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) { - return setBAS(sdp, bandwidth, isScreen); - }, - setVideoBitrates: function(sdp, params) { - return setVideoBitrates(sdp, params); - }, - setOpusAttributes: function(sdp, params) { - return setOpusAttributes(sdp, params); - }, - preferVP9: preferVP9 + // --------- Detect if WebAudio API are supported + var webAudio = { + isSupported: false, + isCreateMediaStreamSourceSupported: false }; - })(); - - // backward compatibility - window.BandwidthHandler = CodecsHandler; - - // OnIceCandidateHandler.js - - var OnIceCandidateHandler = (function() { - function processCandidates(connection, icePair) { - var candidate = icePair.candidate; - - var iceRestrictions = connection.candidates; - var stun = iceRestrictions.stun; - var turn = iceRestrictions.turn; - - if (!isNull(iceRestrictions.reflexive)) { - stun = iceRestrictions.reflexive; - } - - if (!isNull(iceRestrictions.relay)) { - turn = iceRestrictions.relay; - } - - if (!iceRestrictions.host && !!candidate.match(/typ host/g)) { - return; - } - - if (!turn && !!candidate.match(/typ relay/g)) { - return; - } - if (!stun && !!candidate.match(/typ srflx/g)) { + ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) { + if (webAudio.isSupported) { return; } - var protocol = connection.iceProtocols; + if (item in window) { + webAudio.isSupported = true; - if (!protocol.udp && !!candidate.match(/ udp /g)) { - return; + if ('createMediaStreamSource' in window[item].prototype) { + webAudio.isCreateMediaStreamSourceSupported = true; + } } + }); + DetectRTC.isAudioContextSupported = webAudio.isSupported; + DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported; - if (!protocol.tcp && !!candidate.match(/ tcp /g)) { - return; - } + // ---------- Detect if SCTP/RTP channels are supported. - if (connection.enableLogs) { - console.debug('Your candidate pairs:', candidate); - } + var isRtpDataChannelsSupported = false; + if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) { + isRtpDataChannelsSupported = true; + } + DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported; - return { - candidate: candidate, - sdpMid: icePair.sdpMid, - sdpMLineIndex: icePair.sdpMLineIndex - }; + var isSCTPSupportd = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) { + isSCTPSupportd = true; + } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) { + isSCTPSupportd = true; + } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) { + isSCTPSupportd = true; } + DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd; - return { - processCandidates: processCandidates - }; - })(); + // --------- - // IceServersHandler.js + DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js" - var iceFrame, loadedIceFrame; + // ------ + var isGetUserMediaSupported = false; + if (navigator.getUserMedia) { + isGetUserMediaSupported = true; + } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + isGetUserMediaSupported = true; + } + if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && location.protocol !== 'https:') { + DetectRTC.isGetUserMediaSupported = 'Requires HTTPs'; + } + DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported; - function loadIceFrame(callback, skip) { - if (loadedIceFrame) return; - if (!skip) return loadIceFrame(callback, true); + // ----------- + DetectRTC.osName = osName; + DetectRTC.osVersion = osVersion; - loadedIceFrame = true; + var displayResolution = ''; + if (screen.width) { + var width = (screen.width) ? screen.width : ''; + var height = (screen.height) ? screen.height : ''; + displayResolution += '' + width + ' x ' + height; + } + DetectRTC.displayResolution = displayResolution; - var iframe = document.createElement('iframe'); - iframe.onload = function() { - iframe.isLoaded = true; + // ---------- + DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing; + DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing; - listenEventHandler('message', iFrameLoaderCallback); + // ------ + DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress; - function iFrameLoaderCallback(event) { - if (!event.data || !event.data.iceServers) return; - callback(event.data.iceServers); - window.removeEventListener('message', iFrameLoaderCallback); + DetectRTC.isWebSocketsSupported = 'WebSocket' in window && 2 === window.WebSocket.CLOSING; + DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported; + + DetectRTC.checkWebSocketsSupport = function(callback) { + callback = callback || function() {}; + try { + var websocket = new WebSocket('wss://echo.websocket.org:443/'); + websocket.onopen = function() { + DetectRTC.isWebSocketsBlocked = false; + callback(); + websocket.close(); + websocket = null; + }; + websocket.onerror = function() { + DetectRTC.isWebSocketsBlocked = true; + callback(); + }; + } catch (e) { + DetectRTC.isWebSocketsBlocked = true; + callback(); } + }; - iframe.contentWindow.postMessage('get-ice-servers', '*'); + // ------- + DetectRTC.load = function(callback) { + callback = callback || function() {}; + checkDeviceSupport(callback); }; - iframe.src = 'https://cdn.webrtc-experiment.com/getIceServers/'; - iframe.style.display = 'none'; - (document.body || document.documentElement).appendChild(iframe); - } - if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { - loadIceFrame(function(externalIceServers) { - if (!externalIceServers || !externalIceServers.length) return; - window.RMCExternalIceServers = externalIceServers; + DetectRTC.MediaDevices = MediaDevices; + DetectRTC.hasMicrophone = hasMicrophone; + DetectRTC.hasSpeakers = hasSpeakers; + DetectRTC.hasWebcam = hasWebcam; - if (window.iceServersLoadCallback && typeof window.iceServersLoadCallback === 'function') { - window.iceServersLoadCallback(externalIceServers); - } - }); - } + DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; + DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; - var IceServersHandler = (function() { - function getIceServers(connection) { - var iceServers = []; + DetectRTC.audioInputDevices = audioInputDevices; + DetectRTC.audioOutputDevices = audioOutputDevices; + DetectRTC.videoInputDevices = videoInputDevices; - iceServers.push({ - urls: 'stun:stun.l.google.com:19302' - }, { - urls: 'stun:mmt-stun.verkstad.net' - }, { - urls: 'stun:stun.anyfirewall.com:3478' - }); + // ------ + var isSetSinkIdSupported = false; + if ('setSinkId' in document.createElement('video')) { + isSetSinkIdSupported = true; + } + DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported; - iceServers.push({ - urls: 'turn:turn.bistri.com:80', - credential: 'homeo', - username: 'homeo' - }); + // ----- + var isRTPSenderReplaceTracksSupported = false; + if (DetectRTC.browser.isFirefox /*&& DetectRTC.browser.version > 39*/ ) { + /*global mozRTCPeerConnection:true */ + if ('getSenders' in mozRTCPeerConnection.prototype) { + isRTPSenderReplaceTracksSupported = true; + } + } else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') { + /*global webkitRTCPeerConnection:true */ + if ('getSenders' in webkitRTCPeerConnection.prototype) { + isRTPSenderReplaceTracksSupported = true; + } + } + DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported; - iceServers.push({ - urls: 'turn:turn.anyfirewall.com:443', - credential: 'webrtc', - username: 'webrtc' - }); + //------ + var isRemoteStreamProcessingSupported = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) { + isRemoteStreamProcessingSupported = true; + } + DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported; - // copyright of mmt-turn.verkstad: Ericsson - iceServers.push({ - urls: 'turn:mmt-turn.verkstad.net', - username: 'webrtc', - credential: 'secret' - }); + //------- + var isApplyConstraintsSupported = false; - if (window.RMCExternalIceServers) { - iceServers = window.RMCExternalIceServers.concat(iceServers); - connection.iceServers = iceServers; - } else if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { - window.iceServersLoadCallback = function() { - iceServers = window.RMCExternalIceServers.concat(iceServers); - connection.iceServers = iceServers; - }; - } + /*global MediaStreamTrack:true */ + if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) { + isApplyConstraintsSupported = true; + } + DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported; - return iceServers; + //------- + var isMultiMonitorScreenCapturingSupported = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) { + // version 43 merely supports platforms for multi-monitors + // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing. + isMultiMonitorScreenCapturingSupported = true; } + DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported; - return { - getIceServers: getIceServers - }; - })(); + window.DetectRTC = DetectRTC; - // Last time updated at Fri Jan 08 2016 14:06 + })(); - // gumadapter.js - // https://cdn.webrtc-experiment.com/gumadapter.js + // ios-hacks.js - // getUserMedia hacks from git/webrtc/adapter; - // removed redundant codes - // A-to-Zee, all copyrights goes to: - // https://github.com/webrtc/adapter/blob/master/LICENSE.md + function setCordovaAPIs() { + if (DetectRTC.osName !== 'iOS') return; + if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return; - var getUserMedia = null; - var webrtcDetectedBrowser = null; - var webrtcDetectedVersion = null; - var webrtcMinimumVersion = null; + var iosrtc = cordova.plugins.iosrtc; + window.webkitRTCPeerConnection = iosrtc.RTCPeerConnection; + window.RTCSessionDescription = iosrtc.RTCSessionDescription; + window.RTCIceCandidate = iosrtc.RTCIceCandidate; + window.MediaStream = iosrtc.MediaStream; + window.MediaStreamTrack = iosrtc.MediaStreamTrack; + navigator.getUserMedia = navigator.webkitGetUserMedia = iosrtc.getUserMedia; - var webrtcUtils = window.webrtcUtils || {}; - if (!webrtcUtils.enableLogs) { - webrtcUtils.enableLogs = true; + iosrtc.debug.enable('iosrtc*'); + iosrtc.registerGlobals(); } - if (!webrtcUtils.log) { - webrtcUtils.log = function() { - if (!webrtcUtils.enableLogs) { - return; - } - // suppress console.log output when being included as a module. - if (typeof module !== 'undefined' || - typeof require === 'function' && typeof define === 'function') { - return; - } - console.log.apply(console, arguments); + document.addEventListener('deviceready', setCordovaAPIs, false); + setCordovaAPIs(); + + // RTCPeerConnection.js + + var defaults = {}; + + function setSdpConstraints(config) { + var sdpConstraints; + + var sdpConstraints_mandatory = { + OfferToReceiveAudio: !!config.OfferToReceiveAudio, + OfferToReceiveVideo: !!config.OfferToReceiveVideo }; - } - if (!webrtcUtils.extractVersion) { - webrtcUtils.extractVersion = function(uastring, expr, pos) { - var match = uastring.match(expr); - return match && match.length >= pos && parseInt(match[pos], 10); + sdpConstraints = { + mandatory: sdpConstraints_mandatory, + optional: [{ + VoiceActivityDetection: false + }] }; - } - if (typeof window === 'object') { - if (window.HTMLMediaElement && - !('srcObject' in window.HTMLMediaElement.prototype)) { - // Shim the srcObject property, once, when HTMLMediaElement is found. - Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', { - get: function() { - // If prefixed srcObject property exists, return it. - // Otherwise use the shimmed property, _srcObject - return 'mozSrcObject' in this ? this.mozSrcObject : this._srcObject; - }, - set: function(stream) { - if ('mozSrcObject' in this) { - this.mozSrcObject = stream; - } else { - // Use _srcObject as a private property for this shim - this._srcObject = stream; - // TODO: revokeObjectUrl(this.src) when !stream to release resources? - this.src = stream ? URL.createObjectURL(stream) : null; - } - } - }); + if (!!navigator.mozGetUserMedia && firefoxVersion > 34) { + sdpConstraints = { + OfferToReceiveAudio: !!config.OfferToReceiveAudio, + OfferToReceiveVideo: !!config.OfferToReceiveVideo + }; } - // Proxy existing globals - getUserMedia = window.navigator && window.navigator.getUserMedia; + + return sdpConstraints; } - if (typeof window === 'undefined' || !window.navigator) { - webrtcDetectedBrowser = 'not a browser'; - } else if (navigator.mozGetUserMedia && window.mozRTCPeerConnection) { - webrtcDetectedBrowser = 'firefox'; + var RTCPeerConnection; + if (typeof mozRTCPeerConnection !== 'undefined') { + RTCPeerConnection = mozRTCPeerConnection; + } else if (typeof webkitRTCPeerConnection !== 'undefined') { + RTCPeerConnection = webkitRTCPeerConnection; + } else if (typeof window.RTCPeerConnection !== 'undefined') { + RTCPeerConnection = window.RTCPeerConnection; + } else { + console.error('WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.'); + RTCPeerConnection = window.RTCSessionDescription = window.RTCIceCandidate = function() {}; + } - // the detected firefox version. - webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, - /Firefox\/([0-9]+)\./, 1); + var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription; + var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + var MediaStreamTrack = window.MediaStreamTrack; - // the minimum firefox version still supported by adapter. - webrtcMinimumVersion = 31; + var Plugin = {}; - // getUserMedia constraints shim. - getUserMedia = function(constraints, onSuccess, onError) { - var constraintsToFF37 = function(c) { - if (typeof c !== 'object' || c.require) { - return c; - } - var require = []; - Object.keys(c).forEach(function(key) { - if (key === 'require' || key === 'advanced' || key === 'mediaSource') { - return; - } - var r = c[key] = (typeof c[key] === 'object') ? - c[key] : { - ideal: c[key] - }; - if (r.min !== undefined || - r.max !== undefined || r.exact !== undefined) { - require.push(key); - } - if (r.exact !== undefined) { - if (typeof r.exact === 'number') { - r.min = r.max = r.exact; - } else { - c[key] = r.exact; - } - delete r.exact; + function onPluginRTCInitialized(pluginRTCObject) { + Plugin = pluginRTCObject; + MediaStreamTrack = Plugin.MediaStreamTrack; + RTCPeerConnection = Plugin.RTCPeerConnection; + RTCIceCandidate = Plugin.RTCIceCandidate; + RTCSessionDescription = Plugin.RTCSessionDescription; + } + if (typeof PluginRTC !== 'undefined') onPluginRTCInitialized(PluginRTC); + + function PeerInitiator(config) { + var connection = config.rtcMultiConnection; + + this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra; + this.remoteUserId = config.remoteUserId; + this.streams = []; + this.channels = []; + this.connectionDescription = config.connectionDescription; + + var that = this; + + if (config.remoteSdp) { + this.connectionDescription = config.remoteSdp.connectionDescription; + } + + var allRemoteStreams = {}; + + if (Object.observe) { + var that = this; + Object.observe(this.channels, function(changes) { + changes.forEach(function(change) { + if (change.type === 'add') { + change.object[change.name].addEventListener('close', function() { + delete that.channels[that.channels.indexOf(change.object[change.name])]; + that.channels = removeNullEntries(that.channels); + }, false); } - if (r.ideal !== undefined) { - c.advanced = c.advanced || []; - var oc = {}; - if (typeof r.ideal === 'number') { - oc[key] = { - min: r.ideal, - max: r.ideal - }; - } else { - oc[key] = r.ideal; - } - c.advanced.push(oc); - delete r.ideal; - if (!Object.keys(r).length) { - delete c[key]; + if (change.type === 'remove' || change.type === 'delete') { + if (that.channels.indexOf(change.object[change.name]) !== -1) { + delete that.channels.indexOf(change.object[change.name]); } } + + that.channels = removeNullEntries(that.channels); }); - if (require.length) { - c.require = require; - } - return c; - }; - if (webrtcDetectedVersion < 38) { - webrtcUtils.log('spec: ' + JSON.stringify(constraints)); - if (constraints.audio) { - constraints.audio = constraintsToFF37(constraints.audio); - } - if (constraints.video) { - constraints.video = constraintsToFF37(constraints.video); - } - webrtcUtils.log('ff37: ' + JSON.stringify(constraints)); - } - return navigator.mozGetUserMedia(constraints, onSuccess, onError); - }; + }); + } - navigator.getUserMedia = getUserMedia; + defaults.sdpConstraints = setSdpConstraints({ + OfferToReceiveAudio: true, + OfferToReceiveVideo: true + }); - // Shim for mediaDevices on older versions. - if (!navigator.mediaDevices) { - navigator.mediaDevices = { - getUserMedia: requestUserMedia, - addEventListener: function() {}, - removeEventListener: function() {} - }; + var peer; + + var renegotiatingPeer = !!config.renegotiatingPeer; + if (config.remoteSdp) { + renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer; } - navigator.mediaDevices.enumerateDevices = - navigator.mediaDevices.enumerateDevices || function() { - return new Promise(function(resolve) { - var infos = [{ - kind: 'audioinput', - deviceId: 'default', - label: '', - groupId: '' - }, { - kind: 'videoinput', - deviceId: 'default', - label: '', - groupId: '' - }]; - resolve(infos); + + var localStreams = []; + connection.attachStreams.forEach(function(stream) { + if (!!stream) localStreams.push(stream); + }); + + if (!renegotiatingPeer) { + peer = new RTCPeerConnection(navigator.onLine ? { + iceServers: connection.iceServers, + iceTransports: 'all' + } : null, connection.optionalArgument); + } else { + peer = config.peerRef; + + peer.getLocalStreams().forEach(function(stream) { + localStreams.forEach(function(localStream, index) { + if (stream == localStream) { + delete localStreams[index]; + } }); - }; - if (webrtcDetectedVersion < 41) { - // Work around http://bugzil.la/1169665 - var orgEnumerateDevices = - navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices); - navigator.mediaDevices.enumerateDevices = function() { - return orgEnumerateDevices().then(undefined, function(e) { - if (e.name === 'NotFoundError') { - return []; + connection.removeStreams.forEach(function(streamToRemove, index) { + if (stream === streamToRemove) { + stream = connection.beforeRemovingStream(stream); + if (stream && !!peer.removeStream) { + peer.removeStream(stream); + } + + localStreams.forEach(function(localStream, index) { + if (streamToRemove == localStream) { + delete localStreams[index]; + } + }); } - throw e; }); - }; + }); } - } else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) { - webrtcDetectedBrowser = 'chrome'; + if (connection.DetectRTC.browser.name === 'Firefox') { + peer.removeStream = function(stream) { + stream.mute(); + connection.StreamsHandler.onSyncNeeded(stream.streamid, 'stream-removed'); + }; + } - webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, - /Chrom(e|ium)\/([0-9]+)\./, 2); + peer.onicecandidate = function(event) { + if (!event.candidate) return; + config.onLocalCandidate({ + candidate: event.candidate.candidate, + sdpMid: event.candidate.sdpMid, + sdpMLineIndex: event.candidate.sdpMLineIndex + }); + }; - // the minimum chrome version still supported by adapter. - webrtcMinimumVersion = 38; + var isFirefoxOffered = !isFirefox; + if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered) { + isFirefoxOffered = true; + } - // getUserMedia constraints shim. - var constraintsToChrome = function(c) { - if (typeof c !== 'object' || c.mandatory || c.optional) { - return c; + localStreams.forEach(function(localStream) { + if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) { + return; } - var cc = {}; - Object.keys(c).forEach(function(key) { - if (key === 'require' || key === 'advanced' || key === 'mediaSource') { - return; - } - var r = (typeof c[key] === 'object') ? c[key] : { - ideal: c[key] - }; - if (r.exact !== undefined && typeof r.exact === 'number') { - r.min = r.max = r.exact; - } - var oldname = function(prefix, name) { - if (prefix) { - return prefix + name.charAt(0).toUpperCase() + name.slice(1); - } - return (name === 'deviceId') ? 'sourceId' : name; - }; - if (r.ideal !== undefined) { - cc.optional = cc.optional || []; - var oc = {}; - if (typeof r.ideal === 'number') { - oc[oldname('min', key)] = r.ideal; - cc.optional.push(oc); - oc = {}; - oc[oldname('max', key)] = r.ideal; - cc.optional.push(oc); - } else { - oc[oldname('', key)] = r.ideal; - cc.optional.push(oc); - } - } - if (r.exact !== undefined && typeof r.exact !== 'number') { - cc.mandatory = cc.mandatory || {}; - cc.mandatory[oldname('', key)] = r.exact; - } else { - ['min', 'max'].forEach(function(mix) { - if (r[mix] !== undefined) { - cc.mandatory = cc.mandatory || {}; - cc.mandatory[oldname(mix, key)] = r[mix]; - } - }); - } - }); - if (c.advanced) { - cc.optional = (cc.optional || []).concat(c.advanced); + + if (config.dontAttachLocalStream) { + return; } - return cc; - }; - getUserMedia = function(constraints, onSuccess, onError) { - if (constraints.audio) { - constraints.audio = constraintsToChrome(constraints.audio); + localStream = connection.beforeAddingStream(localStream); + if (localStream) { + peer.addStream(localStream); } - if (constraints.video) { - constraints.video = constraintsToChrome(constraints.video); + }); + + peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() { + var extra = that.extra; + if (connection.peers[that.remoteUserId]) { + extra = connection.peers[that.remoteUserId].extra || extra; } - webrtcUtils.log('chrome: ' + JSON.stringify(constraints)); - return navigator.webkitGetUserMedia(constraints, onSuccess, onError); - }; - navigator.getUserMedia = getUserMedia; - if (!navigator.mediaDevices) { - navigator.mediaDevices = { - getUserMedia: requestUserMedia - }; - } + if (!peer) { + return; + } - // A shim for getUserMedia method on the mediaDevices object. - // TODO(KaptenJansson) remove once implemented in Chrome stable. - if (!navigator.mediaDevices.getUserMedia) { - navigator.mediaDevices.getUserMedia = function(constraints) { - return requestUserMedia(constraints); - }; - } else { - // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia - // function which returns a Promise, it does not accept spec-style - // constraints. - var origGetUserMedia = navigator.mediaDevices.getUserMedia. - bind(navigator.mediaDevices); - navigator.mediaDevices.getUserMedia = function(c) { - webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment - c.audio = constraintsToChrome(c.audio); - c.video = constraintsToChrome(c.video); - webrtcUtils.log('chrome: ' + JSON.stringify(c)); - return origGetUserMedia(c); - }; - } + config.onPeerStateChanged({ + iceConnectionState: peer.iceConnectionState, + iceGatheringState: peer.iceGatheringState, + signalingState: peer.signalingState, + extra: extra, + userid: that.remoteUserId + }); + }; - // Dummy devicechange event methods. - // TODO(KaptenJansson) remove once implemented in Chrome stable. - if (typeof navigator.mediaDevices.addEventListener === 'undefined') { - navigator.mediaDevices.addEventListener = function() { - webrtcUtils.log('Dummy mediaDevices.addEventListener called.'); - }; - } - if (typeof navigator.mediaDevices.removeEventListener === 'undefined') { - navigator.mediaDevices.removeEventListener = function() { - webrtcUtils.log('Dummy mediaDevices.removeEventListener called.'); - }; - } + var sdpConstraints = { + OfferToReceiveAudio: !!localStreams.length, + OfferToReceiveVideo: !!localStreams.length + }; - } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { - webrtcUtils.log('This appears to be Edge'); - webrtcDetectedBrowser = 'edge'; + if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints; - webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2); + defaults.sdpConstraints = setSdpConstraints(sdpConstraints); - // the minimum version still supported by adapter. - webrtcMinimumVersion = 12; - } else { - webrtcUtils.log('Browser does not appear to be WebRTC-capable'); - } + peer.onaddstream = function(event) { + var streamsToShare = {}; + if (config.remoteSdp && config.remoteSdp.streamsToShare) { + streamsToShare = config.remoteSdp.streamsToShare; + } else if (config.streamsToShare) { + streamsToShare = config.streamsToShare; + } - // Returns the result of getUserMedia as a Promise. - function requestUserMedia(constraints) { - return new Promise(function(resolve, reject) { - getUserMedia(constraints, resolve, reject); - }); - } + var streamToShare = streamsToShare[event.stream.id]; + if (streamToShare) { + event.stream.isAudio = streamToShare.isAudio; + event.stream.isVideo = streamToShare.isVideo; + event.stream.isScreen = streamToShare.isScreen; + } - if (typeof module !== 'undefined') { - module.exports = { - getUserMedia: getUserMedia, - webrtcDetectedBrowser: webrtcDetectedBrowser, - webrtcDetectedVersion: webrtcDetectedVersion, - webrtcMinimumVersion: webrtcMinimumVersion, - webrtcUtils: webrtcUtils + event.stream.streamid = event.stream.id; + if (!event.stream.stop) { + event.stream.stop = function() { + if (isFirefox) { + fireEvent(this, 'ended'); + } + }; + } + allRemoteStreams[event.stream.id] = event.stream; + config.onRemoteStream(event.stream); }; - } else if ((typeof require === 'function') && (typeof define === 'function')) { - // Expose objects and functions when RequireJS is doing the loading. - define([], function() { - return { - getUserMedia: getUserMedia, - webrtcDetectedBrowser: webrtcDetectedBrowser, - webrtcDetectedVersion: webrtcDetectedVersion, - webrtcMinimumVersion: webrtcMinimumVersion, - webrtcUtils: webrtcUtils - }; - }); - } - - // getUserMediaHandler.js - - if (typeof webrtcUtils !== 'undefined') { - webrtcUtils.enableLogs = false; - } - - function setStreamType(constraints, stream) { - if (constraints.mandatory && constraints.mandatory.chromeMediaSource) { - stream.isScreen = true; - } else if (constraints.mozMediaSource || constraints.mediaSource) { - stream.isScreen = true; - } else if (constraints.video) { - stream.isVideo = true; - } else if (constraints.audio) { - stream.isAudio = true; - } - } - var currentUserMediaRequest = { - streams: [], - mutex: false, - queueRequests: [], - remove: function(idInstance) { - this.mutex = false; + peer.onremovestream = function(event) { + event.stream.streamid = event.stream.id; - var stream = this.streams[idInstance]; - if (!stream) { - return; + if (allRemoteStreams[event.stream.id]) { + delete allRemoteStreams[event.stream.id]; } - stream = stream.stream; + config.onRemoteStreamRemoved(event.stream); + }; - var options = stream.currentUserMediaRequestOptions; + this.addRemoteCandidate = function(remoteCandidate) { + peer.addIceCandidate(new RTCIceCandidate(remoteCandidate)); + }; - if (this.queueRequests.indexOf(options)) { - delete this.queueRequests[this.queueRequests.indexOf(options)]; - this.queueRequests = removeNullEntries(this.queueRequests); - } + this.addRemoteSdp = function(remoteSdp) { + remoteSdp.sdp = connection.processSdp(remoteSdp.sdp); + peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), function() {}, function(error) { + if (!!connection.enableLogs) { + console.error(JSON.stringify(error, null, '\t')); + } + }); + }; - this.streams[idInstance].stream = null; - delete this.streams[idInstance]; + var isOfferer = true; + + if (config.remoteSdp) { + isOfferer = false; } - }; - function getUserMediaHandler(options) { - if (currentUserMediaRequest.mutex === true) { - currentUserMediaRequest.queueRequests.push(options); - return; + if (connection.session.data === true) { + createDataChannel(); } - currentUserMediaRequest.mutex = true; - // easy way to match - var idInstance = JSON.stringify(options.localMediaConstraints); + if (config.remoteSdp) { + if (config.remoteSdp.remotePeerSdpConstraints) { + sdpConstraints = config.remoteSdp.remotePeerSdpConstraints; + } + defaults.sdpConstraints = setSdpConstraints(sdpConstraints); + this.addRemoteSdp(config.remoteSdp); + } - function streaming(stream, returnBack) { - setStreamType(options.localMediaConstraints, stream); - options.onGettingLocalMedia(stream, returnBack); + function createDataChannel() { + if (!isOfferer) { + peer.ondatachannel = function(event) { + var channel = event.channel; + setChannelEvents(channel); + }; + return; + } - stream.addEventListener('ended', function() { - delete currentUserMediaRequest.streams[idInstance]; + var channel = peer.createDataChannel('RTCDataChannel', {}); + setChannelEvents(channel); + } - currentUserMediaRequest.mutex = false; - if (currentUserMediaRequest.queueRequests.indexOf(options)) { - delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)]; - currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests); - } - }, false); + function setChannelEvents(channel) { + // force ArrayBuffer in Firefox; which uses "Blob" by default. + channel.binaryType = 'arraybuffer'; - currentUserMediaRequest.streams[idInstance] = { - stream: stream + channel.onmessage = function(event) { + config.onDataChannelMessage(event.data); }; - currentUserMediaRequest.mutex = false; - if (currentUserMediaRequest.queueRequests.length) { - getUserMediaHandler(currentUserMediaRequest.queueRequests.shift()); - } - } + channel.onopen = function() { + config.onDataChannelOpened(channel); + }; - if (currentUserMediaRequest.streams[idInstance]) { - streaming(currentUserMediaRequest.streams[idInstance].stream, true); - } else { - if (isPluginRTC) { - var mediaElement = document.createElement('video'); - Plugin.getUserMedia({ - audio: true, - video: true - }, function(stream) { - stream.streamid = stream.id || getRandomString(); - streaming(stream); - }, function(error) {}); + channel.onerror = function(error) { + config.onDataChannelError(error); + }; - return; - } + channel.onclose = function(event) { + config.onDataChannelClosed(event); + }; - if (typeof DetectRTC !== 'undefined') { - if (!DetectRTC.hasMicrophone) { - options.localMediaConstraints.audio = false; + channel.internalSend = channel.send; + channel.send = function(data) { + if (channel.readyState !== 'open') { + return; } - if (!DetectRTC.hasWebcam) { - options.localMediaConstraints.video = false; - } - } + channel.internalSend(data); + }; - navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream) { - stream.streamid = stream.streamid || stream.id || getRandomString(); - stream.idInstance = idInstance; - streaming(stream); - }).catch(function(error) { - options.onLocalMediaError(error, options.localMediaConstraints); - }); + peer.channel = channel; } - } - // StreamsHandler.js + if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') { + defaults.sdpConstraints = setSdpConstraints({ + OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio), + OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio) + }); + } - var StreamsHandler = (function() { - function handleType(type) { - if (!type) { - return; - } + var streamsToShare = {}; + peer.getLocalStreams().forEach(function(stream) { + streamsToShare[stream.streamid] = { + isAudio: !!stream.isAudio, + isVideo: !!stream.isVideo, + isScreen: !!stream.isScreen + }; + }); - if (typeof type === 'string' || typeof type === 'undefined') { - return type; + peer[isOfferer ? 'createOffer' : 'createAnswer'](function(localSdp) { + localSdp.sdp = connection.processSdp(localSdp.sdp); + peer.setLocalDescription(localSdp); + config.onLocalSdp({ + type: localSdp.type, + sdp: localSdp.sdp, + remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, + renegotiatingPeer: !!config.renegotiatingPeer || false, + connectionDescription: that.connectionDescription, + dontGetRemoteStream: !!config.dontGetRemoteStream, + extra: connection ? connection.extra : {}, + streamsToShare: streamsToShare, + isFirefoxOffered: isFirefox + }); + }, function(error) { + if (!!connection.enableLogs) { + console.error('sdp-error', error); } + }, defaults.sdpConstraints); - if (type.audio && type.video) { - return null; + peer.nativeClose = peer.close; + peer.close = function() { + if (!peer) { + return; } - if (type.audio) { - return 'audio'; - } + try { + if (peer.iceConnectionState.search(/closed|failed/gi) === -1) { + peer.getRemoteStreams().forEach(function(stream) { + stream.stop(); + }); + } + peer.nativeClose(); + } catch (e) {} - if (type.video) { - return 'video'; - } + peer = null; + that.peer = null; + }; - return; - } + this.peer = peer; + } - function setHandlers(stream, syncAction, connection) { - if (typeof syncAction == 'undefined' || syncAction == true) { - stream.addEventListener('ended', function() { - StreamsHandler.onSyncNeeded(this.streamid, 'ended'); - }, false); - } + // CodecsHandler.js - stream.mute = function(type, isSyncAction) { - type = handleType(type); + var CodecsHandler = (function() { + var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); + if (typeof cordova !== 'undefined') { + isMobileDevice = true; + } - if (typeof isSyncAction !== 'undefined') { - syncAction = isSyncAction; - } + if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { + isMobileDevice = true; + } - if (typeof type == 'undefined' || type == 'audio') { - stream.getAudioTracks().forEach(function(track) { - track.enabled = false; - connection.streamEvents[stream.streamid].isAudioMuted = true; - }); - } + // "removeVPX" and "removeNonG722" methods are taken from github/mozilla/webrtc-landing + function removeVPX(sdp) { + if (!sdp || typeof sdp !== 'string') { + throw 'Invalid arguments.'; + } - if (typeof type == 'undefined' || type == 'video') { - stream.getVideoTracks().forEach(function(track) { - track.enabled = false; - }); - } + // this method is NOT reliable - if (typeof syncAction == 'undefined' || syncAction == true) { - StreamsHandler.onSyncNeeded(stream.streamid, 'mute', type); - } + sdp = sdp.replace('a=rtpmap:100 VP8/90000\r\n', ''); + sdp = sdp.replace('a=rtpmap:101 VP9/90000\r\n', ''); - connection.streamEvents[stream.streamid].muteType = type || 'both'; + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g, 'm=video $1 RTP\/SAVPF $2'); + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g, 'm=video $1 RTP\/SAVPF $2'); - fireEvent(stream, 'mute', type); - }; + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); - stream.unmute = function(type, isSyncAction) { - type = handleType(type); + sdp = sdp.replace('a=rtcp-fb:120 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:120 nack pli\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:120 ccm fir\r\n', ''); - if (typeof isSyncAction !== 'undefined') { - syncAction = isSyncAction; - } + sdp = sdp.replace('a=rtcp-fb:101 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:101 nack pli\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:101 ccm fir\r\n', ''); - graduallyIncreaseVolume(); + return sdp; + } - if (typeof type == 'undefined' || type == 'audio') { - stream.getAudioTracks().forEach(function(track) { - track.enabled = true; - connection.streamEvents[stream.streamid].isAudioMuted = false; - }); - } + function disableNACK(sdp) { + if (!sdp || typeof sdp !== 'string') { + throw 'Invalid arguments.'; + } - if (typeof type == 'undefined' || type == 'video') { - stream.getVideoTracks().forEach(function(track) { - track.enabled = true; - }); + sdp = sdp.replace('a=rtcp-fb:126 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:126 nack pli\r\n', 'a=rtcp-fb:126 pli\r\n'); + sdp = sdp.replace('a=rtcp-fb:97 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:97 nack pli\r\n', 'a=rtcp-fb:97 pli\r\n'); - // make sure that video unmute doesn't affects audio - if (typeof type !== 'undefined' && type == 'video' && connection.streamEvents[stream.streamid].isAudioMuted) { - (function looper(times) { - if (!times) { - times = 0; - } + return sdp; + } - times++; + function prioritize(codecMimeType, peer) { + if (!peer || !peer.getSenders || !peer.getSenders().length) { + return; + } - // check until five-seconds - if (times < 100 && connection.streamEvents[stream.streamid].isAudioMuted) { - stream.mute('audio'); + if (!codecMimeType || typeof codecMimeType !== 'string') { + throw 'Invalid arguments.'; + } - setTimeout(function() { - looper(times); - }, 50); - } - })(); + peer.getSenders().forEach(function(sender) { + var params = sender.getParameters(); + for (var i = 0; i < params.codecs.length; i++) { + if (params.codecs[i].mimeType == codecMimeType) { + params.codecs.unshift(params.codecs.splice(i, 1)); + break; } } + sender.setParameters(params); + }); + } - if (typeof syncAction == 'undefined' || syncAction == true) { - StreamsHandler.onSyncNeeded(stream.streamid, 'unmute', type); - } + function removeNonG722(sdp) { + return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, 'm=audio $1 RTP\/SAVPF 9'); + } - connection.streamEvents[stream.streamid].unmuteType = type || 'both'; + function setBAS(sdp, bandwidth, isScreen) { + if (!bandwidth) { + return sdp; + } - fireEvent(stream, 'unmute', type); - }; + if (typeof isFirefox !== 'undefined' && isFirefox) { + return sdp; + } - function graduallyIncreaseVolume() { - if (!connection.streamEvents[stream.streamid].mediaElement) { - return; + if (isMobileDevice) { + return sdp; + } + + if (isScreen) { + if (!bandwidth.screen) { + console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.'); + } else if (bandwidth.screen < 300) { + console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.'); } + } - var mediaElement = connection.streamEvents[stream.streamid].mediaElement; - mediaElement.volume = 0; - afterEach(200, 5, function() { - mediaElement.volume += .20; - }); + // if screen; must use at least 300kbs + if (bandwidth.screen && isScreen) { + sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n'); } - } - function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) { - startedTimes = (startedTimes || 0) + 1; - if (startedTimes >= numberOfTimes) return; + // remove existing bandwidth lines + if (bandwidth.audio || bandwidth.video || bandwidth.data) { + sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + } - setTimeout(function() { - callback(); - afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes); - }, setTimeoutInteval); - } + if (bandwidth.audio) { + sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n'); + } - return { - setHandlers: setHandlers, - onSyncNeeded: function(streamid, action, type) {} - }; - })(); + if (bandwidth.video) { + sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + (isScreen ? bandwidth.screen : bandwidth.video) + '\r\n'); + } - // Last time updated at Monday, January 4th, 2016, 1:17:50 PM + return sdp; + } - // Latest file can be found here: https://cdn.webrtc-experiment.com/DetectRTC.js + // Find the line in sdpLines that starts with |prefix|, and, if specified, + // contains |substr| (case-insensitive search). + function findLine(sdpLines, prefix, substr) { + return findLineInRange(sdpLines, 0, -1, prefix, substr); + } - // Muaz Khan - www.MuazKhan.com - // MIT License - www.WebRTC-Experiment.com/licence - // Documentation - github.com/muaz-khan/DetectRTC - // ____________ - // DetectRTC.js - - // DetectRTC.hasWebcam (has webcam device!) - // DetectRTC.hasMicrophone (has microphone device!) - // DetectRTC.hasSpeakers (has speakers!) - - (function() { - - 'use strict'; - - var navigator = window.navigator; - - if (typeof navigator !== 'undefined') { - if (typeof navigator.webkitGetUserMedia !== 'undefined') { - navigator.getUserMedia = navigator.webkitGetUserMedia; - } - - if (typeof navigator.mozGetUserMedia !== 'undefined') { - navigator.getUserMedia = navigator.mozGetUserMedia; + // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix| + // and, if specified, contains |substr| (case-insensitive search). + function findLineInRange(sdpLines, startLine, endLine, prefix, substr) { + var realEndLine = endLine !== -1 ? endLine : sdpLines.length; + for (var i = startLine; i < realEndLine; ++i) { + if (sdpLines[i].indexOf(prefix) === 0) { + if (!substr || + sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) { + return i; + } + } } - } else { - navigator = { - getUserMedia: function() {}, - userAgent: 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45' - }; + return null; } - var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); - var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob); + // Gets the codec payload type from an a=rtpmap:X line. + function getCodecPayloadType(sdpLine) { + var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); + var result = sdpLine.match(pattern); + return (result && result.length === 2) ? result[1] : null; + } - var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; - var isFirefox = typeof window.InstallTrigger !== 'undefined'; - var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; - var isChrome = !!window.chrome && !isOpera; - var isIE = !!document.documentMode && !isEdge; + function setVideoBitrates(sdp, params) { + if (isMobileDevice) { + return sdp; + } - // this one can also be used: - // https://www.websocket.org/js/stuff.js (DetectBrowser.js) + params = params || {}; + var xgoogle_min_bitrate = params.min; + var xgoogle_max_bitrate = params.max; - function getBrowserInfo() { - var nVer = navigator.appVersion; - var nAgt = navigator.userAgent; - var browserName = navigator.appName; - var fullVersion = '' + parseFloat(navigator.appVersion); - var majorVersion = parseInt(navigator.appVersion, 10); - var nameOffset, verOffset, ix; + var sdpLines = sdp.split('\r\n'); - // In Opera, the true version is after 'Opera' or after 'Version' - if (isOpera) { - browserName = 'Opera'; - try { - fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0]; - majorVersion = fullVersion.split('.')[0]; - } catch (e) { - fullVersion = '0.0.0.0'; - majorVersion = 0; - } + // VP8 + var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000'); + var vp8Payload; + if (vp8Index) { + vp8Payload = getCodecPayloadType(sdpLines[vp8Index]); } - // In MSIE, the true version is after 'MSIE' in userAgent - else if (isIE) { - verOffset = nAgt.indexOf('MSIE'); - browserName = 'IE'; - fullVersion = nAgt.substring(verOffset + 5); + + if (!vp8Payload) { + return sdp; } - // In Chrome, the true version is after 'Chrome' - else if (isChrome) { - verOffset = nAgt.indexOf('Chrome'); - browserName = 'Chrome'; - fullVersion = nAgt.substring(verOffset + 7); + + var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000'); + var rtxPayload; + if (rtxIndex) { + rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]); } - // In Safari, the true version is after 'Safari' or after 'Version' - else if (isSafari) { - verOffset = nAgt.indexOf('Safari'); - browserName = 'Safari'; - fullVersion = nAgt.substring(verOffset + 7); - if ((verOffset = nAgt.indexOf('Version')) !== -1) { - fullVersion = nAgt.substring(verOffset + 8); - } + if (!rtxIndex) { + return sdp; } - // In Firefox, the true version is after 'Firefox' - else if (isFirefox) { - verOffset = nAgt.indexOf('Firefox'); - browserName = 'Firefox'; - fullVersion = nAgt.substring(verOffset + 8); + + var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString()); + if (rtxFmtpLineIndex !== null) { + var appendrtxNext = '\r\n'; + appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228'); + sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext); + sdp = sdpLines.join('\r\n'); } - // In most other browsers, 'name/version' is at the end of userAgent - else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) { - browserName = nAgt.substring(nameOffset, verOffset); - fullVersion = nAgt.substring(verOffset + 1); + return sdp; + } - if (browserName.toLowerCase() === browserName.toUpperCase()) { - browserName = navigator.appName; - } + function setOpusAttributes(sdp, params) { + if (isMobileDevice) { + return sdp; } - if (isEdge) { - browserName = 'Edge'; - // fullVersion = navigator.userAgent.split('Edge/')[1]; - fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString(); + params = params || {}; + + var sdpLines = sdp.split('\r\n'); + + // Opus + var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000'); + var opusPayload; + if (opusIndex) { + opusPayload = getCodecPayloadType(sdpLines[opusIndex]); } - // trim the fullVersion string at semicolon/space if present - if ((ix = fullVersion.indexOf(';')) !== -1) { - fullVersion = fullVersion.substring(0, ix); + if (!opusPayload) { + return sdp; } - if ((ix = fullVersion.indexOf(' ')) !== -1) { - fullVersion = fullVersion.substring(0, ix); + var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString()); + if (opusFmtpLineIndex === null) { + return sdp; } - majorVersion = parseInt('' + fullVersion, 10); + var appendOpusNext = ''; + appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1'); + appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1'); - if (isNaN(majorVersion)) { - fullVersion = '' + parseFloat(navigator.appVersion); - majorVersion = parseInt(navigator.appVersion, 10); + if (typeof params.maxaveragebitrate != 'undefined') { + appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8); } - return { - fullVersion: fullVersion, - version: majorVersion, - name: browserName, - isPrivateBrowsing: false - }; - } - - // via: https://gist.github.com/cou929/7973956 + if (typeof params.maxplaybackrate != 'undefined') { + appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8); + } - function retry(isDone, next) { - var currentTrial = 0, - maxRetry = 50, - interval = 10, - isTimeout = false; - var id = window.setInterval( - function() { - if (isDone()) { - window.clearInterval(id); - next(isTimeout); - } - if (currentTrial++ > maxRetry) { - window.clearInterval(id); - isTimeout = true; - next(isTimeout); - } - }, - 10 - ); - } + if (typeof params.cbr != 'undefined') { + appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1'); + } - function isIE10OrLater(userAgent) { - var ua = userAgent.toLowerCase(); - if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) { - return false; + if (typeof params.useinbandfec != 'undefined') { + appendOpusNext += '; useinbandfec=' + params.useinbandfec; } - var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua); - if (match && parseInt(match[1], 10) >= 10) { - return true; + + if (typeof params.usedtx != 'undefined') { + appendOpusNext += '; usedtx=' + params.usedtx; } - return false; - } - function detectPrivateMode(callback) { - var isPrivate; + if (typeof params.maxptime != 'undefined') { + appendOpusNext += '\r\na=maxptime:' + params.maxptime; + } - if (window.webkitRequestFileSystem) { - window.webkitRequestFileSystem( - window.TEMPORARY, 1, - function() { - isPrivate = false; - }, - function(e) { - console.log(e); - isPrivate = true; - } - ); - } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) { - var db; - try { - db = window.indexedDB.open('test'); - } catch (e) { - isPrivate = true; - } + sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext); - if (typeof isPrivate === 'undefined') { - retry( - function isDone() { - return db.readyState === 'done' ? true : false; - }, - function next(isTimeout) { - if (!isTimeout) { - isPrivate = db.result ? false : true; - } - } - ); - } - } else if (isIE10OrLater(window.navigator.userAgent)) { - isPrivate = false; - try { - if (!window.indexedDB) { - isPrivate = true; - } - } catch (e) { - isPrivate = true; - } - } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) { - try { - window.localStorage.setItem('test', 1); - } catch (e) { - isPrivate = true; - } + sdp = sdpLines.join('\r\n'); + return sdp; + } - if (typeof isPrivate === 'undefined') { - isPrivate = false; - window.localStorage.removeItem('test'); - } + function preferVP9(sdp) { + if (sdp.indexOf('SAVPF 100 101') === -1 || sdp.indexOf('VP9/90000') === -1) { + return sdp; } - retry( - function isDone() { - return typeof isPrivate !== 'undefined' ? true : false; - }, - function next(isTimeout) { - callback(isPrivate); - } - ); + return sdp.replace('SAVPF 100 101', 'SAVPF 101 100'); } - var isMobile = { - Android: function() { - return navigator.userAgent.match(/Android/i); - }, - BlackBerry: function() { - return navigator.userAgent.match(/BlackBerry/i); - }, - iOS: function() { - return navigator.userAgent.match(/iPhone|iPad|iPod/i); - }, - Opera: function() { - return navigator.userAgent.match(/Opera Mini/i); + return { + removeVPX: removeVPX, + disableNACK: disableNACK, + prioritize: prioritize, + removeNonG722: removeNonG722, + setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) { + return setBAS(sdp, bandwidth, isScreen); }, - Windows: function() { - return navigator.userAgent.match(/IEMobile/i); + setVideoBitrates: function(sdp, params) { + return setVideoBitrates(sdp, params); }, - any: function() { - return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); + setOpusAttributes: function(sdp, params) { + return setOpusAttributes(sdp, params); }, - getOsName: function() { - var osName = 'Unknown OS'; - if (isMobile.Android()) { - osName = 'Android'; - } - - if (isMobile.BlackBerry()) { - osName = 'BlackBerry'; - } - - if (isMobile.iOS()) { - osName = 'iOS'; - } - - if (isMobile.Opera()) { - osName = 'Opera Mini'; - } - - if (isMobile.Windows()) { - osName = 'Windows'; - } - - return osName; - } + preferVP9: preferVP9 }; + })(); - // via: http://jsfiddle.net/ChristianL/AVyND/ - function detectDesktopOS() { - var unknown = '-'; + // backward compatibility + window.BandwidthHandler = CodecsHandler; - var nVer = navigator.appVersion; - var nAgt = navigator.userAgent; + // OnIceCandidateHandler.js - var os = unknown; - var clientStrings = [{ - s: 'Windows 10', - r: /(Windows 10.0|Windows NT 10.0)/ - }, { - s: 'Windows 8.1', - r: /(Windows 8.1|Windows NT 6.3)/ - }, { - s: 'Windows 8', - r: /(Windows 8|Windows NT 6.2)/ - }, { - s: 'Windows 7', - r: /(Windows 7|Windows NT 6.1)/ - }, { - s: 'Windows Vista', - r: /Windows NT 6.0/ - }, { - s: 'Windows Server 2003', - r: /Windows NT 5.2/ - }, { - s: 'Windows XP', - r: /(Windows NT 5.1|Windows XP)/ - }, { - s: 'Windows 2000', - r: /(Windows NT 5.0|Windows 2000)/ - }, { - s: 'Windows ME', - r: /(Win 9x 4.90|Windows ME)/ - }, { - s: 'Windows 98', - r: /(Windows 98|Win98)/ - }, { - s: 'Windows 95', - r: /(Windows 95|Win95|Windows_95)/ - }, { - s: 'Windows NT 4.0', - r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ - }, { - s: 'Windows CE', - r: /Windows CE/ - }, { - s: 'Windows 3.11', - r: /Win16/ - }, { - s: 'Android', - r: /Android/ - }, { - s: 'Open BSD', - r: /OpenBSD/ - }, { - s: 'Sun OS', - r: /SunOS/ - }, { - s: 'Linux', - r: /(Linux|X11)/ - }, { - s: 'iOS', - r: /(iPhone|iPad|iPod)/ - }, { - s: 'Mac OS X', - r: /Mac OS X/ - }, { - s: 'Mac OS', - r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ - }, { - s: 'QNX', - r: /QNX/ - }, { - s: 'UNIX', - r: /UNIX/ - }, { - s: 'BeOS', - r: /BeOS/ - }, { - s: 'OS/2', - r: /OS\/2/ - }, { - s: 'Search Bot', - r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ - }]; - for (var id in clientStrings) { - var cs = clientStrings[id]; - if (cs.r.test(nAgt)) { - os = cs.s; - break; - } + var OnIceCandidateHandler = (function() { + function processCandidates(connection, icePair) { + var candidate = icePair.candidate; + + var iceRestrictions = connection.candidates; + var stun = iceRestrictions.stun; + var turn = iceRestrictions.turn; + + if (!isNull(iceRestrictions.reflexive)) { + stun = iceRestrictions.reflexive; } - var osVersion = unknown; + if (!isNull(iceRestrictions.relay)) { + turn = iceRestrictions.relay; + } - if (/Windows/.test(os)) { - osVersion = /Windows (.*)/.exec(os)[1]; - os = 'Windows'; + if (!iceRestrictions.host && !!candidate.match(/typ host/g)) { + return; } - switch (os) { - case 'Mac OS X': - osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; - break; + if (!turn && !!candidate.match(/typ relay/g)) { + return; + } - case 'Android': - osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; - break; + if (!stun && !!candidate.match(/typ srflx/g)) { + return; + } - case 'iOS': - osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); - osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); - break; + var protocol = connection.iceProtocols; + + if (!protocol.udp && !!candidate.match(/ udp /g)) { + return; + } + + if (!protocol.tcp && !!candidate.match(/ tcp /g)) { + return; + } + + if (connection.enableLogs) { + console.debug('Your candidate pairs:', candidate); } return { - osName: os, - osVersion: osVersion + candidate: candidate, + sdpMid: icePair.sdpMid, + sdpMLineIndex: icePair.sdpMLineIndex }; } - var osName = 'Unknown OS'; - var osVersion = 'Unknown OS Version'; + return { + processCandidates: processCandidates + }; + })(); - if (isMobile.any()) { - osName = isMobile.getOsName(); - } else { - var osInfo = detectDesktopOS(); - osName = osInfo.osName; - osVersion = osInfo.osVersion; - } + // IceServersHandler.js - var isCanvasSupportsStreamCapturing = false; - var isVideoSupportsStreamCapturing = false; - ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) { - if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) { - isCanvasSupportsStreamCapturing = true; - } + var iceFrame, loadedIceFrame; - if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) { - isVideoSupportsStreamCapturing = true; - } - }); + function loadIceFrame(callback, skip) { + if (loadedIceFrame) return; + if (!skip) return loadIceFrame(callback, true); - // via: https://github.com/diafygi/webrtc-ips - function DetectLocalIPAddress(callback) { - if (!DetectRTC.isWebRTCSupported) { - return; + loadedIceFrame = true; + + var iframe = document.createElement('iframe'); + iframe.onload = function() { + iframe.isLoaded = true; + + listenEventHandler('message', iFrameLoaderCallback); + + function iFrameLoaderCallback(event) { + if (!event.data || !event.data.iceServers) return; + callback(event.data.iceServers); + window.removeEventListener('message', iFrameLoaderCallback); } - if (DetectRTC.isORTCSupported) { - return; + iframe.contentWindow.postMessage('get-ice-servers', '*'); + }; + iframe.src = 'https://cdn.webrtc-experiment.com/getIceServers/'; + iframe.style.display = 'none'; + (document.body || document.documentElement).appendChild(iframe); + } + + if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { + loadIceFrame(function(externalIceServers) { + if (!externalIceServers || !externalIceServers.length) return; + window.RMCExternalIceServers = externalIceServers; + + if (window.iceServersLoadCallback && typeof window.iceServersLoadCallback === 'function') { + window.iceServersLoadCallback(externalIceServers); } + }); + } - getIPs(function(ip) { - //local IPs - if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) { - callback('Local: ' + ip); - } + var IceServersHandler = (function() { + function getIceServers(connection) { + var iceServers = []; - //assume the rest are public IPs - else { - callback('Public: ' + ip); - } + iceServers.push({ + urls: 'stun:stun.l.google.com:19302' + }, { + urls: 'stun:mmt-stun.verkstad.net' + }, { + urls: 'stun:stun.anyfirewall.com:3478' }); - } - //get the IP addresses associated with an account - function getIPs(callback) { - var ipDuplicates = {}; + iceServers.push({ + urls: 'turn:turn.bistri.com:80', + credential: 'homeo', + username: 'homeo' + }); - //compatibility for firefox and chrome - var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; - var useWebKit = !!window.webkitRTCPeerConnection; + iceServers.push({ + urls: 'turn:turn.anyfirewall.com:443', + credential: 'webrtc', + username: 'webrtc' + }); - // bypass naive webrtc blocking using an iframe - if (!RTCPeerConnection) { - var iframe = document.getElementById('iframe'); - if (!iframe) { - // - throw 'NOTE: you need to have an iframe in the page right above the script tag.'; - } - var win = iframe.contentWindow; - RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection; - useWebKit = !!win.webkitRTCPeerConnection; - } + // copyright of mmt-turn.verkstad: Ericsson + iceServers.push({ + urls: 'turn:mmt-turn.verkstad.net', + username: 'webrtc', + credential: 'secret' + }); - // if still no RTCPeerConnection then it is not supported by the browser so just return - if (!RTCPeerConnection) { - return; + if (window.RMCExternalIceServers) { + iceServers = window.RMCExternalIceServers.concat(iceServers); + connection.iceServers = iceServers; + } else if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { + window.iceServersLoadCallback = function() { + iceServers = window.RMCExternalIceServers.concat(iceServers); + connection.iceServers = iceServers; + }; } - //minimal requirements for data connection - var mediaConstraints = { - optional: [{ - RtpDataChannels: true - }] - }; + return iceServers; + } - //firefox already has a default stun server in about:config - // media.peerconnection.default_iceservers = - // [{"url": "stun:stun.services.mozilla.com"}] - var servers; + return { + getIceServers: getIceServers + }; + })(); - //add same stun server for chrome - if (useWebKit) { - servers = { - iceServers: [{ - urls: 'stun:stun.services.mozilla.com' - }] - }; + // Last time updated at Fri Jan 08 2016 14:06 - if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isFirefox && DetectRTC.browser.version <= 38) { - servers[0] = { - url: servers[0].urls - }; - } + // gumadapter.js + // https://cdn.webrtc-experiment.com/gumadapter.js + + // getUserMedia hacks from git/webrtc/adapter; + // removed redundant codes + // A-to-Zee, all copyrights goes to: + // https://github.com/webrtc/adapter/blob/master/LICENSE.md + + var getUserMedia = null; + var webrtcDetectedBrowser = null; + var webrtcDetectedVersion = null; + var webrtcMinimumVersion = null; + + var webrtcUtils = window.webrtcUtils || {}; + if (!webrtcUtils.enableLogs) { + webrtcUtils.enableLogs = true; + } + if (!webrtcUtils.log) { + webrtcUtils.log = function() { + if (!webrtcUtils.enableLogs) { + return; } - //construct a new RTCPeerConnection - var pc = new RTCPeerConnection(servers, mediaConstraints); + // suppress console.log output when being included as a module. + if (typeof module !== 'undefined' || + typeof require === 'function' && typeof define === 'function') { + return; + } + console.log.apply(console, arguments); + }; + } - function handleCandidate(candidate) { - //match just the IP address - var ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/; - var match = ipRegex.exec(candidate); - if (!match) { - console.warn('Could not match IP address in', candidate); - return; + if (!webrtcUtils.extractVersion) { + webrtcUtils.extractVersion = function(uastring, expr, pos) { + var match = uastring.match(expr); + return match && match.length >= pos && parseInt(match[pos], 10); + }; + } + + if (typeof window === 'object') { + if (window.HTMLMediaElement && + !('srcObject' in window.HTMLMediaElement.prototype)) { + // Shim the srcObject property, once, when HTMLMediaElement is found. + Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', { + get: function() { + // If prefixed srcObject property exists, return it. + // Otherwise use the shimmed property, _srcObject + return 'mozSrcObject' in this ? this.mozSrcObject : this._srcObject; + }, + set: function(stream) { + if ('mozSrcObject' in this) { + this.mozSrcObject = stream; + } else { + // Use _srcObject as a private property for this shim + this._srcObject = stream; + // TODO: revokeObjectUrl(this.src) when !stream to release resources? + this.src = stream ? URL.createObjectURL(stream) : null; + } } - var ipAddress = match[1]; + }); + } + // Proxy existing globals + getUserMedia = window.navigator && window.navigator.getUserMedia; + } - //remove duplicates - if (ipDuplicates[ipAddress] === undefined) { - callback(ipAddress); - } + if (typeof window === 'undefined' || !window.navigator) { + webrtcDetectedBrowser = 'not a browser'; + } else if (navigator.mozGetUserMedia && window.mozRTCPeerConnection) { + webrtcDetectedBrowser = 'firefox'; - ipDuplicates[ipAddress] = true; - } + // the detected firefox version. + webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, + /Firefox\/([0-9]+)\./, 1); - //listen for candidate events - pc.onicecandidate = function(ice) { - //skip non-candidate events - if (ice.candidate) { - handleCandidate(ice.candidate.candidate); + // the minimum firefox version still supported by adapter. + webrtcMinimumVersion = 31; + + // getUserMedia constraints shim. + getUserMedia = function(constraints, onSuccess, onError) { + var constraintsToFF37 = function(c) { + if (typeof c !== 'object' || c.require) { + return c; + } + var require = []; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = c[key] = (typeof c[key] === 'object') ? + c[key] : { + ideal: c[key] + }; + if (r.min !== undefined || + r.max !== undefined || r.exact !== undefined) { + require.push(key); + } + if (r.exact !== undefined) { + if (typeof r.exact === 'number') { + r.min = r.max = r.exact; + } else { + c[key] = r.exact; + } + delete r.exact; + } + if (r.ideal !== undefined) { + c.advanced = c.advanced || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[key] = { + min: r.ideal, + max: r.ideal + }; + } else { + oc[key] = r.ideal; + } + c.advanced.push(oc); + delete r.ideal; + if (!Object.keys(r).length) { + delete c[key]; + } + } + }); + if (require.length) { + c.require = require; } + return c; }; + if (webrtcDetectedVersion < 38) { + webrtcUtils.log('spec: ' + JSON.stringify(constraints)); + if (constraints.audio) { + constraints.audio = constraintsToFF37(constraints.audio); + } + if (constraints.video) { + constraints.video = constraintsToFF37(constraints.video); + } + webrtcUtils.log('ff37: ' + JSON.stringify(constraints)); + } + return navigator.mozGetUserMedia(constraints, onSuccess, onError); + }; - //create a bogus data channel - pc.createDataChannel(''); - - //create an offer sdp - pc.createOffer(function(result) { - - //trigger the stun server request - pc.setLocalDescription(result, function() {}, function() {}); - - }, function() {}); + navigator.getUserMedia = getUserMedia; - //wait for a while to let everything done - setTimeout(function() { - //read candidate info from local description - var lines = pc.localDescription.sdp.split('\n'); + // Shim for mediaDevices on older versions. + if (!navigator.mediaDevices) { + navigator.mediaDevices = { + getUserMedia: requestUserMedia, + addEventListener: function() {}, + removeEventListener: function() {} + }; + } + navigator.mediaDevices.enumerateDevices = + navigator.mediaDevices.enumerateDevices || function() { + return new Promise(function(resolve) { + var infos = [{ + kind: 'audioinput', + deviceId: 'default', + label: '', + groupId: '' + }, { + kind: 'videoinput', + deviceId: 'default', + label: '', + groupId: '' + }]; + resolve(infos); + }); + }; - lines.forEach(function(line) { - if (line.indexOf('a=candidate:') === 0) { - handleCandidate(line); + if (webrtcDetectedVersion < 41) { + // Work around http://bugzil.la/1169665 + var orgEnumerateDevices = + navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices); + navigator.mediaDevices.enumerateDevices = function() { + return orgEnumerateDevices().then(undefined, function(e) { + if (e.name === 'NotFoundError') { + return []; } + throw e; }); - }, 1000); - } - - var MediaDevices = []; - - var audioInputDevices = []; - var audioOutputDevices = []; - var videoInputDevices = []; - - if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { - // Firefox 38+ seems having support of enumerateDevices - // Thanks @xdumaine/enumerateDevices - navigator.enumerateDevices = function(callback) { - navigator.mediaDevices.enumerateDevices().then(callback); }; } - // ---------- Media Devices detection - var canEnumerate = false; - - /*global MediaStreamTrack:true */ - if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) { - canEnumerate = true; - } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) { - canEnumerate = true; - } + } else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) { + webrtcDetectedBrowser = 'chrome'; - var hasMicrophone = false; - var hasSpeakers = false; - var hasWebcam = false; + webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, + /Chrom(e|ium)\/([0-9]+)\./, 2); - var isWebsiteHasMicrophonePermissions = false; - var isWebsiteHasWebcamPermissions = false; + // the minimum chrome version still supported by adapter. + webrtcMinimumVersion = 38; - // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices - // todo: switch to enumerateDevices when landed in canary. - function checkDeviceSupport(callback) { - if (!canEnumerate) { - return; + // getUserMedia constraints shim. + var constraintsToChrome = function(c) { + if (typeof c !== 'object' || c.mandatory || c.optional) { + return c; } - - // This method is useful only for Chrome! - - if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) { - navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack); + var cc = {}; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = (typeof c[key] === 'object') ? c[key] : { + ideal: c[key] + }; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + var oldname = function(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return (name === 'deviceId') ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname('', key)] = r.exact; + } else { + ['min', 'max'].forEach(function(mix) { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname(mix, key)] = r[mix]; + } + }); + } + }); + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); } + return cc; + }; - if (!navigator.enumerateDevices && navigator.enumerateDevices) { - navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator); + getUserMedia = function(constraints, onSuccess, onError) { + if (constraints.audio) { + constraints.audio = constraintsToChrome(constraints.audio); } - - if (!navigator.enumerateDevices) { - if (callback) { - callback(); - } - return; + if (constraints.video) { + constraints.video = constraintsToChrome(constraints.video); } + webrtcUtils.log('chrome: ' + JSON.stringify(constraints)); + return navigator.webkitGetUserMedia(constraints, onSuccess, onError); + }; + navigator.getUserMedia = getUserMedia; - MediaDevices = []; - - audioInputDevices = []; - audioOutputDevices = []; - videoInputDevices = []; + if (!navigator.mediaDevices) { + navigator.mediaDevices = { + getUserMedia: requestUserMedia + }; + } - navigator.enumerateDevices(function(devices) { - devices.forEach(function(_device) { - var device = {}; - for (var d in _device) { - device[d] = _device[d]; - } + // A shim for getUserMedia method on the mediaDevices object. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (!navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia = function(constraints) { + return requestUserMedia(constraints); + }; + } else { + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + var origGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment + c.audio = constraintsToChrome(c.audio); + c.video = constraintsToChrome(c.video); + webrtcUtils.log('chrome: ' + JSON.stringify(c)); + return origGetUserMedia(c); + }; + } - // if it is MediaStreamTrack.getSources - if (device.kind === 'audio') { - device.kind = 'audioinput'; - } + // Dummy devicechange event methods. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (typeof navigator.mediaDevices.addEventListener === 'undefined') { + navigator.mediaDevices.addEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.addEventListener called.'); + }; + } + if (typeof navigator.mediaDevices.removeEventListener === 'undefined') { + navigator.mediaDevices.removeEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.removeEventListener called.'); + }; + } - if (device.kind === 'video') { - device.kind = 'videoinput'; - } + } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { + webrtcUtils.log('This appears to be Edge'); + webrtcDetectedBrowser = 'edge'; - var skip; - MediaDevices.forEach(function(d) { - if (d.id === device.id && d.kind === device.kind) { - skip = true; - } - }); + webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2); - if (skip) { - return; - } + // the minimum version still supported by adapter. + webrtcMinimumVersion = 12; + } else { + webrtcUtils.log('Browser does not appear to be WebRTC-capable'); + } - if (!device.deviceId) { - device.deviceId = device.id; - } + // Returns the result of getUserMedia as a Promise. + function requestUserMedia(constraints) { + return new Promise(function(resolve, reject) { + getUserMedia(constraints, resolve, reject); + }); + } - if (!device.id) { - device.id = device.deviceId; - } + if (typeof module !== 'undefined') { + module.exports = { + getUserMedia: getUserMedia, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcUtils: webrtcUtils + }; + } else if ((typeof require === 'function') && (typeof define === 'function')) { + // Expose objects and functions when RequireJS is doing the loading. + define([], function() { + return { + getUserMedia: getUserMedia, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcUtils: webrtcUtils + }; + }); + } - if (!device.label) { - device.label = 'Please invoke getUserMedia once.'; - if (location.protocol !== 'https:') { - device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; - } - } else { - if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) { - isWebsiteHasWebcamPermissions = true; - } + // getUserMediaHandler.js - if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) { - isWebsiteHasMicrophonePermissions = true; - } - } + if (typeof webrtcUtils !== 'undefined') { + webrtcUtils.enableLogs = false; + } - if (device.kind === 'audioinput') { - hasMicrophone = true; + function setStreamType(constraints, stream) { + if (constraints.mandatory && constraints.mandatory.chromeMediaSource) { + stream.isScreen = true; + } else if (constraints.mozMediaSource || constraints.mediaSource) { + stream.isScreen = true; + } else if (constraints.video) { + stream.isVideo = true; + } else if (constraints.audio) { + stream.isAudio = true; + } + } - if (audioInputDevices.indexOf(device) === -1) { - audioInputDevices.push(device); - } - } + var currentUserMediaRequest = { + streams: [], + mutex: false, + queueRequests: [], + remove: function(idInstance) { + this.mutex = false; - if (device.kind === 'audiooutput') { - hasSpeakers = true; + var stream = this.streams[idInstance]; + if (!stream) { + return; + } - if (audioOutputDevices.indexOf(device) === -1) { - audioOutputDevices.push(device); - } - } + stream = stream.stream; - if (device.kind === 'videoinput') { - hasWebcam = true; + var options = stream.currentUserMediaRequestOptions; - if (videoInputDevices.indexOf(device) === -1) { - videoInputDevices.push(device); - } - } + if (this.queueRequests.indexOf(options)) { + delete this.queueRequests[this.queueRequests.indexOf(options)]; + this.queueRequests = removeNullEntries(this.queueRequests); + } - // there is no 'videoouput' in the spec. + this.streams[idInstance].stream = null; + delete this.streams[idInstance]; + } + }; - if (MediaDevices.indexOf(device) === -1) { - MediaDevices.push(device); - } - }); + function getUserMediaHandler(options) { + if (currentUserMediaRequest.mutex === true) { + currentUserMediaRequest.queueRequests.push(options); + return; + } + currentUserMediaRequest.mutex = true; - if (typeof DetectRTC !== 'undefined') { - // to sync latest outputs - DetectRTC.MediaDevices = MediaDevices; - DetectRTC.hasMicrophone = hasMicrophone; - DetectRTC.hasSpeakers = hasSpeakers; - DetectRTC.hasWebcam = hasWebcam; + // easy way to match + var idInstance = JSON.stringify(options.localMediaConstraints); - DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; - DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; + function streaming(stream, returnBack) { + setStreamType(options.localMediaConstraints, stream); + options.onGettingLocalMedia(stream, returnBack); - DetectRTC.audioInputDevices = audioInputDevices; - DetectRTC.audioOutputDevices = audioOutputDevices; - DetectRTC.videoInputDevices = videoInputDevices; - } + stream.addEventListener('ended', function() { + delete currentUserMediaRequest.streams[idInstance]; - if (callback) { - callback(); + currentUserMediaRequest.mutex = false; + if (currentUserMediaRequest.queueRequests.indexOf(options)) { + delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)]; + currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests); } - }); - } - - // check for microphone/camera support! - checkDeviceSupport(); + }, false); - var DetectRTC = window.DetectRTC || {}; + currentUserMediaRequest.streams[idInstance] = { + stream: stream + }; + currentUserMediaRequest.mutex = false; - // ---------- - // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion - DetectRTC.browser = getBrowserInfo(); + if (currentUserMediaRequest.queueRequests.length) { + getUserMediaHandler(currentUserMediaRequest.queueRequests.shift()); + } + } - detectPrivateMode(function(isPrivateBrowsing) { - DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing; - }); + if (currentUserMediaRequest.streams[idInstance]) { + streaming(currentUserMediaRequest.streams[idInstance].stream, true); + } else { + if (isPluginRTC) { + var mediaElement = document.createElement('video'); + Plugin.getUserMedia({ + audio: true, + video: true + }, function(stream) { + stream.streamid = stream.id || getRandomString(); + streaming(stream); + }, function(error) {}); - // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge - DetectRTC.browser['is' + DetectRTC.browser.name] = true; + return; + } - var isNodeWebkit = !!(window.process && (typeof window.process === 'object') && window.process.versions && window.process.versions['node-webkit']); + navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream) { + stream.streamid = stream.streamid || stream.id || getRandomString(); + stream.idInstance = idInstance; + streaming(stream); + }).catch(function(error) { + options.onLocalMediaError(error, options.localMediaConstraints); + }); + } + } - // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1. - var isWebRTCSupported = false; - ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) { - if (isWebRTCSupported) { + // StreamsHandler.js + + var StreamsHandler = (function() { + function handleType(type) { + if (!type) { return; } - if (item in window) { - isWebRTCSupported = true; + if (typeof type === 'string' || typeof type === 'undefined') { + return type; } - }); - DetectRTC.isWebRTCSupported = isWebRTCSupported; - //------- - DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined'; + if (type.audio && type.video) { + return null; + } - // --------- Detect if system supports screen capturing API - var isScreenCapturingSupported = false; - if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) { - isScreenCapturingSupported = true; - } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) { - isScreenCapturingSupported = true; - } + if (type.audio) { + return 'audio'; + } - if (location.protocol !== 'https:') { - isScreenCapturingSupported = false; - } - DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported; + if (type.video) { + return 'video'; + } - // --------- Detect if WebAudio API are supported - var webAudio = { - isSupported: false, - isCreateMediaStreamSourceSupported: false - }; + return; + } - ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) { - if (webAudio.isSupported) { - return; + function setHandlers(stream, syncAction, connection) { + if (typeof syncAction == 'undefined' || syncAction == true) { + stream.addEventListener('ended', function() { + StreamsHandler.onSyncNeeded(this.streamid, 'ended'); + }, false); } - if (item in window) { - webAudio.isSupported = true; + stream.mute = function(type, isSyncAction) { + type = handleType(type); - if ('createMediaStreamSource' in window[item].prototype) { - webAudio.isCreateMediaStreamSourceSupported = true; + if (typeof isSyncAction !== 'undefined') { + syncAction = isSyncAction; } - } - }); - DetectRTC.isAudioContextSupported = webAudio.isSupported; - DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported; - // ---------- Detect if SCTP/RTP channels are supported. + if (typeof type == 'undefined' || type == 'audio') { + stream.getAudioTracks().forEach(function(track) { + track.enabled = false; + connection.streamEvents[stream.streamid].isAudioMuted = true; + }); + } - var isRtpDataChannelsSupported = false; - if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) { - isRtpDataChannelsSupported = true; - } - DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported; + if (typeof type == 'undefined' || type == 'video') { + stream.getVideoTracks().forEach(function(track) { + track.enabled = false; + }); + } - var isSCTPSupportd = false; - if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) { - isSCTPSupportd = true; - } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) { - isSCTPSupportd = true; - } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) { - isSCTPSupportd = true; - } - DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd; + if (typeof syncAction == 'undefined' || syncAction == true) { + StreamsHandler.onSyncNeeded(stream.streamid, 'mute', type); + } - // --------- + connection.streamEvents[stream.streamid].muteType = type || 'both'; - DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js" + fireEvent(stream, 'mute', type); + }; - // ------ - var isGetUserMediaSupported = false; - if (navigator.getUserMedia) { - isGetUserMediaSupported = true; - } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - isGetUserMediaSupported = true; - } - if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && location.protocol !== 'https:') { - DetectRTC.isGetUserMediaSupported = 'Requires HTTPs'; - } - DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported; + stream.unmute = function(type, isSyncAction) { + type = handleType(type); - // ----------- - DetectRTC.osName = osName; - DetectRTC.osVersion = osVersion; + if (typeof isSyncAction !== 'undefined') { + syncAction = isSyncAction; + } - var displayResolution = ''; - if (screen.width) { - var width = (screen.width) ? screen.width : ''; - var height = (screen.height) ? screen.height : ''; - displayResolution += '' + width + ' x ' + height; - } - DetectRTC.displayResolution = displayResolution; + graduallyIncreaseVolume(); - // ---------- - DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing; - DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing; + if (typeof type == 'undefined' || type == 'audio') { + stream.getAudioTracks().forEach(function(track) { + track.enabled = true; + connection.streamEvents[stream.streamid].isAudioMuted = false; + }); + } - // ------ - DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress; + if (typeof type == 'undefined' || type == 'video') { + stream.getVideoTracks().forEach(function(track) { + track.enabled = true; + }); - DetectRTC.isWebSocketsSupported = 'WebSocket' in window && 2 === window.WebSocket.CLOSING; - DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported; + // make sure that video unmute doesn't affects audio + if (typeof type !== 'undefined' && type == 'video' && connection.streamEvents[stream.streamid].isAudioMuted) { + (function looper(times) { + if (!times) { + times = 0; + } - DetectRTC.checkWebSocketsSupport = function(callback) { - callback = callback || function() {}; - try { - var websocket = new WebSocket('wss://echo.websocket.org:443/'); - websocket.onopen = function() { - DetectRTC.isWebSocketsBlocked = false; - callback(); - websocket.close(); - websocket = null; - }; - websocket.onerror = function() { - DetectRTC.isWebSocketsBlocked = true; - callback(); - }; - } catch (e) { - DetectRTC.isWebSocketsBlocked = true; - callback(); - } - }; + times++; - // ------- - DetectRTC.load = function(callback) { - callback = callback || function() {}; - checkDeviceSupport(callback); - }; + // check until five-seconds + if (times < 100 && connection.streamEvents[stream.streamid].isAudioMuted) { + stream.mute('audio'); - DetectRTC.MediaDevices = MediaDevices; - DetectRTC.hasMicrophone = hasMicrophone; - DetectRTC.hasSpeakers = hasSpeakers; - DetectRTC.hasWebcam = hasWebcam; + setTimeout(function() { + looper(times); + }, 50); + } + })(); + } + } - DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; - DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; + if (typeof syncAction == 'undefined' || syncAction == true) { + StreamsHandler.onSyncNeeded(stream.streamid, 'unmute', type); + } - DetectRTC.audioInputDevices = audioInputDevices; - DetectRTC.audioOutputDevices = audioOutputDevices; - DetectRTC.videoInputDevices = videoInputDevices; + connection.streamEvents[stream.streamid].unmuteType = type || 'both'; - // ------ - var isSetSinkIdSupported = false; - if ('setSinkId' in document.createElement('video')) { - isSetSinkIdSupported = true; - } - DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported; + fireEvent(stream, 'unmute', type); + }; - // ----- - var isRTPSenderReplaceTracksSupported = false; - if (DetectRTC.browser.isFirefox /*&& DetectRTC.browser.version > 39*/ ) { - /*global mozRTCPeerConnection:true */ - if ('getSenders' in mozRTCPeerConnection.prototype) { - isRTPSenderReplaceTracksSupported = true; - } - } else if (DetectRTC.browser.isChrome) { - /*global webkitRTCPeerConnection:true */ - if ('getSenders' in webkitRTCPeerConnection.prototype) { - isRTPSenderReplaceTracksSupported = true; - } - } - DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported; + function graduallyIncreaseVolume() { + if (!connection.streamEvents[stream.streamid].mediaElement) { + return; + } - //------ - var isRemoteStreamProcessingSupported = false; - if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) { - isRemoteStreamProcessingSupported = true; + var mediaElement = connection.streamEvents[stream.streamid].mediaElement; + mediaElement.volume = 0; + afterEach(200, 5, function() { + mediaElement.volume += .20; + }); + } } - DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported; - //------- - var isApplyConstraintsSupported = false; - - /*global MediaStreamTrack:true */ - if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) { - isApplyConstraintsSupported = true; - } - DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported; + function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) { + startedTimes = (startedTimes || 0) + 1; + if (startedTimes >= numberOfTimes) return; - //------- - var isMultiMonitorScreenCapturingSupported = false; - if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) { - // version 43 merely supports platforms for multi-monitors - // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing. - isMultiMonitorScreenCapturingSupported = true; + setTimeout(function() { + callback(); + afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes); + }, setTimeoutInteval); } - DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported; - - window.DetectRTC = DetectRTC; + return { + setHandlers: setHandlers, + onSyncNeeded: function(streamid, action, type) {} + }; })(); // Last time updated at Oct 24, 2015, 08:32:23 diff --git a/RTCMultiConnection.min.js b/RTCMultiConnection.min.js index 8a7710f7..f0f89c25 100644 --- a/RTCMultiConnection.min.js +++ b/RTCMultiConnection.min.js @@ -1,6 +1,6 @@ -// Last time updated: 2016-02-25 12:41:18 PM UTC +// Last time updated: 2016-03-02 9:37:17 AM UTC "use strict";!function(){function RTCMultiConnection(roomid,forceOptions){function onUserLeft(remoteUserId){connection.deletePeer(remoteUserId)}function connectSocket(connectCallback){if(socket)return void(connectCallback&&connectCallback(socket));if("undefined"==typeof SocketConnection)if("undefined"!=typeof FirebaseConnection)window.SocketConnection=FirebaseConnection;else{if("undefined"==typeof PubNubConnection)throw"SocketConnection.js seems missed.";window.SocketConnection=PubNubConnection}socket=new SocketConnection(connection,function(s){socket=s,connectCallback&&connectCallback(socket)})}function beforeUnload(shiftModerationControlOnLeave,dontCloseSocket){connection.closeBeforeUnload&&(connection.isInitiator===!0&&connection.dontMakeMeModerator(),connection.peers.getAllParticipants().forEach(function(participant){mPeer.onNegotiationNeeded({userLeft:!0},participant),connection.peers[participant]&&connection.peers[participant].peer&&connection.peers[participant].peer.close()}),dontCloseSocket||connection.closeSocket(),connection.broadcasters=[],connection.isInitiator=!1)}function applyConstraints(stream,mediaConstraints){return stream?(mediaConstraints.audio&&stream.getAudioTracks().forEach(function(track){track.applyConstraints(mediaConstraints.audio)}),void(mediaConstraints.video&&stream.getVideoTracks().forEach(function(track){track.applyConstraints(mediaConstraints.video)}))):void(connection.enableLogs&&console.error("No stream to applyConstraints."))}function replaceTrack(track,remoteUserId,isVideoTrack){return remoteUserId?void mPeer.replaceTrack(track,remoteUserId,isVideoTrack):void connection.peers.getAllParticipants().forEach(function(participant){mPeer.replaceTrack(track,participant,isVideoTrack)})}function setStreamEndHandler(stream,isRemote){isRemote=!!isRemote,stream.alreadySetEndHandler||(stream.alreadySetEndHandler=!0,stream&&stream.addEventListener&&stream.addEventListener("ended",function(){stream.idInstance&¤tUserMediaRequest.remove(stream.idInstance),isRemote||(delete connection.attachStreams[connection.attachStreams.indexOf(stream)],-1===connection.removeStreams.indexOf(stream)&&connection.removeStreams.push(stream),connection.attachStreams=removeNullEntries(connection.attachStreams),connection.removeStreams=removeNullEntries(connection.removeStreams),connection.observers.all());var streamEvent=connection.streamEvents[stream.streamid];streamEvent||(streamEvent={stream:stream,streamid:stream.streamid,type:isRemote?"remote":"local",userid:connection.userid,extra:connection.extra,mediaElement:connection.streamEvents[stream.streamid]?connection.streamEvents[stream.streamid].mediaElement:null}),(streamEvent.userid!==connection.userid||"remote"!==streamEvent.type)&&(connection.onstreamended(streamEvent),delete connection.streamEvents[stream.streamid])},!1))}function keepNextBroadcasterOnServer(){if(connection.isInitiator&&!connection.session.oneway&&!connection.session.broadcast&&"many-to-many"===connection.direction){var firstBroadcaster=connection.broadcasters[0],otherBroadcasters=[];connection.broadcasters.forEach(function(broadcaster){broadcaster!==firstBroadcaster&&otherBroadcasters.push(broadcaster)}),connection.autoCloseEntireSession||connection.shiftModerationControl(firstBroadcaster,otherBroadcasters,!0)}}forceOptions=forceOptions||{};var connection=this;connection.channel=connection.sessionid=(roomid||location.href.replace(/\/|:|#|\?|\$|\^|%|\.|`|~|!|\+|@|\[|\||]|\|*. /g,"").split("\n").join("").split("\r").join(""))+"";var mPeer=new MultiPeers(connection);mPeer.onGettingLocalMedia=function(stream){stream.type="local",setStreamEndHandler(stream),getRMCMediaElement(stream,function(mediaElement){mediaElement.id=stream.streamid,mediaElement.muted=!0,mediaElement.volume=0,-1===connection.attachStreams.indexOf(stream)&&connection.attachStreams.push(stream),"undefined"!=typeof StreamsHandler&&StreamsHandler.setHandlers(stream,!0,connection),connection.streamEvents[stream.streamid]={stream:stream,type:"local",mediaElement:mediaElement,userid:connection.userid,extra:connection.extra,streamid:stream.streamid,blobURL:mediaElement.src||URL.createObjectURL(stream),isAudioMuted:!0},setHarkEvents(connection,connection.streamEvents[stream.streamid]),setMuteHandlers(connection,connection.streamEvents[stream.streamid]),connection.onstream(connection.streamEvents[stream.streamid])},connection),connection.observers.all()},mPeer.onGettingRemoteMedia=function(stream,remoteUserId){stream.type="remote",setStreamEndHandler(stream,"remote-stream"),getRMCMediaElement(stream,function(mediaElement){mediaElement.id=stream.streamid,"undefined"!=typeof StreamsHandler&&StreamsHandler.setHandlers(stream,!1,connection),connection.streamEvents[stream.streamid]={stream:stream,type:"remote",userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},mediaElement:mediaElement,streamid:stream.streamid,blobURL:mediaElement.src||URL.createObjectURL(stream)},setMuteHandlers(connection,connection.streamEvents[stream.streamid]),connection.onstream(connection.streamEvents[stream.streamid])},connection)},mPeer.onRemovingRemoteMedia=function(stream,remoteUserId){var streamEvent=connection.streamEvents[stream.streamid];streamEvent||(streamEvent={stream:stream,type:"remote",userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},streamid:stream.streamid,mediaElement:connection.streamEvents[stream.streamid]?connection.streamEvents[stream.streamid].mediaElement:null}),connection.onstreamended(streamEvent),delete connection.streamEvents[stream.streamid]},mPeer.onNegotiationNeeded=function(message,remoteUserId,callback){connectSocket(function(){socket.emit(connection.socketMessageEvent,"password"in message?message:{remoteUserId:message.remoteUserId||remoteUserId,message:message,sender:connection.userid},callback||function(){})})},mPeer.onUserLeft=onUserLeft,mPeer.disconnectWith=function(remoteUserId,callback){socket&&socket.emit("disconnect-with",remoteUserId,callback||function(){}),connection.deletePeer(remoteUserId)},connection.broadcasters=[],connection.socketOptions={transport:"polling"};var socket;connection.openOrJoin=function(localUserid,password){connection.checkPresence(localUserid,function(isRoomExists,roomid){if("function"==typeof password&&(password(isRoomExists,roomid),password=null),isRoomExists){connection.sessionid=roomid;var localPeerSdpConstraints=!1,remotePeerSdpConstraints=!1,isOneWay=!!connection.session.oneway,isDataOnly=isData(connection.session);remotePeerSdpConstraints={OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},localPeerSdpConstraints={OfferToReceiveAudio:isOneWay?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:isOneWay?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo};var connectionDescription={remoteUserId:connection.sessionid,message:{newParticipationRequest:!0,isOneWay:isOneWay,isDataOnly:isDataOnly,localPeerSdpConstraints:localPeerSdpConstraints,remotePeerSdpConstraints:remotePeerSdpConstraints},sender:connection.userid,password:password||!1};return void mPeer.onNegotiationNeeded(connectionDescription)}connection.userid;connection.userid=connection.sessionid=localUserid||connection.sessionid,connection.userid+="",socket.emit("changed-uuid",connection.userid),password&&socket.emit("set-password",password),connection.isInitiator=!0,isData(connection.session)||connection.captureUserMedia()})},connection.open=function(localUserid,isPublicModerator){connection.userid;return connection.userid=connection.sessionid=localUserid||connection.sessionid,connection.userid+="",connection.isInitiator=!0,connectSocket(function(){socket.emit("changed-uuid",connection.userid),1==isPublicModerator&&connection.becomePublicModerator()}),isData(connection.session)?void("function"==typeof isPublicModerator&&isPublicModerator()):void connection.captureUserMedia("function"==typeof isPublicModerator?isPublicModerator:null)},connection.becomePublicModerator=function(){connection.isInitiator&&socket.emit("become-a-public-moderator")},connection.dontMakeMeModerator=function(){socket.emit("dont-make-me-moderator")},connection.deletePeer=function(remoteUserId){if(remoteUserId){if(connection.onleave({userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{}}),connection.peers[remoteUserId]){connection.peers[remoteUserId].streams.forEach(function(stream){stream.stop()});var peer=connection.peers[remoteUserId].peer;if(peer&&"closed"!==peer.iceConnectionState)try{peer.close()}catch(e){}connection.peers[remoteUserId]&&(connection.peers[remoteUserId].peer=null,delete connection.peers[remoteUserId])}if(-1!==connection.broadcasters.indexOf(remoteUserId)){var newArray=[];connection.broadcasters.forEach(function(broadcaster){broadcaster!==remoteUserId&&newArray.push(broadcaster)}),connection.broadcasters=newArray,keepNextBroadcasterOnServer()}}},connection.rejoin=function(connectionDescription){if(!connection.isInitiator&&connectionDescription&&Object.keys(connectionDescription).length){var extra={};connection.peers[connectionDescription.remoteUserId]&&(extra=connection.peers[connectionDescription.remoteUserId].extra,connection.deletePeer(connectionDescription.remoteUserId)),connectionDescription&&connectionDescription.remoteUserId&&(connection.join(connectionDescription.remoteUserId),connection.onReConnecting({userid:connectionDescription.remoteUserId,extra:extra}))}},connection.join=connection.connect=function(remoteUserId,options){connection.sessionid=(remoteUserId?remoteUserId.sessionid||remoteUserId.remoteUserId||remoteUserId:!1)||connection.sessionid,connection.sessionid+="";var localPeerSdpConstraints=!1,remotePeerSdpConstraints=!1,isOneWay=!1,isDataOnly=!1;if(remoteUserId&&remoteUserId.session||!remoteUserId||"string"==typeof remoteUserId){var session=remoteUserId?remoteUserId.session||connection.session:connection.session;isOneWay=!!session.oneway,isDataOnly=isData(session),remotePeerSdpConstraints={OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},localPeerSdpConstraints={OfferToReceiveAudio:isOneWay?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:isOneWay?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo}}options=options||{},"undefined"!=typeof options.localPeerSdpConstraints&&(localPeerSdpConstraints=options.localPeerSdpConstraints),"undefined"!=typeof options.remotePeerSdpConstraints&&(remotePeerSdpConstraints=options.remotePeerSdpConstraints),"undefined"!=typeof options.isOneWay&&(isOneWay=options.isOneWay),"undefined"!=typeof options.isDataOnly&&(isDataOnly=options.isDataOnly);var connectionDescription={remoteUserId:connection.sessionid,message:{newParticipationRequest:!0,isOneWay:isOneWay,isDataOnly:isDataOnly,localPeerSdpConstraints:localPeerSdpConstraints,remotePeerSdpConstraints:remotePeerSdpConstraints},sender:connection.userid,password:!1};return connectSocket(function(){connection.peers[connection.sessionid]||mPeer.onNegotiationNeeded(connectionDescription)}),connectionDescription},connection.connectWithAllParticipants=function(remoteUserId){mPeer.onNegotiationNeeded("connectWithAllParticipants",remoteUserId||connection.sessionid)},connection.removeFromBroadcastersList=function(remoteUserId){mPeer.onNegotiationNeeded("removeFromBroadcastersList",remoteUserId||connection.sessionid),connection.peers.getAllParticipants(remoteUserId||connection.sessionid).forEach(function(participant){mPeer.onNegotiationNeeded("dropPeerConnection",participant),connection.deletePeer(participant)}),connection.attachStreams.forEach(function(stream){stream.stop()})},connection.getUserMedia=connection.captureUserMedia=function(callback,session){function invokeGetUserMedia(localMediaConstraints,getUserMedia_callback){var isScreen=!1;localMediaConstraints&&(isScreen=localMediaConstraints.isScreen,delete localMediaConstraints.isScreen),getUserMediaHandler({onGettingLocalMedia:function(stream){return stream.isAudio=stream.isVideo=stream.isScreen=!1,isScreen?stream.isScreen=!0:session.audio&&session.video?stream.isVideo=!0:session.audio&&(stream.isAudio=!0),mPeer.onGettingLocalMedia(stream),getUserMedia_callback?getUserMedia_callback():void(callback&&callback(stream))},onLocalMediaError:function(error,constraints){return mPeer.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints,getUserMedia_callback)):void(callback&&callback())},localMediaConstraints:localMediaConstraints||{audio:session.audio?connection.mediaConstraints.audio:!1,video:session.video?connection.mediaConstraints.video:!1}})}return connection.observers.all(),session=session||connection.session,connection.dontCaptureUserMedia||isData(session)?void(callback&&callback()):void((session.audio||session.video||session.screen)&&(session.screen?connection.getScreenConstraints(function(error,screen_constraints){if(error)throw error;invokeGetUserMedia({video:screen_constraints,isScreen:!0},session.audio||session.video?invokeGetUserMedia:!1)}):(session.audio||session.video)&&invokeGetUserMedia()))},connection.closeBeforeUnload=!0,window.addEventListener("beforeunload",beforeUnload,!1),connection.userid=getRandomString(),connection.changeUserId=function(newUserId){connection.userid=newUserId||getRandomString(),socket.emit("changed-uuid",connection.userid)},connection.observers={extra:function(){observeObject(connection.extra,function(changes){socket.emit("extra-data-updated",connection.extra)})},attachStreams:function(){observeObject(connection.attachStreams,function(changes){changes.forEach(function(change){"add"===change.type&&setStreamEndHandler(change.object[change.name]),("remove"===change.type||"delete"===change.type)&&-1===connection.removeStreams.indexOf(change.object[change.name])&&connection.removeStreams.push(change.object[change.name]),connection.attachStreams=removeNullEntries(connection.attachStreams),connection.removeStreams=removeNullEntries(connection.removeStreams),connection.observers.all()})})},all:function(){this.extra(),this.attachStreams(),socket&&socket.emit("extra-data-updated",connection.extra)}},connection.extra={},connection.attachStreams=[],connection.removeStreams=[],connection.observers.all(),connection.session={audio:!0,video:!0},connection.enableFileSharing=!1,connection.bandwidth={screen:512,audio:128,video:512},connection.codecs={audio:"opus",video:"VP9"},connection.processSdp=function(sdp){return isMobileDevice||isFirefox?sdp:(sdp=CodecsHandler.setApplicationSpecificBandwidth(sdp,connection.bandwidth,!!connection.session.screen),sdp=CodecsHandler.setVideoBitrates(sdp,{min:8*connection.bandwidth.video*1024,max:8*connection.bandwidth.video*1024}),sdp=CodecsHandler.setOpusAttributes(sdp,{maxaveragebitrate:8*connection.bandwidth.audio*1024,maxplaybackrate:8*connection.bandwidth.audio*1024,stereo:1,maxptime:3}),"VP9"===connection.codecs.video&&(sdp=CodecsHandler.preferVP9(sdp)),"H264"===connection.codecs.video&&(sdp=CodecsHandler.removeVPX(sdp)),"G722"===connection.codecs.audio&&(sdp=CodecsHandler.removeNonG722(sdp)),sdp)},"undefined"!=typeof CodecsHandler&&(connection.BandwidthHandler=connection.CodecsHandler=CodecsHandler),connection.mediaConstraints={audio:{mandatory:{},optional:[{bandwidth:8*connection.bandwidth.audio*1024||1048576}]},video:{mandatory:{},optional:[{bandwidth:8*connection.bandwidth.audio*1024||1048576},{googLeakyBucket:!0},{facingMode:"user"}]}},isFirefox&&(connection.mediaConstraints={audio:!0,video:!0}),forceOptions.useDefaultDevices||isMobileDevice||DetectRTC.load(function(){var lastAudioDevice,lastVideoDevice;if(DetectRTC.MediaDevices.forEach(function(device){"audioinput"===device.kind&&connection.mediaConstraints.audio!==!1&&(lastAudioDevice=device),"videoinput"===device.kind&&connection.mediaConstraints.video!==!1&&(lastVideoDevice=device)}),lastAudioDevice){if(isFirefox)return void(connection.mediaConstraints.audio!==!0?connection.mediaConstraints.audio.deviceId=lastAudioDevice.id:connection.mediaConstraints.audio={deviceId:lastAudioDevice.id});1==connection.mediaConstraints.audio&&(connection.mediaConstraints.audio={mandatory:{},optional:[]}),connection.mediaConstraints.audio.optional||(connection.mediaConstraints.audio.optional=[]);var optional=[{sourceId:lastAudioDevice.id}];connection.mediaConstraints.audio.optional=optional.concat(connection.mediaConstraints.audio.optional)}if(lastVideoDevice){if(isFirefox)return void(connection.mediaConstraints.video!==!0?connection.mediaConstraints.video.deviceId=lastVideoDevice.id:connection.mediaConstraints.video={deviceId:lastVideoDevice.id});1==connection.mediaConstraints.video&&(connection.mediaConstraints.video={mandatory:{},optional:[]}),connection.mediaConstraints.video.optional||(connection.mediaConstraints.video.optional=[]);var optional=[{sourceId:lastVideoDevice.id}];connection.mediaConstraints.video.optional=optional.concat(connection.mediaConstraints.video.optional)}}),connection.sdpConstraints={mandatory:{OfferToReceiveAudio:!0,OfferToReceiveVideo:!0},optional:[{VoiceActivityDetection:!1}]},connection.optionalArgument={optional:[{DtlsSrtpKeyAgreement:!0},{googImprovedWifiBwe:!0},{googScreencastMinBitrate:300},{googIPv6:!0},{googDscp:!0},{googCpuUnderuseThreshold:55},{googCpuOveruseThreshold:85},{googSuspendBelowMinBitrate:!0},{googCpuOveruseDetection:!0}],mandatory:{}},connection.iceServers=IceServersHandler.getIceServers(connection),connection.candidates={host:!0,stun:!0,turn:!0},connection.iceProtocols={tcp:!0,udp:!0},connection.onopen=function(event){connection.enableLogs&&console.info("Data connection has been opened between you & ",event.userid)},connection.onclose=function(event){connection.enableLogs&&console.warn("Data connection has been closed between you & ",event.userid)},connection.onerror=function(error){connection.enableLogs&&console.error(error.userid,"data-error",error)},connection.onmessage=function(event){connection.enableLogs&&console.debug("data-message",event.userid,event.data)},connection.send=function(data,remoteUserId){connection.peers.send(data,remoteUserId)},connection.close=connection.disconnect=connection.leave=function(){beforeUnload(!1,!0)},connection.onstream=function(e){var parentNode=connection.videosContainer;parentNode.insertBefore(e.mediaElement,parentNode.firstChild),isMobileDevice&&setTimeout(function(){e.mediaElement.play()},5e3)},connection.onstreamended=function(e){e.mediaElement||(e.mediaElement=document.getElementById(e.streamid)),e.mediaElement&&e.mediaElement.parentNode&&e.mediaElement.parentNode.removeChild(e.mediaElement)},connection.direction="many-to-many",connection.removeStream=function(streamid){var stream;return connection.attachStreams.forEach(function(localStream){localStream.id===streamid&&(stream=localStream)}),stream?void(-1===connection.removeStreams.indexOf(stream)&&(connection.removeStreams.push(stream),connection.peers.getAllParticipants().forEach(function(participant){mPeer.renegotiatePeer(participant)}))):void console.warn("No such stream exists.",streamid)},connection.addStream=function(session){function invokeGetUserMedia(localMediaConstraints,callback){getUserMediaHandler({onGettingLocalMedia:function(stream){var videoConstraints=localMediaConstraints?localMediaConstraints.video:connection.mediaConstraints;return videoConstraints&&(videoConstraints.mediaSource||videoConstraints.mozMediaSource?stream.isScreen=!0:videoConstraints.mandatory&&videoConstraints.mandatory.chromeMediaSource&&(stream.isScreen=!0)),stream.isScreen||(stream.isVideo=stream.getVideoTracks().length,stream.isAudio=!stream.isVideo&&stream.getAudioTracks().length),mPeer.onGettingLocalMedia(stream),session.streamCallback&&session.streamCallback(stream),callback?callback():void connection.renegotiate()},onLocalMediaError:function(error,constraints){return mPeer.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints,callback)):callback?callback():void connection.renegotiate()},localMediaConstraints:localMediaConstraints||{audio:session.audio?connection.mediaConstraints.audio:!1,video:session.video?connection.mediaConstraints.video:!1}})}return isData(session)?void connection.renegotiate():void((!session.audio||session.video||session.screen)&&(session.screen?connection.getScreenConstraints(function(error,screen_constraints){return error?alert(error):void invokeGetUserMedia({video:screen_constraints},session.audio||session.video?invokeGetUserMedia:!1)}):(session.audio||session.video)&&invokeGetUserMedia()))},connection.applyConstraints=function(mediaConstraints,streamid){if(!MediaStreamTrack||!MediaStreamTrack.prototype.applyConstraints)return void alert("track.applyConstraints is NOT supported in your browser.");if(streamid){return connection.streamEvents[streamid]&&(stream=connection.streamEvents[streamid].stream),void applyConstraints(stream,mediaConstraints)}connection.attachStreams.forEach(function(stream){applyConstraints(stream,mediaConstraints)})},connection.replaceTrack=function(session,remoteUserId,isVideoTrack){function invokeGetUserMedia(localMediaConstraints,callback){getUserMediaHandler({onGettingLocalMedia:function(stream){return mPeer.onGettingLocalMedia(stream),callback?callback():void connection.replaceTrack(stream,remoteUserId,isVideoTrack||session.video||session.screen)},onLocalMediaError:function(error,constraints){return mPeer.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints,callback)):void(callback&&callback())},localMediaConstraints:localMediaConstraints||{audio:session.audio?connection.mediaConstraints.audio:!1,video:session.video?connection.mediaConstraints.video:!1}})}if(session=session||{},!RTCPeerConnection.prototype.getSenders)return void connection.addStream(session);if(session instanceof MediaStreamTrack)return void replaceTrack(session,remoteUserId,isVideoTrack);if(session instanceof MediaStream)return session.getVideoTracks().length&&replaceTrack(session.getVideoTracks()[0],remoteUserId,!0),void(session.getAudioTracks().length&&replaceTrack(session.getAudioTracks()[0],remoteUserId,!1));if(isData(session))throw"connection.replaceTrack requires audio and/or video and/or screen.";(!session.audio||session.video||session.screen)&&(session.screen?connection.getScreenConstraints(function(error,screen_constraints){return error?alert(error):void invokeGetUserMedia({video:screen_constraints},session.audio||session.video?invokeGetUserMedia:!1)}):(session.audio||session.video)&&invokeGetUserMedia())},connection.resetTrack=function(remoteUsersIds,isVideoTrack){remoteUsersIds||(remoteUsersIds=connection.getAllParticipants()),"string"==typeof remoteUsersIds&&(remoteUsersIds=[remoteUsersIds]),remoteUsersIds.forEach(function(participant){var peer=connection.peers[participant].peer;"undefined"!=typeof isVideoTrack&&isVideoTrack!==!0||!peer.lastVideoTrack||connection.replaceTrack(peer.lastVideoTrack,participant,!0),"undefined"!=typeof isVideoTrack&&isVideoTrack!==!1||!peer.lastAudioTrack||connection.replaceTrack(peer.lastAudioTrack,participant,!1)})},connection.renegotiate=function(remoteUserId){return remoteUserId?void mPeer.renegotiatePeer(remoteUserId):void connection.peers.getAllParticipants().forEach(function(participant){mPeer.renegotiatePeer(participant)})},connection.onMediaError=function(error,constraints){connection.enableLogs&&console.error(error,constraints)},connection.addNewBroadcaster=function(broadcasterId,userPreferences){connection.broadcasters.length&&setTimeout(function(){mPeer.connectNewParticipantWithAllBroadcasters(broadcasterId,userPreferences,connection.broadcasters.join("|-,-|"))},1e4),connection.session.oneway||connection.session.broadcast||"many-to-many"!==connection.direction||-1!==connection.broadcasters.indexOf(broadcasterId)||(connection.broadcasters.push(broadcasterId),keepNextBroadcasterOnServer())},connection.autoCloseEntireSession=!1,connection.filesContainer=connection.videosContainer=document.body||document.documentElement,connection.isInitiator=!1,connection.shareFile=mPeer.shareFile,"undefined"!=typeof FileProgressBarHandler&&FileProgressBarHandler.handle(connection),"undefined"!=typeof TranslationHandler&&TranslationHandler.handle(connection),connection.token=getRandomString,connection.onNewParticipant=function(participantId,userPreferences){connection.acceptParticipationRequest(participantId,userPreferences)},connection.acceptParticipationRequest=function(participantId,userPreferences){userPreferences.successCallback&&(userPreferences.successCallback(),delete userPreferences.successCallback),mPeer.createNewPeer(participantId,userPreferences)},connection.onShiftedModerationControl=function(sender,existingBroadcasters){connection.acceptModerationControl(sender,existingBroadcasters)},connection.acceptModerationControl=function(sender,existingBroadcasters){connection.isInitiator=!0,connection.broadcasters=existingBroadcasters,connection.peers.getAllParticipants().forEach(function(participant){mPeer.onNegotiationNeeded({changedUUID:sender,oldUUID:connection.userid,newUUID:sender},participant)}),connection.userid=sender,socket.emit("changed-uuid",connection.userid)},connection.shiftModerationControl=function(remoteUserId,existingBroadcasters,firedOnLeave){mPeer.onNegotiationNeeded({shiftedModerationControl:!0,broadcasters:existingBroadcasters,firedOnLeave:!!firedOnLeave},remoteUserId)},"undefined"!=typeof StreamsHandler&&(connection.StreamsHandler=StreamsHandler),connection.onleave=function(userid){},connection.invokeSelectFileDialog=function(callback){var selector=new FileSelector;selector.selectSingleFile(callback)},connection.getPublicModerators=function(userIdStartsWith,callback){"function"==typeof userIdStartsWith&&(callback=userIdStartsWith),connectSocket(function(socket){socket.emit("get-public-moderators","string"==typeof userIdStartsWith?userIdStartsWith:"",callback)})},connection.onmute=function(e){e.mediaElement&&("both"===e.muteType||"video"===e.muteType?(e.mediaElement.src=null,e.mediaElement.pause(),e.mediaElement.poster=e.snapshot||"https://cdn.webrtc-experiment.com/images/muted.png"):"audio"===e.muteType&&(e.mediaElement.muted=!0))},connection.onunmute=function(e){e.mediaElement&&("both"===e.unmuteType||"video"===e.unmuteType?(e.mediaElement.poster=null,e.mediaElement.src=URL.createObjectURL(e.stream),e.mediaElement.play()):"audio"===e.unmuteType&&(e.mediaElement.muted=!1))},connection.onExtraDataUpdated=function(event){event.status="online",connection.onUserStatusChanged(event,!0)},connection.onJoinWithPassword=function(remoteUserId){console.warn(remoteUserId,"is password protected. Please join with password.")},connection.onInvalidPassword=function(remoteUserId,oldPassword){console.warn(remoteUserId,"is password protected. Please join with valid password. Your old password",oldPassword,"is wrong.")},connection.onPasswordMaxTriesOver=function(remoteUserId){console.warn(remoteUserId,"is password protected. Your max password tries exceeded the limit.")},connection.getAllParticipants=function(sender){return connection.peers.getAllParticipants(sender)},"undefined"!=typeof StreamsHandler&&(StreamsHandler.onSyncNeeded=function(streamid,action,type){connection.peers.getAllParticipants().forEach(function(participant){mPeer.onNegotiationNeeded({streamid:streamid,action:action,streamSyncNeeded:!0,type:type||"both"},participant)})}),connection.connectSocket=function(callback){connectSocket(callback)},connection.closeSocket=function(){socket&&("undefined"!=typeof socket.disconnect&&socket.disconnect(),socket=null)},connection.getSocket=function(callback){return socket?callback&&callback(socket):connectSocket(callback),socket},connection.getRemoteStreams=mPeer.getRemoteStreams;var skipStreams=["selectFirst","selectAll","forEach"];if(connection.streamEvents={selectFirst:function(options){if(!options){var firstStream;for(var str in connection.streamEvents)-1!==skipStreams.indexOf(str)||firstStream||(firstStream=connection.streamEvents[str]);return firstStream}},selectAll:function(){}},connection.socketURL="/",connection.socketMessageEvent="RTCMultiConnection-Message",connection.socketCustomEvent="RTCMultiConnection-Custom-Message",connection.DetectRTC=DetectRTC,connection.onUserStatusChanged=function(event,dontWriteLogs){connection.enableLogs&&!dontWriteLogs&&console.info(event.userid,event.status)},connection.getUserMediaHandler=getUserMediaHandler,connection.multiPeersHandler=mPeer,connection.enableLogs=!0,connection.setCustomSocketHandler=function(customSocketHandler){"undefined"!=typeof SocketConnection&&(SocketConnection=customSocketHandler)},connection.chunkSize=65e3,connection.maxParticipantsAllowed=1e3,connection.disconnectWith=mPeer.disconnectWith,connection.checkPresence=function(remoteUserId,callback){mPeer.onNegotiationNeeded({detectPresence:!0,userid:(remoteUserId||connection.sessionid)+""},"system",callback)},connection.onReadyForOffer=function(remoteUserId,userPreferences){connection.multiPeersHandler.createNewPeer(remoteUserId,userPreferences)},connection.setUserPreferences=function(userPreferences){return connection.dontAttachStream&&(userPreferences.dontAttachLocalStream=!0),connection.dontGetRemoteStream&&(userPreferences.dontGetRemoteStream=!0),userPreferences},connection.updateExtraData=function(){socket.emit("extra-data-updated",connection.extra)},connection.enableScalableBroadcast=!1,connection.maxRelayLimitPerUser=3,connection.dontCaptureUserMedia=!1,connection.dontAttachStream=!1,connection.dontGetRemoteStream=!1,connection.onReConnecting=function(event){connection.enableLogs&&console.info("ReConnecting with",event.userid,"...")},connection.beforeAddingStream=function(stream){return stream},connection.beforeRemovingStream=function(stream){return stream},"undefined"!=typeof isChromeExtensionAvailable&&(connection.checkIfChromeExtensionAvailable=isChromeExtensionAvailable),"undefined"!=typeof isFirefoxExtensionAvailable&&(connection.checkIfChromeExtensionAvailable=isFirefoxExtensionAvailable),"undefined"!=typeof getChromeExtensionStatus&&(connection.getChromeExtensionStatus=getChromeExtensionStatus),connection.getScreenConstraints=function(callback){getScreenConstraints(function(error,screen_constraints){error||(screen_constraints=connection.modifyScreenConstraints(screen_constraints),callback(error,screen_constraints))})},connection.modifyScreenConstraints=function(screen_constraints){return screen_constraints},connection.onPeerStateChanged=function(state){connection.enableLogs&&-1!==state.iceConnectionState.search(/closed|failed/gi)&&console.error("Peer connection is closed between you & ",state.userid,state.extra,"state:",state.iceConnectionState)},connection.isOnline=!0,listenEventHandler("online",function(){connection.isOnline=!0}),listenEventHandler("offline",function(){connection.isOnline=!1}),connection.isLowBandwidth=!1,navigator&&navigator.connection&&navigator.connection.type&&(connection.isLowBandwidth=-1!==navigator.connection.type.toString().toLowerCase().search(/wifi|cell/g),connection.isLowBandwidth)){if(connection.bandwidth={audio:30,video:30,screen:30},connection.mediaConstraints.audio&&connection.mediaConstraints.audio.optional.length){var newArray=[];connection.mediaConstraints.audio.optional.forEach(function(opt){"undefined"==typeof opt.bandwidth&&newArray.push(opt); -}),connection.mediaConstraints.audio.optional=newArray}if(connection.mediaConstraints.video&&connection.mediaConstraints.video.optional.length){var newArray=[];connection.mediaConstraints.video.optional.forEach(function(opt){"undefined"==typeof opt.bandwidth&&newArray.push(opt)}),connection.mediaConstraints.video.optional=newArray}}connection.getExtraData=function(remoteUserId){if(!remoteUserId)throw"remoteUserId is required.";return connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{}},forceOptions.autoOpenOrJoin&&connection.openOrJoin(connection.sessionid)}function SocketConnection(connection,connectCallback){var parameters="";parameters+="?userid="+connection.userid,parameters+="&msgEvent="+connection.socketMessageEvent,parameters+="&socketCustomEvent="+connection.socketCustomEvent,connection.enableScalableBroadcast&&(parameters+="&enableScalableBroadcast=true",parameters+="&maxRelayLimitPerUser="+(connection.maxRelayLimitPerUser||2));var socket=io.connect((connection.socketURL||"/")+parameters,connection.socketOptions),mPeer=connection.multiPeersHandler;return socket.on("extra-data-updated",function(remoteUserId,extra){connection.peers[remoteUserId]&&(connection.peers[remoteUserId].extra=extra,connection.onExtraDataUpdated({userid:remoteUserId,extra:extra}))}),socket.on(connection.socketMessageEvent,function(message){if(message.remoteUserId==connection.userid){if(connection.peers[message.sender]&&connection.peers[message.sender].extra!=message.extra&&(connection.peers[message.sender].extra=message.extra,connection.onExtraDataUpdated({userid:message.sender,extra:message.extra})),message.message.streamSyncNeeded&&connection.peers[message.sender]){var stream=connection.streamEvents[message.message.streamid];if(!stream||!stream.stream)return;var action=message.message.action;if("ended"===action||"stream-removed"===action)return void connection.onstreamended(stream);var type="both"!=message.message.type?message.message.type:null;return void stream.stream[action](type)}if("connectWithAllParticipants"===message.message)return-1===connection.broadcasters.indexOf(message.sender)&&connection.broadcasters.push(message.sender),void mPeer.onNegotiationNeeded({allParticipants:connection.getAllParticipants(message.sender)},message.sender);if("removeFromBroadcastersList"===message.message)return void(-1!==connection.broadcasters.indexOf(message.sender)&&(delete connection.broadcasters[connection.broadcasters.indexOf(message.sender)],connection.broadcasters=removeNullEntries(connection.broadcasters)));if("dropPeerConnection"===message.message)return void connection.deletePeer(message.sender);if(message.message.allParticipants)return-1===message.message.allParticipants.indexOf(message.sender)&&message.message.allParticipants.push(message.sender),void message.message.allParticipants.forEach(function(participant){mPeer[connection.peers[participant]?"renegotiatePeer":"createNewPeer"](participant,{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})});if(message.message.newParticipant){if(message.message.newParticipant==connection.userid)return;if(connection.peers[message.message.newParticipant])return;return void mPeer.createNewPeer(message.message.newParticipant,message.message.userPreferences||{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})}if((message.message.readyForOffer||message.message.addMeAsBroadcaster)&&connection.addNewBroadcaster(message.sender),message.message.newParticipationRequest&&message.sender!==connection.userid){connection.peers[message.sender]&&connection.deletePeer(message.sender);var userPreferences={extra:message.extra||{},localPeerSdpConstraints:message.message.remotePeerSdpConstraints||{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:message.message.localPeerSdpConstraints||{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:"undefined"!=typeof message.message.isDataOnly?message.message.isDataOnly:isData(connection.session),dontGetRemoteStream:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,dontAttachLocalStream:!!message.message.dontGetRemoteStream,connectionDescription:message,successCallback:function(){("undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction)&&connection.addNewBroadcaster(message.sender,userPreferences),(connection.session.oneway||"one-way"===connection.direction||isData(connection.session))&&connection.addNewBroadcaster(message.sender,userPreferences)}};return void connection.onNewParticipant(message.sender,userPreferences)}return message.message.shiftedModerationControl?void connection.onShiftedModerationControl(message.sender,message.message.broadcasters):(message.message.changedUUID&&connection.peers[message.message.oldUUID]&&(connection.peers[message.message.newUUID]=connection.peers[message.message.oldUUID],delete connection.peers[message.message.oldUUID]),message.message.userLeft?(mPeer.onUserLeft(message.sender),void(message.message.autoCloseEntireSession&&connection.leave())):void mPeer.addNegotiatedMessage(message.message,message.sender))}}),socket.on("user-left",function(userid){onUserLeft(userid),connection.onUserStatusChanged({userid:userid,status:"offline",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}}),connection.onleave({userid:userid,extra:{}})}),socket.on("connect",function(){connection.enableLogs&&console.info("socket.io connection is opened."),socket.emit("extra-data-updated",connection.extra),connectCallback&&connectCallback(socket)}),socket.on("disconnect",function(){connection.enableLogs&&(console.info("socket.io connection is closed"),console.warn("socket.io reconnecting"))}),socket.on("join-with-password",function(remoteUserId){connection.onJoinWithPassword(remoteUserId)}),socket.on("invalid-password",function(remoteUserId,oldPassword){connection.onInvalidPassword(remoteUserId,oldPassword)}),socket.on("password-max-tries-over",function(remoteUserId){connection.onPasswordMaxTriesOver(remoteUserId)}),socket.on("user-disconnected",function(remoteUserId){remoteUserId!==connection.userid&&(connection.onUserStatusChanged({userid:remoteUserId,status:"offline",extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra||{}:{}}),connection.deletePeer(remoteUserId))}),socket.on("user-connected",function(userid){userid!==connection.userid&&connection.onUserStatusChanged({userid:userid,status:"online",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}})}),socket.on("logs",function(log){connection.enableLogs&&console.debug("server-logs",log)}),socket}function MultiPeers(connection){function initFileBufferReader(){fbr=new FileBufferReader,fbr.onProgress=function(chunk){connection.onFileProgress(chunk)},fbr.onBegin=function(file){connection.onFileStart(file)},fbr.onEnd=function(file){connection.onFileEnd(file)}}var self=this,skipPeers=["getAllParticipants","getLength","selectFirst","streams","send","forEach"];connection.peers={getLength:function(){var numberOfPeers=0;for(var peer in this)-1==skipPeers.indexOf(peer)&&numberOfPeers++;return numberOfPeers},selectFirst:function(){var firstPeer;for(var peer in this)-1==skipPeers.indexOf(peer)&&(firstPeer=this[peer]);return firstPeer},getAllParticipants:function(sender){var allPeers=[];for(var peer in this)-1==skipPeers.indexOf(peer)&&peer!=sender&&allPeers.push(peer);return allPeers},forEach:function(callbcak){this.getAllParticipants().forEach(function(participant){callbcak(connection.peers[participant])})},send:function(data,remoteUserId){var that=this;if(!isNull(data.size)&&!isNull(data.type))return void self.shareFile(data,remoteUserId);if(!("text"===data.type||data instanceof ArrayBuffer||data instanceof DataView))return void TextSender.send({text:data,channel:this,connection:connection,remoteUserId:remoteUserId});if("text"===data.type&&(data=JSON.stringify(data)),remoteUserId){var remoteUser=connection.peers[remoteUserId];if(remoteUser)return void remoteUser.channels.forEach(function(channel){channel.send(data)})}this.getAllParticipants().forEach(function(participant){that[participant].channels.forEach(function(channel){channel.send(data)})})}},this.uuid=connection.userid,this.getLocalConfig=function(remoteSdp,remoteUserId,userPreferences){return userPreferences||(userPreferences={}),{streamsToShare:userPreferences.streamsToShare||{},rtcMultiConnection:connection,connectionDescription:userPreferences.connectionDescription,remoteUserId:remoteUserId,localPeerSdpConstraints:userPreferences.localPeerSdpConstraints,remotePeerSdpConstraints:userPreferences.remotePeerSdpConstraints,dontGetRemoteStream:!!userPreferences.dontGetRemoteStream,dontAttachLocalStream:!!userPreferences.dontAttachLocalStream,renegotiatingPeer:!!userPreferences.renegotiatingPeer,peerRef:userPreferences.peerRef,onLocalSdp:function(localSdp){self.onNegotiationNeeded(localSdp,remoteUserId)},onLocalCandidate:function(localCandidate){localCandidate=OnIceCandidateHandler.processCandidates(connection,localCandidate),localCandidate&&self.onNegotiationNeeded(localCandidate,remoteUserId)},remoteSdp:remoteSdp,onDataChannelMessage:function(message){if(!fbr&&connection.enableFileSharing&&initFileBufferReader(),"string"==typeof message||!connection.enableFileSharing)return void self.onDataChannelMessage(message,remoteUserId);var that=this;return message instanceof ArrayBuffer||message instanceof DataView?void fbr.convertToObject(message,function(object){that.onDataChannelMessage(object)}):message.readyForNextChunk?void fbr.getNextChunk(message.uuid,function(nextChunk,isLastChunk){connection.peers[remoteUserId].channels.forEach(function(channel){channel.send(nextChunk)})},remoteUserId):void fbr.addChunk(message,function(promptNextChunk){connection.peers[remoteUserId].peer.channel.send(promptNextChunk)})},onDataChannelError:function(error){self.onDataChannelError(error,remoteUserId)},onDataChannelOpened:function(channel){self.onDataChannelOpened(channel,remoteUserId)},onDataChannelClosed:function(event){self.onDataChannelClosed(event,remoteUserId)},onRemoteStream:function(stream){if(connection.peers[remoteUserId].streams.push(stream),isPluginRTC){var mediaElement=document.createElement("video"),body=connection.videosContainer;return body.insertBefore(mediaElement,body.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),self.onGettingRemoteMedia(mediaElement,remoteUserId)},3e3)}self.onGettingRemoteMedia(stream,remoteUserId)},onRemoteStreamRemoved:function(stream){self.onRemovingRemoteMedia(stream,remoteUserId)},onPeerStateChanged:function(states){self.onPeerStateChanged(states),"new"===states.iceConnectionState&&self.onNegotiationStarted(remoteUserId,states),"connected"===states.iceConnectionState&&self.onNegotiationCompleted(remoteUserId,states),-1!==states.iceConnectionState.search(/closed|failed/gi)&&(self.onUserLeft(remoteUserId),self.disconnectWith(remoteUserId))}}},this.createNewPeer=function(remoteUserId,userPreferences){if(!(connection.maxParticipantsAllowed<=connection.getAllParticipants().length)){if(userPreferences=userPreferences||{},!userPreferences.isOneWay&&!userPreferences.isDataOnly)return userPreferences.isOneWay=!0,void this.onNegotiationNeeded({enableMedia:!0,userPreferences:userPreferences},remoteUserId);userPreferences=connection.setUserPreferences(userPreferences,remoteUserId);var localConfig=this.getLocalConfig(null,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)}},this.createAnsweringPeer=function(remoteSdp,remoteUserId,userPreferences){userPreferences=connection.setUserPreferences(userPreferences||{},remoteUserId);var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.renegotiatePeer=function(remoteUserId,userPreferences,remoteSdp){if(!connection.peers[remoteUserId])return void(connection.enableLogs&&console.error("This peer ("+remoteUserId+") does not exists. Renegotiation skipped."));userPreferences||(userPreferences={}),userPreferences.renegotiatingPeer=!0,userPreferences.peerRef=connection.peers[remoteUserId].peer;var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.replaceTrack=function(track,remoteUserId,isVideoTrack){if(!connection.peers[remoteUserId])throw"This peer ("+remoteUserId+") does not exists.";var peer=connection.peers[remoteUserId].peer;return peer.getSenders&&"function"==typeof peer.getSenders&&peer.getSenders().length?void peer.getSenders().forEach(function(rtpSender){isVideoTrack&&rtpSender.track instanceof VideoStreamTrack&&(connection.peers[remoteUserId].peer.lastVideoTrack=rtpSender.track,rtpSender.replaceTrack(track)),!isVideoTrack&&rtpSender.track instanceof AudioStreamTrack&&(connection.peers[remoteUserId].peer.lastAudioTrack=rtpSender.track,rtpSender.replaceTrack(track))}):(console.warn("RTPSender.replaceTrack is NOT supported."),void this.renegotiatePeer(remoteUserId))},this.onNegotiationNeeded=function(message,remoteUserId){},this.addNegotiatedMessage=function(message,remoteUserId){function invokeGetUserMedia(mediaConstraints){getUserMediaHandler({onGettingLocalMedia:function(localStream){self.onGettingLocalMedia(localStream);var streamsToShare={};connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},onLocalMediaError:function(error,constraints){return self.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints)):void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},localMediaConstraints:mediaConstraints})}if(message.type&&message.sdp)return"answer"==message.type&&connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteSdp(message),"offer"==message.type&&(message.renegotiatingPeer?this.renegotiatePeer(remoteUserId,null,message):this.createAnsweringPeer(message,remoteUserId)),void(connection.enableLogs&&console.log("Remote peer's sdp:",message.sdp));if(message.candidate)return connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteCandidate(message),void(connection.enableLogs&&console.log("Remote peer's candidate pairs:",message.candidate));if(message.enableMedia){if(connection.attachStreams.length){var streamsToShare={};return connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)}var localMediaConstraints={},userPreferences=message.userPreferences;userPreferences.localPeerSdpConstraints.OfferToReceiveAudio&&(localMediaConstraints.audio=connection.mediaConstraints.audio),userPreferences.localPeerSdpConstraints.OfferToReceiveVideo&&(localMediaConstraints.video=connection.mediaConstraints.video),invokeGetUserMedia(localMediaConstraints)}message.readyForOffer&&connection.onReadyForOffer(remoteUserId,message.userPreferences)},this.connectNewParticipantWithAllBroadcasters=function(newParticipantId,userPreferences,broadcastersList){if(broadcastersList=broadcastersList.split("|-,-|"),broadcastersList.length){var firstBroadcaster=broadcastersList[0];self.onNegotiationNeeded({newParticipant:newParticipantId,userPreferences:userPreferences||!1},firstBroadcaster),delete broadcastersList[0];var array=[];broadcastersList.forEach(function(broadcaster){broadcaster&&array.push(broadcaster)}),setTimeout(function(){self.connectNewParticipantWithAllBroadcasters(newParticipantId,userPreferences,array.join("|-,-|"))},1e4)}},this.onGettingRemoteMedia=function(stream,remoteUserId){},this.onRemovingRemoteMedia=function(stream,remoteUserId){},this.onGettingLocalMedia=function(localStream){},this.onLocalMediaError=function(error,constraints){connection.onMediaError(error,constraints)};var fbr;this.shareFile=function(file,remoteUserId){if(!connection.enableFileSharing)throw'"connection.enableFileSharing" is false.';initFileBufferReader(),fbr.readAsArrayBuffer(file,function(uuid){var arrayOfUsers=connection.getAllParticipants();remoteUserId&&(arrayOfUsers=[remoteUserId]),arrayOfUsers.forEach(function(participant){fbr.getNextChunk(uuid,function(nextChunk){connection.peers[participant].channels.forEach(function(channel){channel.send(nextChunk)})},participant)})},{userid:connection.userid,chunkSize:isFirefox?15e3:connection.chunkSize||0})};var textReceiver=new TextReceiver(connection);this.onDataChannelMessage=function(message,remoteUserId){textReceiver.receive(JSON.parse(message),remoteUserId,connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{})},this.onDataChannelClosed=function(event,remoteUserId){event.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onclose(event)},this.onDataChannelError=function(error,remoteUserId){error.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onerror(error)},this.onDataChannelOpened=function(channel,remoteUserId){connection.peers[remoteUserId].channels.length||(connection.peers[remoteUserId].channels.push(channel),connection.onopen({userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},channel:channel}),connection.observers.all())},this.onPeerStateChanged=function(state){connection.onPeerStateChanged(state)},this.onNegotiationStarted=function(remoteUserId,states){},this.onNegotiationCompleted=function(remoteUserId,states){},this.getRemoteStreams=function(remoteUserId){return remoteUserId=remoteUserId||connection.peers.getAllParticipants()[0],connection.peers[remoteUserId]?connection.peers[remoteUserId].streams:[]},this.isPluginRTC=connection.isPluginRTC=isPluginRTC}function fireEvent(obj,eventName,args){if("undefined"!=typeof CustomEvent){var eventDetail={arguments:args,__exposedProps__:args},event=new CustomEvent(eventName,eventDetail);obj.dispatchEvent(event)}}function setHarkEvents(connection,streamEvent){if(!connection||!streamEvent)throw"Both arguments are required.";if(connection.onspeaking&&connection.onsilence){if("undefined"==typeof hark)throw"hark.js not found.";hark(streamEvent.stream,{onspeaking:function(){connection.onspeaking(streamEvent)},onsilence:function(){connection.onsilence(streamEvent)},onvolumechange:function(volume,threshold){connection.onvolumechange&&connection.onvolumechange(merge({volume:volume,threshold:threshold},streamEvent))}})}}function setMuteHandlers(connection,streamEvent){streamEvent.stream.addEventListener("mute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.muteType,video:"video"===event.muteType},connection.onmute(event)},!1),streamEvent.stream.addEventListener("unmute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.unmuteType,video:"video"===event.unmuteType},connection.onunmute(event)},!1)}function getRandomString(){if(window.crypto&&window.crypto.getRandomValues&&-1===navigator.userAgent.indexOf("Safari")){for(var a=window.crypto.getRandomValues(new Uint32Array(3)),token="",i=0,l=a.length;l>i;i++)token+=a[i].toString(36);return token}return(Math.random()*(new Date).getTime()).toString(36).replace(/\./g,"")}function getRMCMediaElement(stream,callback,connection){var isAudioOnly=!1;stream.getVideoTracks().length||(isAudioOnly=!0);var mediaElement=document.createElement(isAudioOnly?"audio":"video");return isPluginRTC?(connection.videosContainer.insertBefore(mediaElement,connection.videosContainer.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),callback(mediaElement)},1e3)):(mediaElement[isFirefox?"mozSrcObject":"src"]=isFirefox?stream:window.URL.createObjectURL(stream),mediaElement.controls=!0,isFirefox&&mediaElement.addEventListener("ended",function(){if(currentUserMediaRequest.remove(stream.idInstance),"local"===stream.type){StreamsHandler.onSyncNeeded(stream.streamid,"ended"),connection.attachStreams.forEach(function(aStream,idx){stream.streamid===aStream.streamid&&delete connection.attachStreams[idx]});var newStreamsArray=[];connection.attachStreams.forEach(function(aStream){aStream&&newStreamsArray.push(aStream)}),connection.attachStreams=newStreamsArray,connection.observers.all();var streamEvent=connection.streamEvents[stream.streamid];if(streamEvent)return void connection.onstreamended(streamEvent);this.parentNode&&this.parentNode.removeChild(this)}},!1),mediaElement.play(),void callback(mediaElement))}function listenEventHandler(eventName,eventHandler){window.removeEventListener(eventName,eventHandler),window.addEventListener(eventName,eventHandler,!1)}function removeNullEntries(array){var newArray=[];return array.forEach(function(item){item&&newArray.push(item)}),newArray}function isData(session){return!session.audio&&!session.video&&!session.screen&&session.data}function isNull(obj){return"undefined"==typeof obj}function isString(obj){return"string"==typeof obj}function observeObject(obj,callback){Object.observe&&(isMobileDevice||Object.observe(obj,function(changes){var jsonStringified=JSON.stringify(changes);lastChanges!=jsonStringified&&(lastChanges=jsonStringified,callback(changes))}))}function setSdpConstraints(config){var sdpConstraints,sdpConstraints_mandatory={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo};return sdpConstraints={mandatory:sdpConstraints_mandatory,optional:[{VoiceActivityDetection:!1}]},navigator.mozGetUserMedia&&firefoxVersion>34&&(sdpConstraints={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo}),sdpConstraints}function onPluginRTCInitialized(pluginRTCObject){Plugin=pluginRTCObject,MediaStreamTrack=Plugin.MediaStreamTrack,RTCPeerConnection=Plugin.RTCPeerConnection,RTCIceCandidate=Plugin.RTCIceCandidate,RTCSessionDescription=Plugin.RTCSessionDescription}function PeerInitiator(config){function createDataChannel(){if(!isOfferer)return void(peer.ondatachannel=function(event){var channel=event.channel;setChannelEvents(channel)});var channel=peer.createDataChannel("RTCDataChannel",{});setChannelEvents(channel)}function setChannelEvents(channel){channel.binaryType="arraybuffer",channel.onmessage=function(event){config.onDataChannelMessage(event.data)},channel.onopen=function(){config.onDataChannelOpened(channel)},channel.onerror=function(error){config.onDataChannelError(error)},channel.onclose=function(event){config.onDataChannelClosed(event)},channel.internalSend=channel.send,channel.send=function(data){"open"===channel.readyState&&channel.internalSend(data)},peer.channel=channel}var connection=config.rtcMultiConnection;this.extra=config.remoteSdp?config.remoteSdp.extra:connection.extra,this.remoteUserId=config.remoteUserId,this.streams=[],this.channels=[],this.connectionDescription=config.connectionDescription;var that=this;config.remoteSdp&&(this.connectionDescription=config.remoteSdp.connectionDescription);var allRemoteStreams={};if(Object.observe){var that=this;Object.observe(this.channels,function(changes){changes.forEach(function(change){"add"===change.type&&change.object[change.name].addEventListener("close",function(){delete that.channels[that.channels.indexOf(change.object[change.name])],that.channels=removeNullEntries(that.channels)},!1),("remove"===change.type||"delete"===change.type)&&-1!==that.channels.indexOf(change.object[change.name])&&delete that.channels.indexOf(change.object[change.name]),that.channels=removeNullEntries(that.channels)})})}defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:!0,OfferToReceiveVideo:!0});var peer,renegotiatingPeer=!!config.renegotiatingPeer;config.remoteSdp&&(renegotiatingPeer=!!config.remoteSdp.renegotiatingPeer);var localStreams=[];connection.attachStreams.forEach(function(stream){stream&&localStreams.push(stream)}),renegotiatingPeer?(peer=config.peerRef,peer.getLocalStreams().forEach(function(stream){localStreams.forEach(function(localStream,index){stream==localStream&&delete localStreams[index]}),connection.removeStreams.forEach(function(streamToRemove,index){stream===streamToRemove&&(stream=connection.beforeRemovingStream(stream),stream&&peer.removeStream&&peer.removeStream(stream),localStreams.forEach(function(localStream,index){streamToRemove==localStream&&delete localStreams[index]}))})})):peer=new RTCPeerConnection(navigator.onLine?{iceServers:connection.iceServers,iceTransports:"all"}:null,connection.optionalArgument),"Firefox"===connection.DetectRTC.browser.name&&(peer.removeStream=function(stream){stream.mute(),connection.StreamsHandler.onSyncNeeded(stream.streamid,"stream-removed")}),peer.onicecandidate=function(event){event.candidate&&config.onLocalCandidate({candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex})};var isFirefoxOffered=!isFirefox;config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered&&(isFirefoxOffered=!0),localStreams.forEach(function(localStream){config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream||config.dontAttachLocalStream||(localStream=connection.beforeAddingStream(localStream),localStream&&peer.addStream(localStream))}),peer.oniceconnectionstatechange=peer.onsignalingstatechange=function(){var extra=that.extra;connection.peers[that.remoteUserId]&&(extra=connection.peers[that.remoteUserId].extra||extra),peer&&config.onPeerStateChanged({iceConnectionState:peer.iceConnectionState,iceGatheringState:peer.iceGatheringState,signalingState:peer.signalingState,extra:extra,userid:that.remoteUserId})};var sdpConstraints={OfferToReceiveAudio:!!localStreams.length,OfferToReceiveVideo:!!localStreams.length};config.localPeerSdpConstraints&&(sdpConstraints=config.localPeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),peer.onaddstream=function(event){var streamsToShare={};config.remoteSdp&&config.remoteSdp.streamsToShare?streamsToShare=config.remoteSdp.streamsToShare:config.streamsToShare&&(streamsToShare=config.streamsToShare);var streamToShare=streamsToShare[event.stream.id];streamToShare&&(event.stream.isAudio=streamToShare.isAudio,event.stream.isVideo=streamToShare.isVideo,event.stream.isScreen=streamToShare.isScreen),event.stream.streamid=event.stream.id,event.stream.stop||(event.stream.stop=function(){isFirefox&&fireEvent(this,"ended")}),allRemoteStreams[event.stream.id]=event.stream,config.onRemoteStream(event.stream)},peer.onremovestream=function(event){event.stream.streamid=event.stream.id,allRemoteStreams[event.stream.id]&&delete allRemoteStreams[event.stream.id],config.onRemoteStreamRemoved(event.stream)},this.addRemoteCandidate=function(remoteCandidate){peer.addIceCandidate(new RTCIceCandidate(remoteCandidate))},this.addRemoteSdp=function(remoteSdp){remoteSdp.sdp=connection.processSdp(remoteSdp.sdp),peer.setRemoteDescription(new RTCSessionDescription(remoteSdp),function(){},function(error){connection.enableLogs&&console.error(JSON.stringify(error,null," "))})};var isOfferer=!0;config.remoteSdp&&(isOfferer=!1),connection.session.data===!0&&createDataChannel(),config.remoteSdp&&(config.remoteSdp.remotePeerSdpConstraints&&(sdpConstraints=config.remoteSdp.remotePeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),this.addRemoteSdp(config.remoteSdp)),("two-way"==connection.session.audio||"two-way"==connection.session.video||"two-way"==connection.session.screen)&&(defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:"two-way"==connection.session.audio||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio,OfferToReceiveVideo:"two-way"==connection.session.video||"two-way"==connection.session.screen||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio}));var streamsToShare={};peer.getLocalStreams().forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),peer[isOfferer?"createOffer":"createAnswer"](function(localSdp){localSdp.sdp=connection.processSdp(localSdp.sdp),peer.setLocalDescription(localSdp),config.onLocalSdp({type:localSdp.type,sdp:localSdp.sdp,remotePeerSdpConstraints:config.remotePeerSdpConstraints||!1,renegotiatingPeer:!!config.renegotiatingPeer||!1,connectionDescription:that.connectionDescription,dontGetRemoteStream:!!config.dontGetRemoteStream,extra:connection?connection.extra:{},streamsToShare:streamsToShare,isFirefoxOffered:isFirefox})},function(error){connection.enableLogs&&console.error("sdp-error",error)},defaults.sdpConstraints),peer.nativeClose=peer.close,peer.close=function(){if(peer){try{-1===peer.iceConnectionState.search(/closed|failed/gi)&&peer.getRemoteStreams().forEach(function(stream){stream.stop()}),peer.nativeClose()}catch(e){}peer=null,that.peer=null}},this.peer=peer}function setCordovaAPIs(){if("undefined"!=typeof cordova&&"undefined"!=typeof cordova.plugins&&"undefined"!=typeof cordova.plugins.iosrtc&&window.device&&"iOS"===window.device.platform){var iosrtc=cordova.plugins.iosrtc;RTCPeerConnection=iosrtc.RTCPeerConnection,RTCSessionDescription=iosrtc.RTCSessionDescription,RTCIceCandidate=iosrtc.RTCIceCandidate,MediaStream=iosrtc.MediaStream,MediaStreamTrack=iosrtc.MediaStreamTrack,iosrtc.debug.enable("iosrtc*"),iosrtc.registerGlobals()}}function loadIceFrame(callback,skip){if(!loadedIceFrame){if(!skip)return loadIceFrame(callback,!0);loadedIceFrame=!0;var iframe=document.createElement("iframe");iframe.onload=function(){function iFrameLoaderCallback(event){event.data&&event.data.iceServers&&(callback(event.data.iceServers), -window.removeEventListener("message",iFrameLoaderCallback))}iframe.isLoaded=!0,listenEventHandler("message",iFrameLoaderCallback),iframe.contentWindow.postMessage("get-ice-servers","*")},iframe.src="https://cdn.webrtc-experiment.com/getIceServers/",iframe.style.display="none",(document.body||document.documentElement).appendChild(iframe)}}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}function setStreamType(constraints,stream){constraints.mandatory&&constraints.mandatory.chromeMediaSource?stream.isScreen=!0:constraints.mozMediaSource||constraints.mediaSource?stream.isScreen=!0:constraints.video?stream.isVideo=!0:constraints.audio&&(stream.isAudio=!0)}function getUserMediaHandler(options){function streaming(stream,returnBack){setStreamType(options.localMediaConstraints,stream),options.onGettingLocalMedia(stream,returnBack),stream.addEventListener("ended",function(){delete currentUserMediaRequest.streams[idInstance],currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.indexOf(options)&&(delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)],currentUserMediaRequest.queueRequests=removeNullEntries(currentUserMediaRequest.queueRequests))},!1),currentUserMediaRequest.streams[idInstance]={stream:stream},currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.length&&getUserMediaHandler(currentUserMediaRequest.queueRequests.shift())}if(currentUserMediaRequest.mutex===!0)return void currentUserMediaRequest.queueRequests.push(options);currentUserMediaRequest.mutex=!0;var idInstance=JSON.stringify(options.localMediaConstraints);if(currentUserMediaRequest.streams[idInstance])streaming(currentUserMediaRequest.streams[idInstance].stream,!0);else{if(isPluginRTC){document.createElement("video");return void Plugin.getUserMedia({audio:!0,video:!0},function(stream){stream.streamid=stream.id||getRandomString(),streaming(stream)},function(error){})}"undefined"!=typeof DetectRTC&&(DetectRTC.hasMicrophone||(options.localMediaConstraints.audio=!1),DetectRTC.hasWebcam||(options.localMediaConstraints.video=!1)),navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream){stream.streamid=stream.streamid||stream.id||getRandomString(),stream.idInstance=idInstance,streaming(stream)})["catch"](function(error){options.onLocalMediaError(error,options.localMediaConstraints)})}}function TextReceiver(connection){function receive(data,userid,extra){var uuid=data.uuid;if(content[uuid]||(content[uuid]=[]),content[uuid].push(data.message),data.last){var message=content[uuid].join("");data.isobject&&(message=JSON.parse(message));var receivingTime=(new Date).getTime(),latency=receivingTime-data.sendingTime,e={data:message,userid:userid,extra:extra,latency:latency};connection.autoTranslateText?(e.original=e.data,connection.Translator.TranslateText(e.data,function(translatedText){e.data=translatedText,connection.onmessage(e)})):connection.onmessage(e),delete content[uuid]}}var content={};return{receive:receive}}var isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode,isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);"undefined"!=typeof cordova&&(isMobileDevice=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0);var isPluginRTC=!isMobileDevice&&(isSafari||isIE),chromeVersion=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),50),matchArray=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);isChrome&&matchArray&&matchArray[2]&&(chromeVersion=parseInt(matchArray[2],10));var firefoxVersion=50;matchArray=navigator.userAgent.match(/Firefox\/(.*)/),isFirefox&&matchArray&&matchArray[1]&&(firefoxVersion=parseInt(matchArray[1],10)),window.addEventListener||(window.addEventListener=function(el,eventName,eventHandler){el.attachEvent&&el.attachEvent("on"+eventName,eventHandler)}),window.attachEventListener=function(video,type,listener,useCapture){video.addEventListener(type,listener,useCapture)};var MediaStream=window.MediaStream;"undefined"==typeof MediaStream&&"undefined"!=typeof webkitMediaStream&&(MediaStream=webkitMediaStream),"undefined"==typeof MediaStream||"stop"in MediaStream.prototype||(MediaStream.prototype.stop=function(){!this.getAudioTracks&&this.getTracks&&(this.getAudioTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("audio")&&array.push(track)}),array}),!this.getVideoTracks&&this.getTracks&&(this.getVideoTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("video")&&array.push(track)}),array}),this.getAudioTracks().forEach(function(track){track.stop&&track.stop()}),this.getVideoTracks().forEach(function(track){track.stop&&track.stop()}),isFirefox&&fireEvent(this,"ended")}),"undefined"!=typeof MediaStream&&("getAudioTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getAudioTracks||(MediaStream.prototype.getAudioTracks=function(){}),"getVideoTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getVideoTracks||(MediaStream.prototype.getVideoTracks=function(){}));var lastChanges="";!function(){function LoadPluginRTC(){function getPlugin(){return document.getElementById("WebrtcEverywherePluginId")}window.PluginRTC={};var extractPluginObj=function(elt){return elt.isWebRtcPlugin?elt:elt.pluginObj},attachEventListener=function(elt,type,listener,useCapture){var _pluginObj=extractPluginObj(elt);_pluginObj?_pluginObj.bindEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEventListener?elt.addEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEvent&&elt.addEventListener("on"+type,listener,useCapture)},installPlugin=function(){if(!document.getElementById("WebrtcEverywherePluginId")){var pluginObj=document.createElement("object");isIE?pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):pluginObj.setAttribute("type","application/webrtc-everywhere"),pluginObj.setAttribute("id","WebrtcEverywherePluginId"),(document.body||document.documentElement).appendChild(pluginObj),pluginObj.setAttribute("width","0"),pluginObj.setAttribute("height","0")}};document.body?installPlugin():(attachEventListener(window,"load",function(){installPlugin()}),attachEventListener(document,"readystatechange",function(){"complete"==document.readyState&&installPlugin()}));var getUserMediaDelayed;window.PluginRTC.getUserMedia=navigator.getUserMedia=function(constraints,successCallback,errorCallback){"complete"!==document.readyState?getUserMediaDelayed||(getUserMediaDelayed=!0,attachEventListener(document,"readystatechange",function(){getUserMediaDelayed&&"complete"==document.readyState&&(getUserMediaDelayed=!1,getPlugin().getUserMedia(constraints,successCallback,errorCallback))})):getPlugin().getUserMedia(constraints,successCallback,errorCallback)},window.PluginRTC.attachMediaStream=function(element,stream){if(element.isWebRtcPlugin)return element.src=stream,element;if("video"===element.nodeName.toLowerCase()){if(!element.pluginObj&&stream){var _pluginObj=document.createElement("object"),_isIE=Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window;_isIE?_pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):_pluginObj.setAttribute("type","application/webrtc-everywhere"),element.pluginObj=_pluginObj,_pluginObj.setAttribute("className",element.className),_pluginObj.setAttribute("innerHTML",element.innerHTML);var width=element.getAttribute("width"),height=element.getAttribute("height"),bounds=element.getBoundingClientRect();if(width||(width=bounds.right-bounds.left),height||(height=bounds.bottom-bounds.top),"getComputedStyle"in window){var computedStyle=window.getComputedStyle(element,null);width||"auto"==computedStyle.width||"0px"==computedStyle.width||(width=computedStyle.width),height||"auto"==computedStyle.height||"0px"==computedStyle.height||(height=computedStyle.height)}width?_pluginObj.setAttribute("width",width):_pluginObj.setAttribute("autowidth",!0),height?_pluginObj.setAttribute("height",height):_pluginObj.setAttribute("autoheight",!0),(document.body||document.documentElement).appendChild(_pluginObj),element.parentNode&&(element.parentNode.replaceChild(_pluginObj,element),document.body.appendChild(element),element.style.visibility="hidden")}return element.pluginObj&&(element.pluginObj.bindEventListener("play",function(objvid){element.pluginObj&&(element.pluginObj.getAttribute("autowidth")&&objvid.videoWidth&&element.pluginObj.setAttribute("width",objvid.videoWidth),element.pluginObj.getAttribute("autoheight")&&objvid.videoHeight&&element.pluginObj.setAttribute("height",objvid.videoHeight))}),element.pluginObj.src=stream),element.pluginObj}return"audio"===element.nodeName.toLowerCase()?element:void 0},window.PluginRTC.MediaStreamTrack={};var getSourcesDelayed;window.PluginRTC.MediaStreamTrack.getSources=function(gotSources){"complete"!==document.readyState?getSourcesDelayed||(getSourcesDelayed=!0,attachEventListener(document,"readystatechange",function(){getSourcesDelayed&&"complete"==document.readyState&&(getSourcesDelayed=!1,getPlugin().getSources(gotSources))})):getPlugin().getSources(gotSources)},window.PluginRTC.RTCPeerConnection=function(configuration,constraints){return getPlugin().createPeerConnection(configuration,constraints)},window.PluginRTC.RTCIceCandidate=function(RTCIceCandidateInit){return getPlugin().createIceCandidate(RTCIceCandidateInit)},window.PluginRTC.RTCSessionDescription=function(RTCSessionDescriptionInit){return getPlugin().createSessionDescription(RTCSessionDescriptionInit)},window.onPluginRTCInitialized&&window.onPluginRTCInitialized(window.PluginRTC)}var ua=navigator.userAgent.toLowerCase(),isSafari=-1!=ua.indexOf("safari")&&-1==ua.indexOf("chrome"),isIE=!!(Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window);(isSafari||isIE)&&window.addEventListener("load",LoadPluginRTC,!1)}();var RTCPeerConnection,defaults={};"undefined"!=typeof mozRTCPeerConnection?RTCPeerConnection=mozRTCPeerConnection:"undefined"!=typeof webkitRTCPeerConnection?RTCPeerConnection=webkitRTCPeerConnection:"undefined"!=typeof window.RTCPeerConnection?RTCPeerConnection=window.RTCPeerConnection:(console.error("WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser."),RTCPeerConnection=window.RTCSessionDescription=window.RTCIceCandidate=function(){});var RTCSessionDescription=window.RTCSessionDescription||window.mozRTCSessionDescription,RTCIceCandidate=window.RTCIceCandidate||window.mozRTCIceCandidate,MediaStreamTrack=window.MediaStreamTrack,Plugin={};"undefined"!=typeof PluginRTC&&onPluginRTCInitialized(PluginRTC);var isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isIE=!!document.documentMode,isPluginRTC=isSafari||isIE;document.addEventListener("deviceready",setCordovaAPIs,!1),setCordovaAPIs();var CodecsHandler=function(){function removeVPX(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtpmap:100 VP8/90000\r\n",""),sdp=sdp.replace("a=rtpmap:101 VP9/90000\r\n",""),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace("a=rtcp-fb:120 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 ccm fir\r\n","")}function disableNACK(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtcp-fb:126 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:126 nack pli\r\n","a=rtcp-fb:126 pli\r\n"),sdp=sdp.replace("a=rtcp-fb:97 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:97 nack pli\r\n","a=rtcp-fb:97 pli\r\n")}function prioritize(codecMimeType,peer){if(peer&&peer.getSenders&&peer.getSenders().length){if(!codecMimeType||"string"!=typeof codecMimeType)throw"Invalid arguments.";peer.getSenders().forEach(function(sender){for(var params=sender.getParameters(),i=0;ii;++i)if(0===sdpLines[i].indexOf(prefix)&&(!substr||-1!==sdpLines[i].toLowerCase().indexOf(substr.toLowerCase())))return i;return null}function getCodecPayloadType(sdpLine){var pattern=new RegExp("a=rtpmap:(\\d+) \\w+\\/\\d+"),result=sdpLine.match(pattern);return result&&2===result.length?result[1]:null}function setVideoBitrates(sdp,params){if(isMobileDevice)return sdp;params=params||{};var vp8Payload,xgoogle_min_bitrate=params.min,xgoogle_max_bitrate=params.max,sdpLines=sdp.split("\r\n"),vp8Index=findLine(sdpLines,"a=rtpmap","VP8/90000");if(vp8Index&&(vp8Payload=getCodecPayloadType(sdpLines[vp8Index])),!vp8Payload)return sdp;var rtxPayload,rtxIndex=findLine(sdpLines,"a=rtpmap","rtx/90000");if(rtxIndex&&(rtxPayload=getCodecPayloadType(sdpLines[rtxIndex])),!rtxIndex)return sdp;var rtxFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+rtxPayload.toString());if(null!==rtxFmtpLineIndex){var appendrtxNext="\r\n";appendrtxNext+="a=fmtp:"+vp8Payload+" x-google-min-bitrate="+(xgoogle_min_bitrate||"228")+"; x-google-max-bitrate="+(xgoogle_max_bitrate||"228"),sdpLines[rtxFmtpLineIndex]=sdpLines[rtxFmtpLineIndex].concat(appendrtxNext),sdp=sdpLines.join("\r\n")}return sdp}function setOpusAttributes(sdp,params){if(isMobileDevice)return sdp;params=params||{};var opusPayload,sdpLines=sdp.split("\r\n"),opusIndex=findLine(sdpLines,"a=rtpmap","opus/48000");if(opusIndex&&(opusPayload=getCodecPayloadType(sdpLines[opusIndex])),!opusPayload)return sdp;var opusFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+opusPayload.toString());if(null===opusFmtpLineIndex)return sdp;var appendOpusNext="";return appendOpusNext+="; stereo="+("undefined"!=typeof params.stereo?params.stereo:"1"),appendOpusNext+="; sprop-stereo="+("undefined"!=typeof params["sprop-stereo"]?params["sprop-stereo"]:"1"),"undefined"!=typeof params.maxaveragebitrate&&(appendOpusNext+="; maxaveragebitrate="+(params.maxaveragebitrate||1048576)),"undefined"!=typeof params.maxplaybackrate&&(appendOpusNext+="; maxplaybackrate="+(params.maxplaybackrate||1048576)),"undefined"!=typeof params.cbr&&(appendOpusNext+="; cbr="+("undefined"!=typeof params.cbr?params.cbr:"1")),"undefined"!=typeof params.useinbandfec&&(appendOpusNext+="; useinbandfec="+params.useinbandfec),"undefined"!=typeof params.usedtx&&(appendOpusNext+="; usedtx="+params.usedtx),"undefined"!=typeof params.maxptime&&(appendOpusNext+="\r\na=maxptime:"+params.maxptime),sdpLines[opusFmtpLineIndex]=sdpLines[opusFmtpLineIndex].concat(appendOpusNext),sdp=sdpLines.join("\r\n")}function preferVP9(sdp){return-1===sdp.indexOf("SAVPF 100 101")||-1===sdp.indexOf("VP9/90000")?sdp:sdp.replace("SAVPF 100 101","SAVPF 101 100")}var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);return"undefined"!=typeof cordova&&(isMobileDevice=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0),{removeVPX:removeVPX,disableNACK:disableNACK,prioritize:prioritize,removeNonG722:removeNonG722,setApplicationSpecificBandwidth:function(sdp,bandwidth,isScreen){return setBAS(sdp,bandwidth,isScreen)},setVideoBitrates:function(sdp,params){return setVideoBitrates(sdp,params)},setOpusAttributes:function(sdp,params){return setOpusAttributes(sdp,params)},preferVP9:preferVP9}}();window.BandwidthHandler=CodecsHandler;var loadedIceFrame,OnIceCandidateHandler=function(){function processCandidates(connection,icePair){var candidate=icePair.candidate,iceRestrictions=connection.candidates,stun=iceRestrictions.stun,turn=iceRestrictions.turn;if(isNull(iceRestrictions.reflexive)||(stun=iceRestrictions.reflexive),isNull(iceRestrictions.relay)||(turn=iceRestrictions.relay),(iceRestrictions.host||!candidate.match(/typ host/g))&&(turn||!candidate.match(/typ relay/g))&&(stun||!candidate.match(/typ srflx/g))){var protocol=connection.iceProtocols;if((protocol.udp||!candidate.match(/ udp /g))&&(protocol.tcp||!candidate.match(/ tcp /g)))return connection.enableLogs&&console.debug("Your candidate pairs:",candidate),{candidate:candidate,sdpMid:icePair.sdpMid,sdpMLineIndex:icePair.sdpMLineIndex}}}return{processCandidates:processCandidates}}();"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&loadIceFrame(function(externalIceServers){externalIceServers&&externalIceServers.length&&(window.RMCExternalIceServers=externalIceServers,window.iceServersLoadCallback&&"function"==typeof window.iceServersLoadCallback&&window.iceServersLoadCallback(externalIceServers))});var IceServersHandler=function(){function getIceServers(connection){var iceServers=[];return iceServers.push({urls:"stun:stun.l.google.com:19302"},{urls:"stun:mmt-stun.verkstad.net"},{urls:"stun:stun.anyfirewall.com:3478"}),iceServers.push({urls:"turn:turn.bistri.com:80",credential:"homeo",username:"homeo"}),iceServers.push({urls:"turn:turn.anyfirewall.com:443",credential:"webrtc",username:"webrtc"}),iceServers.push({urls:"turn:mmt-turn.verkstad.net",username:"webrtc",credential:"secret"}),window.RMCExternalIceServers?(iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers):"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&(window.iceServersLoadCallback=function(){iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers}),iceServers}return{getIceServers:getIceServers}}(),getUserMedia=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils=window.webrtcUtils||{};if(webrtcUtils.enableLogs||(webrtcUtils.enableLogs=!0),webrtcUtils.log||(webrtcUtils.log=function(){webrtcUtils.enableLogs&&("undefined"!=typeof module||"function"==typeof require&&"function"==typeof define||console.log.apply(console,arguments))}),webrtcUtils.extractVersion||(webrtcUtils.extractVersion=function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos],10)}),"object"==typeof window&&(!window.HTMLMediaElement||"srcObject"in window.HTMLMediaElement.prototype||Object.defineProperty(window.HTMLMediaElement.prototype,"srcObject",{get:function(){return"mozSrcObject"in this?this.mozSrcObject:this._srcObject},set:function(stream){"mozSrcObject"in this?this.mozSrcObject=stream:(this._srcObject=stream,this.src=stream?URL.createObjectURL(stream):null)}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia&&window.mozRTCPeerConnection){if(webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,getUserMedia=function(constraints,onSuccess,onError){var constraintsToFF37=function(c){if("object"!=typeof c||c.require)return c;var require=[];return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r=c[key]="object"==typeof c[key]?c[key]:{ideal:c[key]};if((void 0!==r.min||void 0!==r.max||void 0!==r.exact)&&require.push(key),void 0!==r.exact&&("number"==typeof r.exact?r.min=r.max=r.exact:c[key]=r.exact,delete r.exact),void 0!==r.ideal){c.advanced=c.advanced||[];var oc={};"number"==typeof r.ideal?oc[key]={min:r.ideal,max:r.ideal}:oc[key]=r.ideal,c.advanced.push(oc),delete r.ideal,Object.keys(r).length||delete c[key]}}}),require.length&&(c.require=require),c};return 38>webrtcDetectedVersion&&(webrtcUtils.log("spec: "+JSON.stringify(constraints)),constraints.audio&&(constraints.audio=constraintsToFF37(constraints.audio)),constraints.video&&(constraints.video=constraintsToFF37(constraints.video)),webrtcUtils.log("ff37: "+JSON.stringify(constraints))),navigator.mozGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia,addEventListener:function(){},removeEventListener:function(){}}),navigator.mediaDevices.enumerateDevices=navigator.mediaDevices.enumerateDevices||function(){return new Promise(function(resolve){var infos=[{kind:"audioinput",deviceId:"default",label:"",groupId:""},{kind:"videoinput",deviceId:"default",label:"",groupId:""}];resolve(infos)})},41>webrtcDetectedVersion){var orgEnumerateDevices=navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);navigator.mediaDevices.enumerateDevices=function(){return orgEnumerateDevices().then(void 0,function(e){if("NotFoundError"===e.name)return[];throw e})}}}else if(navigator.webkitGetUserMedia&&window.webkitRTCPeerConnection){webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38;var constraintsToChrome=function(c){if("object"!=typeof c||c.mandatory||c.optional)return c;var cc={};return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r="object"==typeof c[key]?c[key]:{ideal:c[key]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);var oldname=function(prefix,name){return prefix?prefix+name.charAt(0).toUpperCase()+name.slice(1):"deviceId"===name?"sourceId":name};if(void 0!==r.ideal){cc.optional=cc.optional||[];var oc={};"number"==typeof r.ideal?(oc[oldname("min",key)]=r.ideal,cc.optional.push(oc),oc={},oc[oldname("max",key)]=r.ideal,cc.optional.push(oc)):(oc[oldname("",key)]=r.ideal,cc.optional.push(oc))}void 0!==r.exact&&"number"!=typeof r.exact?(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname("",key)]=r.exact):["min","max"].forEach(function(mix){void 0!==r[mix]&&(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname(mix,key)]=r[mix])})}}),c.advanced&&(cc.optional=(cc.optional||[]).concat(c.advanced)),cc};if(getUserMedia=function(constraints,onSuccess,onError){return constraints.audio&&(constraints.audio=constraintsToChrome(constraints.audio)),constraints.video&&(constraints.video=constraintsToChrome(constraints.video)),webrtcUtils.log("chrome: "+JSON.stringify(constraints)),navigator.webkitGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia}),navigator.mediaDevices.getUserMedia){var origGetUserMedia=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=function(c){return webrtcUtils.log("spec: "+JSON.stringify(c)),c.audio=constraintsToChrome(c.audio),c.video=constraintsToChrome(c.video),webrtcUtils.log("chrome: "+JSON.stringify(c)),origGetUserMedia(c)}}else navigator.mediaDevices.getUserMedia=function(constraints){return requestUserMedia(constraints)};"undefined"==typeof navigator.mediaDevices.addEventListener&&(navigator.mediaDevices.addEventListener=function(){webrtcUtils.log("Dummy mediaDevices.addEventListener called.")}),"undefined"==typeof navigator.mediaDevices.removeEventListener&&(navigator.mediaDevices.removeEventListener=function(){webrtcUtils.log("Dummy mediaDevices.removeEventListener called.")})}else navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?(webrtcUtils.log("This appears to be Edge"),webrtcDetectedBrowser="edge",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),webrtcMinimumVersion=12):webrtcUtils.log("Browser does not appear to be WebRTC-capable");else webrtcDetectedBrowser="not a browser";"undefined"!=typeof module?module.exports={getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}:"function"==typeof require&&"function"==typeof define&&define([],function(){return{getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}}),"undefined"!=typeof webrtcUtils&&(webrtcUtils.enableLogs=!1);var currentUserMediaRequest={streams:[],mutex:!1,queueRequests:[],remove:function(idInstance){this.mutex=!1;var stream=this.streams[idInstance];if(stream){stream=stream.stream;var options=stream.currentUserMediaRequestOptions;this.queueRequests.indexOf(options)&&(delete this.queueRequests[this.queueRequests.indexOf(options)],this.queueRequests=removeNullEntries(this.queueRequests)),this.streams[idInstance].stream=null,delete this.streams[idInstance]}}},StreamsHandler=function(){function handleType(type){return type?"string"==typeof type||"undefined"==typeof type?type:type.audio&&type.video?null:type.audio?"audio":type.video?"video":void 0:void 0}function setHandlers(stream,syncAction,connection){function graduallyIncreaseVolume(){if(connection.streamEvents[stream.streamid].mediaElement){var mediaElement=connection.streamEvents[stream.streamid].mediaElement;mediaElement.volume=0,afterEach(200,5,function(){mediaElement.volume+=.2})}}("undefined"==typeof syncAction||1==syncAction)&&stream.addEventListener("ended",function(){StreamsHandler.onSyncNeeded(this.streamid,"ended")},!1),stream.mute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!1,connection.streamEvents[stream.streamid].isAudioMuted=!0}),("undefined"==typeof type||"video"==type)&&stream.getVideoTracks().forEach(function(track){track.enabled=!1}),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"mute",type),connection.streamEvents[stream.streamid].muteType=type||"both",fireEvent(stream,"mute",type)},stream.unmute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),graduallyIncreaseVolume(),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!0,connection.streamEvents[stream.streamid].isAudioMuted=!1}),("undefined"==typeof type||"video"==type)&&(stream.getVideoTracks().forEach(function(track){track.enabled=!0}),"undefined"!=typeof type&&"video"==type&&connection.streamEvents[stream.streamid].isAudioMuted&&!function looper(times){times||(times=0),times++,100>times&&connection.streamEvents[stream.streamid].isAudioMuted&&(stream.mute("audio"),setTimeout(function(){looper(times)},50))}()),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"unmute",type),connection.streamEvents[stream.streamid].unmuteType=type||"both",fireEvent(stream,"unmute",type)}}function afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes){startedTimes=(startedTimes||0)+1,startedTimes>=numberOfTimes||setTimeout(function(){callback(),afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes)},setTimeoutInteval)}return{setHandlers:setHandlers,onSyncNeeded:function(streamid,action,type){}}}();!function(){function getBrowserInfo(){var nameOffset,verOffset,ix,nAgt=(navigator.appVersion,navigator.userAgent),browserName=navigator.appName,fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10);if(isOpera){browserName="Opera";try{fullVersion=navigator.userAgent.split("OPR/")[1].split(" ")[0],majorVersion=fullVersion.split(".")[0]}catch(e){fullVersion="0.0.0.0",majorVersion=0}}else isIE?(verOffset=nAgt.indexOf("MSIE"),browserName="IE",fullVersion=nAgt.substring(verOffset+5)):isChrome?(verOffset=nAgt.indexOf("Chrome"),browserName="Chrome",fullVersion=nAgt.substring(verOffset+7)):isSafari?(verOffset=nAgt.indexOf("Safari"),browserName="Safari",fullVersion=nAgt.substring(verOffset+7),-1!==(verOffset=nAgt.indexOf("Version"))&&(fullVersion=nAgt.substring(verOffset+8))):isFirefox?(verOffset=nAgt.indexOf("Firefox"),browserName="Firefox",fullVersion=nAgt.substring(verOffset+8)):(nameOffset=nAgt.lastIndexOf(" ")+1)<(verOffset=nAgt.lastIndexOf("/"))&&(browserName=nAgt.substring(nameOffset,verOffset),fullVersion=nAgt.substring(verOffset+1),browserName.toLowerCase()===browserName.toUpperCase()&&(browserName=navigator.appName));return isEdge&&(browserName="Edge",fullVersion=parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2],10).toString()),-1!==(ix=fullVersion.indexOf(";"))&&(fullVersion=fullVersion.substring(0,ix)),-1!==(ix=fullVersion.indexOf(" "))&&(fullVersion=fullVersion.substring(0,ix)),majorVersion=parseInt(""+fullVersion,10),isNaN(majorVersion)&&(fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10)),{fullVersion:fullVersion,version:majorVersion,name:browserName,isPrivateBrowsing:!1}}function retry(isDone,next){var currentTrial=0,maxRetry=50,isTimeout=!1,id=window.setInterval(function(){isDone()&&(window.clearInterval(id),next(isTimeout)),currentTrial++>maxRetry&&(window.clearInterval(id),isTimeout=!0,next(isTimeout))},10)}function isIE10OrLater(userAgent){var ua=userAgent.toLowerCase();if(0===ua.indexOf("msie")&&0===ua.indexOf("trident"))return!1;var match=/(?:msie|rv:)\s?([\d\.]+)/.exec(ua);return match&&parseInt(match[1],10)>=10?!0:!1}function detectPrivateMode(callback){var isPrivate;if(window.webkitRequestFileSystem)window.webkitRequestFileSystem(window.TEMPORARY,1,function(){isPrivate=!1},function(e){console.log(e),isPrivate=!0});else if(window.indexedDB&&/Firefox/.test(window.navigator.userAgent)){var db;try{db=window.indexedDB.open("test")}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&retry(function(){return"done"===db.readyState?!0:!1},function(isTimeout){isTimeout||(isPrivate=db.result?!1:!0)})}else if(isIE10OrLater(window.navigator.userAgent)){isPrivate=!1;try{window.indexedDB||(isPrivate=!0)}catch(e){isPrivate=!0}}else if(window.localStorage&&/Safari/.test(window.navigator.userAgent)){try{window.localStorage.setItem("test",1)}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&(isPrivate=!1,window.localStorage.removeItem("test")); -}retry(function(){return"undefined"!=typeof isPrivate?!0:!1},function(isTimeout){callback(isPrivate)})}function detectDesktopOS(){var unknown="-",nVer=navigator.appVersion,nAgt=navigator.userAgent,os=unknown,clientStrings=[{s:"Windows 10",r:/(Windows 10.0|Windows NT 10.0)/},{s:"Windows 8.1",r:/(Windows 8.1|Windows NT 6.3)/},{s:"Windows 8",r:/(Windows 8|Windows NT 6.2)/},{s:"Windows 7",r:/(Windows 7|Windows NT 6.1)/},{s:"Windows Vista",r:/Windows NT 6.0/},{s:"Windows Server 2003",r:/Windows NT 5.2/},{s:"Windows XP",r:/(Windows NT 5.1|Windows XP)/},{s:"Windows 2000",r:/(Windows NT 5.0|Windows 2000)/},{s:"Windows ME",r:/(Win 9x 4.90|Windows ME)/},{s:"Windows 98",r:/(Windows 98|Win98)/},{s:"Windows 95",r:/(Windows 95|Win95|Windows_95)/},{s:"Windows NT 4.0",r:/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/},{s:"Windows CE",r:/Windows CE/},{s:"Windows 3.11",r:/Win16/},{s:"Android",r:/Android/},{s:"Open BSD",r:/OpenBSD/},{s:"Sun OS",r:/SunOS/},{s:"Linux",r:/(Linux|X11)/},{s:"iOS",r:/(iPhone|iPad|iPod)/},{s:"Mac OS X",r:/Mac OS X/},{s:"Mac OS",r:/(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},{s:"QNX",r:/QNX/},{s:"UNIX",r:/UNIX/},{s:"BeOS",r:/BeOS/},{s:"OS/2",r:/OS\/2/},{s:"Search Bot",r:/(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/}];for(var id in clientStrings){var cs=clientStrings[id];if(cs.r.test(nAgt)){os=cs.s;break}}var osVersion=unknown;switch(/Windows/.test(os)&&(osVersion=/Windows (.*)/.exec(os)[1],os="Windows"),os){case"Mac OS X":osVersion=/Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1];break;case"Android":osVersion=/Android ([\.\_\d]+)/.exec(nAgt)[1];break;case"iOS":osVersion=/OS (\d+)_(\d+)_?(\d+)?/.exec(nVer),osVersion=osVersion[1]+"."+osVersion[2]+"."+(0|osVersion[3])}return{osName:os,osVersion:osVersion}}function DetectLocalIPAddress(callback){DetectRTC.isWebRTCSupported&&(DetectRTC.isORTCSupported||getIPs(function(ip){callback(ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)?"Local: "+ip:"Public: "+ip)}))}function getIPs(callback){function handleCandidate(candidate){var ipRegex=/([0-9]{1,3}(\.[0-9]{1,3}){3})/,match=ipRegex.exec(candidate);if(!match)return void console.warn("Could not match IP address in",candidate);var ipAddress=match[1];void 0===ipDuplicates[ipAddress]&&callback(ipAddress),ipDuplicates[ipAddress]=!0}var ipDuplicates={},RTCPeerConnection=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection,useWebKit=!!window.webkitRTCPeerConnection;if(!RTCPeerConnection){var iframe=document.getElementById("iframe");if(!iframe)throw"NOTE: you need to have an iframe in the page right above the script tag.";var win=iframe.contentWindow;RTCPeerConnection=win.RTCPeerConnection||win.mozRTCPeerConnection||win.webkitRTCPeerConnection,useWebKit=!!win.webkitRTCPeerConnection}if(RTCPeerConnection){var servers,mediaConstraints={optional:[{RtpDataChannels:!0}]};useWebKit&&(servers={iceServers:[{urls:"stun:stun.services.mozilla.com"}]},"undefined"!=typeof DetectRTC&&DetectRTC.browser.isFirefox&&DetectRTC.browser.version<=38&&(servers[0]={url:servers[0].urls}));var pc=new RTCPeerConnection(servers,mediaConstraints);pc.onicecandidate=function(ice){ice.candidate&&handleCandidate(ice.candidate.candidate)},pc.createDataChannel(""),pc.createOffer(function(result){pc.setLocalDescription(result,function(){},function(){})},function(){}),setTimeout(function(){var lines=pc.localDescription.sdp.split("\n");lines.forEach(function(line){0===line.indexOf("a=candidate:")&&handleCandidate(line)})},1e3)}}function checkDeviceSupport(callback){if(canEnumerate){if(!navigator.enumerateDevices&&window.MediaStreamTrack&&window.MediaStreamTrack.getSources&&(navigator.enumerateDevices=window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack)),!navigator.enumerateDevices&&navigator.enumerateDevices&&(navigator.enumerateDevices=navigator.enumerateDevices.bind(navigator)),!navigator.enumerateDevices)return void(callback&&callback());MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[],navigator.enumerateDevices(function(devices){devices.forEach(function(_device){var device={};for(var d in _device)device[d]=_device[d];"audio"===device.kind&&(device.kind="audioinput"),"video"===device.kind&&(device.kind="videoinput");var skip;MediaDevices.forEach(function(d){d.id===device.id&&d.kind===device.kind&&(skip=!0)}),skip||(device.deviceId||(device.deviceId=device.id),device.id||(device.id=device.deviceId),device.label?("videoinput"!==device.kind||isWebsiteHasWebcamPermissions||(isWebsiteHasWebcamPermissions=!0),"audioinput"!==device.kind||isWebsiteHasMicrophonePermissions||(isWebsiteHasMicrophonePermissions=!0)):(device.label="Please invoke getUserMedia once.","https:"!==location.protocol&&(device.label="HTTPs is required to get label of this "+device.kind+" device.")),"audioinput"===device.kind&&(hasMicrophone=!0,-1===audioInputDevices.indexOf(device)&&audioInputDevices.push(device)),"audiooutput"===device.kind&&(hasSpeakers=!0,-1===audioOutputDevices.indexOf(device)&&audioOutputDevices.push(device)),"videoinput"===device.kind&&(hasWebcam=!0,-1===videoInputDevices.indexOf(device)&&videoInputDevices.push(device)),-1===MediaDevices.indexOf(device)&&MediaDevices.push(device))}),"undefined"!=typeof DetectRTC&&(DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices),callback&&callback()})}}var navigator=window.navigator;"undefined"!=typeof navigator?("undefined"!=typeof navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),"undefined"!=typeof navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia)):navigator={getUserMedia:function(){},userAgent:"Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45"};var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i),isEdge=!(-1===navigator.userAgent.indexOf("Edge")||!navigator.msSaveOrOpenBlob&&!navigator.msSaveBlob),isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode&&!isEdge,isMobile={Android:function(){return navigator.userAgent.match(/Android/i)},BlackBerry:function(){return navigator.userAgent.match(/BlackBerry/i)},iOS:function(){return navigator.userAgent.match(/iPhone|iPad|iPod/i)},Opera:function(){return navigator.userAgent.match(/Opera Mini/i)},Windows:function(){return navigator.userAgent.match(/IEMobile/i)},any:function(){return isMobile.Android()||isMobile.BlackBerry()||isMobile.iOS()||isMobile.Opera()||isMobile.Windows()},getOsName:function(){var osName="Unknown OS";return isMobile.Android()&&(osName="Android"),isMobile.BlackBerry()&&(osName="BlackBerry"),isMobile.iOS()&&(osName="iOS"),isMobile.Opera()&&(osName="Opera Mini"),isMobile.Windows()&&(osName="Windows"),osName}},osName="Unknown OS",osVersion="Unknown OS Version";if(isMobile.any())osName=isMobile.getOsName();else{var osInfo=detectDesktopOS();osName=osInfo.osName,osVersion=osInfo.osVersion}var isCanvasSupportsStreamCapturing=!1,isVideoSupportsStreamCapturing=!1;["captureStream","mozCaptureStream","webkitCaptureStream"].forEach(function(item){!isCanvasSupportsStreamCapturing&&item in document.createElement("canvas")&&(isCanvasSupportsStreamCapturing=!0),!isVideoSupportsStreamCapturing&&item in document.createElement("video")&&(isVideoSupportsStreamCapturing=!0)});var MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[];navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(navigator.enumerateDevices=function(callback){navigator.mediaDevices.enumerateDevices().then(callback)});var canEnumerate=!1;"undefined"!=typeof MediaStreamTrack&&"getSources"in MediaStreamTrack?canEnumerate=!0:navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(canEnumerate=!0);var hasMicrophone=!1,hasSpeakers=!1,hasWebcam=!1,isWebsiteHasMicrophonePermissions=!1,isWebsiteHasWebcamPermissions=!1;checkDeviceSupport();var DetectRTC=window.DetectRTC||{};DetectRTC.browser=getBrowserInfo(),detectPrivateMode(function(isPrivateBrowsing){DetectRTC.browser.isPrivateBrowsing=!!isPrivateBrowsing}),DetectRTC.browser["is"+DetectRTC.browser.name]=!0;var isWebRTCSupported=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),!1);["RTCPeerConnection","webkitRTCPeerConnection","mozRTCPeerConnection","RTCIceGatherer"].forEach(function(item){isWebRTCSupported||item in window&&(isWebRTCSupported=!0)}),DetectRTC.isWebRTCSupported=isWebRTCSupported,DetectRTC.isORTCSupported="undefined"!=typeof RTCIceGatherer;var isScreenCapturingSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>=35?isScreenCapturingSupported=!0:DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=34&&(isScreenCapturingSupported=!0),"https:"!==location.protocol&&(isScreenCapturingSupported=!1),DetectRTC.isScreenCapturingSupported=isScreenCapturingSupported;var webAudio={isSupported:!1,isCreateMediaStreamSourceSupported:!1};["AudioContext","webkitAudioContext","mozAudioContext","msAudioContext"].forEach(function(item){webAudio.isSupported||item in window&&(webAudio.isSupported=!0,"createMediaStreamSource"in window[item].prototype&&(webAudio.isCreateMediaStreamSourceSupported=!0))}),DetectRTC.isAudioContextSupported=webAudio.isSupported,DetectRTC.isCreateMediaStreamSourceSupported=webAudio.isCreateMediaStreamSourceSupported;var isRtpDataChannelsSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>31&&(isRtpDataChannelsSupported=!0),DetectRTC.isRtpDataChannelsSupported=isRtpDataChannelsSupported;var isSCTPSupportd=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>28?isSCTPSupportd=!0:DetectRTC.browser.isChrome&&DetectRTC.browser.version>25?isSCTPSupportd=!0:DetectRTC.browser.isOpera&&DetectRTC.browser.version>=11&&(isSCTPSupportd=!0),DetectRTC.isSctpDataChannelsSupported=isSCTPSupportd,DetectRTC.isMobileDevice=isMobileDevice;var isGetUserMediaSupported=!1;navigator.getUserMedia?isGetUserMediaSupported=!0:navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&(isGetUserMediaSupported=!0),DetectRTC.browser.isChrome&&DetectRTC.browser.version>=46&&"https:"!==location.protocol&&(DetectRTC.isGetUserMediaSupported="Requires HTTPs"),DetectRTC.isGetUserMediaSupported=isGetUserMediaSupported,DetectRTC.osName=osName,DetectRTC.osVersion=osVersion;var displayResolution="";if(screen.width){var width=screen.width?screen.width:"",height=screen.height?screen.height:"";displayResolution+=""+width+" x "+height}DetectRTC.displayResolution=displayResolution,DetectRTC.isCanvasSupportsStreamCapturing=isCanvasSupportsStreamCapturing,DetectRTC.isVideoSupportsStreamCapturing=isVideoSupportsStreamCapturing,DetectRTC.DetectLocalIPAddress=DetectLocalIPAddress,DetectRTC.isWebSocketsSupported="WebSocket"in window&&2===window.WebSocket.CLOSING,DetectRTC.isWebSocketsBlocked=!DetectRTC.isWebSocketsSupported,DetectRTC.checkWebSocketsSupport=function(callback){callback=callback||function(){};try{var websocket=new WebSocket("wss://echo.websocket.org:443/");websocket.onopen=function(){DetectRTC.isWebSocketsBlocked=!1,callback(),websocket.close(),websocket=null},websocket.onerror=function(){DetectRTC.isWebSocketsBlocked=!0,callback()}}catch(e){DetectRTC.isWebSocketsBlocked=!0,callback()}},DetectRTC.load=function(callback){callback=callback||function(){},checkDeviceSupport(callback)},DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices;var isSetSinkIdSupported=!1;"setSinkId"in document.createElement("video")&&(isSetSinkIdSupported=!0),DetectRTC.isSetSinkIdSupported=isSetSinkIdSupported;var isRTPSenderReplaceTracksSupported=!1;DetectRTC.browser.isFirefox?"getSenders"in mozRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0):DetectRTC.browser.isChrome&&"getSenders"in webkitRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0),DetectRTC.isRTPSenderReplaceTracksSupported=isRTPSenderReplaceTracksSupported;var isRemoteStreamProcessingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>38&&(isRemoteStreamProcessingSupported=!0),DetectRTC.isRemoteStreamProcessingSupported=isRemoteStreamProcessingSupported;var isApplyConstraintsSupported=!1;"undefined"!=typeof MediaStreamTrack&&"applyConstraints"in MediaStreamTrack.prototype&&(isApplyConstraintsSupported=!0),DetectRTC.isApplyConstraintsSupported=isApplyConstraintsSupported;var isMultiMonitorScreenCapturingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=43&&(isMultiMonitorScreenCapturingSupported=!0),DetectRTC.isMultiMonitorScreenCapturingSupported=isMultiMonitorScreenCapturingSupported,window.DetectRTC=DetectRTC}(),function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}(),function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}if(-1!==document.domain.indexOf("webrtc-experiment.com")){window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}}();var TextSender={send:function(config){function sendText(textMessage,text){var data={type:"text",uuid:uuid,sendingTime:sendingTime};textMessage&&(text=textMessage,data.packets=parseInt(text.length/packetSize)),text.length>packetSize?data.message=text.slice(0,packetSize):(data.message=text,data.last=!0,data.isobject=isobject),channel.send(data,remoteUserId),textToTransfer=text.slice(data.message.length),textToTransfer.length&&setTimeout(function(){sendText(null,textToTransfer)},connection.chunkInterval||100)}var connection=config.connection,channel=config.channel,remoteUserId=config.remoteUserId,initialText=config.text,packetSize=connection.chunkSize||1e3,textToTransfer="",isobject=!1;isString(initialText)||(isobject=!0,initialText=JSON.stringify(initialText));var uuid=getRandomString(),sendingTime=(new Date).getTime();sendText(initialText)}},FileProgressBarHandler=function(){function handle(connection){function updateLabel(progress,label){if(-1!==progress.position){var position=+progress.position.toFixed(2).split(".")[1]||100;label.innerHTML=position+"%"}}var progressHelper={};connection.onFileStart=function(file){var div=document.createElement("div");return div.title=file.name,div.innerHTML=" ",file.remoteUserId&&(div.innerHTML+=" (Sharing with:"+file.remoteUserId+")"),connection.filesContainer||(connection.filesContainer=document.body||document.documentElement),connection.filesContainer.insertBefore(div,connection.filesContainer.firstChild),file.remoteUserId?(progressHelper[file.uuid]||(progressHelper[file.uuid]={}),progressHelper[file.uuid][file.remoteUserId]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid][file.remoteUserId].progress.max=file.maxChunks)):(progressHelper[file.uuid]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid].progress.max=file.maxChunks))},connection.onFileProgress=function(chunk){var helper=progressHelper[chunk.uuid];helper&&(!chunk.remoteUserId||(helper=progressHelper[chunk.uuid][chunk.remoteUserId]))&&(helper.progress.value=chunk.currentPosition||chunk.maxChunks||helper.progress.max,updateLabel(helper.progress,helper.label))},connection.onFileEnd=function(file){var helper=progressHelper[file.uuid];if(!helper)return void console.error("No such progress-helper element exists.",file);if(!file.remoteUserId||(helper=progressHelper[file.uuid][file.remoteUserId])){var div=helper.div;-1!=file.type.indexOf("image")?div.innerHTML='Download '+file.name+'
':div.innerHTML='Download '+file.name+'
'}}}return{handle:handle}}(),TranslationHandler=function(){function handle(connection){connection.autoTranslateText=!1,connection.language="en",connection.googKey="AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE",connection.Translator={TranslateText:function(text,callback){var newScript=document.createElement("script");newScript.type="text/javascript";var sourceText=encodeURIComponent(text),randomNumber="method"+connection.token();window[randomNumber]=function(response){response.data&&response.data.translations[0]&&callback&&callback(response.data.translations[0].translatedText),response.error&&"Daily Limit Exceeded"===response.error.message&&(warn('Text translation failed. Error message: "Daily Limit Exceeded."'),callback(text))};var source="https://www.googleapis.com/language/translate/v2?key="+connection.googKey+"&target="+(connection.language||"en-US")+"&callback=window."+randomNumber+"&q="+sourceText;newScript.src=source,document.getElementsByTagName("head")[0].appendChild(newScript)}}}return{handle:handle}}();window.RTCMultiConnection=RTCMultiConnection}(); \ No newline at end of file +}),connection.mediaConstraints.audio.optional=newArray}if(connection.mediaConstraints.video&&connection.mediaConstraints.video.optional.length){var newArray=[];connection.mediaConstraints.video.optional.forEach(function(opt){"undefined"==typeof opt.bandwidth&&newArray.push(opt)}),connection.mediaConstraints.video.optional=newArray}}connection.getExtraData=function(remoteUserId){if(!remoteUserId)throw"remoteUserId is required.";return connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{}},forceOptions.autoOpenOrJoin&&connection.openOrJoin(connection.sessionid)}function SocketConnection(connection,connectCallback){var parameters="";parameters+="?userid="+connection.userid,parameters+="&msgEvent="+connection.socketMessageEvent,parameters+="&socketCustomEvent="+connection.socketCustomEvent,connection.enableScalableBroadcast&&(parameters+="&enableScalableBroadcast=true",parameters+="&maxRelayLimitPerUser="+(connection.maxRelayLimitPerUser||2));var socket;try{socket=io((connection.socketURL||"/")+parameters)}catch(e){socket=io.connect((connection.socketURL||"/")+parameters,connection.socketOptions)}var mPeer=connection.multiPeersHandler;return socket.on("extra-data-updated",function(remoteUserId,extra){connection.peers[remoteUserId]&&(connection.peers[remoteUserId].extra=extra,connection.onExtraDataUpdated({userid:remoteUserId,extra:extra}))}),socket.on(connection.socketMessageEvent,function(message){if(message.remoteUserId==connection.userid){if(connection.peers[message.sender]&&connection.peers[message.sender].extra!=message.extra&&(connection.peers[message.sender].extra=message.extra,connection.onExtraDataUpdated({userid:message.sender,extra:message.extra})),message.message.streamSyncNeeded&&connection.peers[message.sender]){var stream=connection.streamEvents[message.message.streamid];if(!stream||!stream.stream)return;var action=message.message.action;if("ended"===action||"stream-removed"===action)return void connection.onstreamended(stream);var type="both"!=message.message.type?message.message.type:null;return void stream.stream[action](type)}if("connectWithAllParticipants"===message.message)return-1===connection.broadcasters.indexOf(message.sender)&&connection.broadcasters.push(message.sender),void mPeer.onNegotiationNeeded({allParticipants:connection.getAllParticipants(message.sender)},message.sender);if("removeFromBroadcastersList"===message.message)return void(-1!==connection.broadcasters.indexOf(message.sender)&&(delete connection.broadcasters[connection.broadcasters.indexOf(message.sender)],connection.broadcasters=removeNullEntries(connection.broadcasters)));if("dropPeerConnection"===message.message)return void connection.deletePeer(message.sender);if(message.message.allParticipants)return-1===message.message.allParticipants.indexOf(message.sender)&&message.message.allParticipants.push(message.sender),void message.message.allParticipants.forEach(function(participant){mPeer[connection.peers[participant]?"renegotiatePeer":"createNewPeer"](participant,{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})});if(message.message.newParticipant){if(message.message.newParticipant==connection.userid)return;if(connection.peers[message.message.newParticipant])return;return void mPeer.createNewPeer(message.message.newParticipant,message.message.userPreferences||{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})}if((message.message.readyForOffer||message.message.addMeAsBroadcaster)&&connection.addNewBroadcaster(message.sender),message.message.newParticipationRequest&&message.sender!==connection.userid){connection.peers[message.sender]&&connection.deletePeer(message.sender);var userPreferences={extra:message.extra||{},localPeerSdpConstraints:message.message.remotePeerSdpConstraints||{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:message.message.localPeerSdpConstraints||{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:"undefined"!=typeof message.message.isDataOnly?message.message.isDataOnly:isData(connection.session),dontGetRemoteStream:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,dontAttachLocalStream:!!message.message.dontGetRemoteStream,connectionDescription:message,successCallback:function(){("undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction)&&connection.addNewBroadcaster(message.sender,userPreferences),(connection.session.oneway||"one-way"===connection.direction||isData(connection.session))&&connection.addNewBroadcaster(message.sender,userPreferences)}};return void connection.onNewParticipant(message.sender,userPreferences)}return message.message.shiftedModerationControl?void connection.onShiftedModerationControl(message.sender,message.message.broadcasters):(message.message.changedUUID&&connection.peers[message.message.oldUUID]&&(connection.peers[message.message.newUUID]=connection.peers[message.message.oldUUID],delete connection.peers[message.message.oldUUID]),message.message.userLeft?(mPeer.onUserLeft(message.sender),void(message.message.autoCloseEntireSession&&connection.leave())):void mPeer.addNegotiatedMessage(message.message,message.sender))}}),socket.on("user-left",function(userid){onUserLeft(userid),connection.onUserStatusChanged({userid:userid,status:"offline",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}}),connection.onleave({userid:userid,extra:{}})}),socket.on("connect",function(){connection.enableLogs&&console.info("socket.io connection is opened."),socket.emit("extra-data-updated",connection.extra),connectCallback&&connectCallback(socket)}),socket.on("disconnect",function(){connection.enableLogs&&(console.info("socket.io connection is closed"),console.warn("socket.io reconnecting"))}),socket.on("join-with-password",function(remoteUserId){connection.onJoinWithPassword(remoteUserId)}),socket.on("invalid-password",function(remoteUserId,oldPassword){connection.onInvalidPassword(remoteUserId,oldPassword)}),socket.on("password-max-tries-over",function(remoteUserId){connection.onPasswordMaxTriesOver(remoteUserId)}),socket.on("user-disconnected",function(remoteUserId){remoteUserId!==connection.userid&&(connection.onUserStatusChanged({userid:remoteUserId,status:"offline",extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra||{}:{}}),connection.deletePeer(remoteUserId))}),socket.on("user-connected",function(userid){userid!==connection.userid&&connection.onUserStatusChanged({userid:userid,status:"online",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}})}),socket.on("logs",function(log){connection.enableLogs&&console.debug("server-logs",log)}),socket}function MultiPeers(connection){function initFileBufferReader(){fbr=new FileBufferReader,fbr.onProgress=function(chunk){connection.onFileProgress(chunk)},fbr.onBegin=function(file){connection.onFileStart(file)},fbr.onEnd=function(file){connection.onFileEnd(file)}}var self=this,skipPeers=["getAllParticipants","getLength","selectFirst","streams","send","forEach"];connection.peers={getLength:function(){var numberOfPeers=0;for(var peer in this)-1==skipPeers.indexOf(peer)&&numberOfPeers++;return numberOfPeers},selectFirst:function(){var firstPeer;for(var peer in this)-1==skipPeers.indexOf(peer)&&(firstPeer=this[peer]);return firstPeer},getAllParticipants:function(sender){var allPeers=[];for(var peer in this)-1==skipPeers.indexOf(peer)&&peer!=sender&&allPeers.push(peer);return allPeers},forEach:function(callbcak){this.getAllParticipants().forEach(function(participant){callbcak(connection.peers[participant])})},send:function(data,remoteUserId){var that=this;if(!isNull(data.size)&&!isNull(data.type))return void self.shareFile(data,remoteUserId);if(!("text"===data.type||data instanceof ArrayBuffer||data instanceof DataView))return void TextSender.send({text:data,channel:this,connection:connection,remoteUserId:remoteUserId});if("text"===data.type&&(data=JSON.stringify(data)),remoteUserId){var remoteUser=connection.peers[remoteUserId];if(remoteUser)return void remoteUser.channels.forEach(function(channel){channel.send(data)})}this.getAllParticipants().forEach(function(participant){that[participant].channels.forEach(function(channel){channel.send(data)})})}},this.uuid=connection.userid,this.getLocalConfig=function(remoteSdp,remoteUserId,userPreferences){return userPreferences||(userPreferences={}),{streamsToShare:userPreferences.streamsToShare||{},rtcMultiConnection:connection,connectionDescription:userPreferences.connectionDescription,remoteUserId:remoteUserId,localPeerSdpConstraints:userPreferences.localPeerSdpConstraints,remotePeerSdpConstraints:userPreferences.remotePeerSdpConstraints,dontGetRemoteStream:!!userPreferences.dontGetRemoteStream,dontAttachLocalStream:!!userPreferences.dontAttachLocalStream,renegotiatingPeer:!!userPreferences.renegotiatingPeer,peerRef:userPreferences.peerRef,onLocalSdp:function(localSdp){self.onNegotiationNeeded(localSdp,remoteUserId)},onLocalCandidate:function(localCandidate){localCandidate=OnIceCandidateHandler.processCandidates(connection,localCandidate),localCandidate&&self.onNegotiationNeeded(localCandidate,remoteUserId)},remoteSdp:remoteSdp,onDataChannelMessage:function(message){if(!fbr&&connection.enableFileSharing&&initFileBufferReader(),"string"==typeof message||!connection.enableFileSharing)return void self.onDataChannelMessage(message,remoteUserId);var that=this;return message instanceof ArrayBuffer||message instanceof DataView?void fbr.convertToObject(message,function(object){that.onDataChannelMessage(object)}):message.readyForNextChunk?void fbr.getNextChunk(message.uuid,function(nextChunk,isLastChunk){connection.peers[remoteUserId].channels.forEach(function(channel){channel.send(nextChunk)})},remoteUserId):void fbr.addChunk(message,function(promptNextChunk){connection.peers[remoteUserId].peer.channel.send(promptNextChunk)})},onDataChannelError:function(error){self.onDataChannelError(error,remoteUserId)},onDataChannelOpened:function(channel){self.onDataChannelOpened(channel,remoteUserId)},onDataChannelClosed:function(event){self.onDataChannelClosed(event,remoteUserId)},onRemoteStream:function(stream){if(connection.peers[remoteUserId].streams.push(stream),isPluginRTC){var mediaElement=document.createElement("video"),body=connection.videosContainer;return body.insertBefore(mediaElement,body.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),self.onGettingRemoteMedia(mediaElement,remoteUserId)},3e3)}self.onGettingRemoteMedia(stream,remoteUserId)},onRemoteStreamRemoved:function(stream){self.onRemovingRemoteMedia(stream,remoteUserId)},onPeerStateChanged:function(states){self.onPeerStateChanged(states),"new"===states.iceConnectionState&&self.onNegotiationStarted(remoteUserId,states),"connected"===states.iceConnectionState&&self.onNegotiationCompleted(remoteUserId,states),-1!==states.iceConnectionState.search(/closed|failed/gi)&&(self.onUserLeft(remoteUserId),self.disconnectWith(remoteUserId))}}},this.createNewPeer=function(remoteUserId,userPreferences){if(!(connection.maxParticipantsAllowed<=connection.getAllParticipants().length)){if(userPreferences=userPreferences||{},!userPreferences.isOneWay&&!userPreferences.isDataOnly)return userPreferences.isOneWay=!0,void this.onNegotiationNeeded({enableMedia:!0,userPreferences:userPreferences},remoteUserId);userPreferences=connection.setUserPreferences(userPreferences,remoteUserId);var localConfig=this.getLocalConfig(null,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)}},this.createAnsweringPeer=function(remoteSdp,remoteUserId,userPreferences){userPreferences=connection.setUserPreferences(userPreferences||{},remoteUserId);var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.renegotiatePeer=function(remoteUserId,userPreferences,remoteSdp){if(!connection.peers[remoteUserId])return void(connection.enableLogs&&console.error("This peer ("+remoteUserId+") does not exists. Renegotiation skipped."));userPreferences||(userPreferences={}),userPreferences.renegotiatingPeer=!0,userPreferences.peerRef=connection.peers[remoteUserId].peer;var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.replaceTrack=function(track,remoteUserId,isVideoTrack){if(!connection.peers[remoteUserId])throw"This peer ("+remoteUserId+") does not exists.";var peer=connection.peers[remoteUserId].peer;return peer.getSenders&&"function"==typeof peer.getSenders&&peer.getSenders().length?void peer.getSenders().forEach(function(rtpSender){isVideoTrack&&rtpSender.track instanceof VideoStreamTrack&&(connection.peers[remoteUserId].peer.lastVideoTrack=rtpSender.track,rtpSender.replaceTrack(track)),!isVideoTrack&&rtpSender.track instanceof AudioStreamTrack&&(connection.peers[remoteUserId].peer.lastAudioTrack=rtpSender.track,rtpSender.replaceTrack(track))}):(console.warn("RTPSender.replaceTrack is NOT supported."),void this.renegotiatePeer(remoteUserId))},this.onNegotiationNeeded=function(message,remoteUserId){},this.addNegotiatedMessage=function(message,remoteUserId){function invokeGetUserMedia(mediaConstraints){getUserMediaHandler({onGettingLocalMedia:function(localStream){self.onGettingLocalMedia(localStream);var streamsToShare={};connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},onLocalMediaError:function(error,constraints){return self.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints)):void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},localMediaConstraints:mediaConstraints})}if(message.type&&message.sdp)return"answer"==message.type&&connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteSdp(message),"offer"==message.type&&(message.renegotiatingPeer?this.renegotiatePeer(remoteUserId,null,message):this.createAnsweringPeer(message,remoteUserId)),void(connection.enableLogs&&console.log("Remote peer's sdp:",message.sdp));if(message.candidate)return connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteCandidate(message),void(connection.enableLogs&&console.log("Remote peer's candidate pairs:",message.candidate));if(message.enableMedia){if(connection.attachStreams.length||connection.dontCaptureUserMedia){var streamsToShare={};return connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)}var localMediaConstraints={},userPreferences=message.userPreferences;userPreferences.localPeerSdpConstraints.OfferToReceiveAudio&&(localMediaConstraints.audio=connection.mediaConstraints.audio),userPreferences.localPeerSdpConstraints.OfferToReceiveVideo&&(localMediaConstraints.video=connection.mediaConstraints.video),invokeGetUserMedia(localMediaConstraints)}message.readyForOffer&&connection.onReadyForOffer(remoteUserId,message.userPreferences)},this.connectNewParticipantWithAllBroadcasters=function(newParticipantId,userPreferences,broadcastersList){if(broadcastersList=broadcastersList.split("|-,-|"),broadcastersList.length){var firstBroadcaster=broadcastersList[0];self.onNegotiationNeeded({newParticipant:newParticipantId,userPreferences:userPreferences||!1},firstBroadcaster),delete broadcastersList[0];var array=[];broadcastersList.forEach(function(broadcaster){broadcaster&&array.push(broadcaster)}),setTimeout(function(){self.connectNewParticipantWithAllBroadcasters(newParticipantId,userPreferences,array.join("|-,-|"))},1e4)}},this.onGettingRemoteMedia=function(stream,remoteUserId){},this.onRemovingRemoteMedia=function(stream,remoteUserId){},this.onGettingLocalMedia=function(localStream){},this.onLocalMediaError=function(error,constraints){connection.onMediaError(error,constraints)};var fbr;this.shareFile=function(file,remoteUserId){if(!connection.enableFileSharing)throw'"connection.enableFileSharing" is false.';initFileBufferReader(),fbr.readAsArrayBuffer(file,function(uuid){var arrayOfUsers=connection.getAllParticipants();remoteUserId&&(arrayOfUsers=[remoteUserId]),arrayOfUsers.forEach(function(participant){fbr.getNextChunk(uuid,function(nextChunk){connection.peers[participant].channels.forEach(function(channel){channel.send(nextChunk)})},participant)})},{userid:connection.userid,chunkSize:isFirefox?15e3:connection.chunkSize||0})};var textReceiver=new TextReceiver(connection);this.onDataChannelMessage=function(message,remoteUserId){textReceiver.receive(JSON.parse(message),remoteUserId,connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{})},this.onDataChannelClosed=function(event,remoteUserId){event.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onclose(event)},this.onDataChannelError=function(error,remoteUserId){error.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onerror(error)},this.onDataChannelOpened=function(channel,remoteUserId){connection.peers[remoteUserId].channels.length||(connection.peers[remoteUserId].channels.push(channel),connection.onopen({userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},channel:channel}),connection.observers.all())},this.onPeerStateChanged=function(state){connection.onPeerStateChanged(state)},this.onNegotiationStarted=function(remoteUserId,states){},this.onNegotiationCompleted=function(remoteUserId,states){},this.getRemoteStreams=function(remoteUserId){return remoteUserId=remoteUserId||connection.peers.getAllParticipants()[0],connection.peers[remoteUserId]?connection.peers[remoteUserId].streams:[]},this.isPluginRTC=connection.isPluginRTC=isPluginRTC}function fireEvent(obj,eventName,args){if("undefined"!=typeof CustomEvent){var eventDetail={arguments:args,__exposedProps__:args},event=new CustomEvent(eventName,eventDetail);obj.dispatchEvent(event)}}function setHarkEvents(connection,streamEvent){if(!connection||!streamEvent)throw"Both arguments are required.";if(connection.onspeaking&&connection.onsilence){if("undefined"==typeof hark)throw"hark.js not found.";hark(streamEvent.stream,{onspeaking:function(){connection.onspeaking(streamEvent)},onsilence:function(){connection.onsilence(streamEvent)},onvolumechange:function(volume,threshold){connection.onvolumechange&&connection.onvolumechange(merge({volume:volume,threshold:threshold},streamEvent))}})}}function setMuteHandlers(connection,streamEvent){streamEvent.stream.addEventListener("mute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.muteType,video:"video"===event.muteType},connection.onmute(event)},!1),streamEvent.stream.addEventListener("unmute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.unmuteType,video:"video"===event.unmuteType},connection.onunmute(event)},!1)}function getRandomString(){if(window.crypto&&window.crypto.getRandomValues&&-1===navigator.userAgent.indexOf("Safari")){for(var a=window.crypto.getRandomValues(new Uint32Array(3)),token="",i=0,l=a.length;l>i;i++)token+=a[i].toString(36);return token}return(Math.random()*(new Date).getTime()).toString(36).replace(/\./g,"")}function getRMCMediaElement(stream,callback,connection){var isAudioOnly=!1;stream.getVideoTracks().length||(isAudioOnly=!0);var mediaElement=document.createElement(isAudioOnly?"audio":"video");return isPluginRTC?(connection.videosContainer.insertBefore(mediaElement,connection.videosContainer.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),callback(mediaElement)},1e3)):(mediaElement[isFirefox?"mozSrcObject":"src"]=isFirefox?stream:window.URL.createObjectURL(stream),mediaElement.controls=!0,isFirefox&&mediaElement.addEventListener("ended",function(){if(currentUserMediaRequest.remove(stream.idInstance),"local"===stream.type){StreamsHandler.onSyncNeeded(stream.streamid,"ended"),connection.attachStreams.forEach(function(aStream,idx){stream.streamid===aStream.streamid&&delete connection.attachStreams[idx]});var newStreamsArray=[];connection.attachStreams.forEach(function(aStream){aStream&&newStreamsArray.push(aStream)}),connection.attachStreams=newStreamsArray,connection.observers.all();var streamEvent=connection.streamEvents[stream.streamid];if(streamEvent)return void connection.onstreamended(streamEvent);this.parentNode&&this.parentNode.removeChild(this)}},!1),mediaElement.play(),void callback(mediaElement))}function listenEventHandler(eventName,eventHandler){window.removeEventListener(eventName,eventHandler),window.addEventListener(eventName,eventHandler,!1)}function removeNullEntries(array){var newArray=[];return array.forEach(function(item){item&&newArray.push(item)}),newArray}function isData(session){return!session.audio&&!session.video&&!session.screen&&session.data}function isNull(obj){return"undefined"==typeof obj}function isString(obj){return"string"==typeof obj}function observeObject(obj,callback){Object.observe&&(isMobileDevice||Object.observe(obj,function(changes){var jsonStringified=JSON.stringify(changes);lastChanges!=jsonStringified&&(lastChanges=jsonStringified,callback(changes))}))}function setCordovaAPIs(){if("iOS"===DetectRTC.osName&&"undefined"!=typeof cordova&&"undefined"!=typeof cordova.plugins&&"undefined"!=typeof cordova.plugins.iosrtc){var iosrtc=cordova.plugins.iosrtc;window.webkitRTCPeerConnection=iosrtc.RTCPeerConnection,window.RTCSessionDescription=iosrtc.RTCSessionDescription,window.RTCIceCandidate=iosrtc.RTCIceCandidate,window.MediaStream=iosrtc.MediaStream,window.MediaStreamTrack=iosrtc.MediaStreamTrack,navigator.getUserMedia=navigator.webkitGetUserMedia=iosrtc.getUserMedia,iosrtc.debug.enable("iosrtc*"),iosrtc.registerGlobals()}}function setSdpConstraints(config){var sdpConstraints,sdpConstraints_mandatory={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo};return sdpConstraints={mandatory:sdpConstraints_mandatory,optional:[{VoiceActivityDetection:!1}]},navigator.mozGetUserMedia&&firefoxVersion>34&&(sdpConstraints={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo}),sdpConstraints}function onPluginRTCInitialized(pluginRTCObject){Plugin=pluginRTCObject,MediaStreamTrack=Plugin.MediaStreamTrack,RTCPeerConnection=Plugin.RTCPeerConnection,RTCIceCandidate=Plugin.RTCIceCandidate,RTCSessionDescription=Plugin.RTCSessionDescription}function PeerInitiator(config){function createDataChannel(){if(!isOfferer)return void(peer.ondatachannel=function(event){var channel=event.channel;setChannelEvents(channel)});var channel=peer.createDataChannel("RTCDataChannel",{});setChannelEvents(channel)}function setChannelEvents(channel){channel.binaryType="arraybuffer",channel.onmessage=function(event){config.onDataChannelMessage(event.data)},channel.onopen=function(){config.onDataChannelOpened(channel)},channel.onerror=function(error){config.onDataChannelError(error)},channel.onclose=function(event){config.onDataChannelClosed(event)},channel.internalSend=channel.send,channel.send=function(data){"open"===channel.readyState&&channel.internalSend(data)},peer.channel=channel}var connection=config.rtcMultiConnection;this.extra=config.remoteSdp?config.remoteSdp.extra:connection.extra,this.remoteUserId=config.remoteUserId,this.streams=[],this.channels=[],this.connectionDescription=config.connectionDescription;var that=this;config.remoteSdp&&(this.connectionDescription=config.remoteSdp.connectionDescription);var allRemoteStreams={};if(Object.observe){var that=this;Object.observe(this.channels,function(changes){changes.forEach(function(change){"add"===change.type&&change.object[change.name].addEventListener("close",function(){delete that.channels[that.channels.indexOf(change.object[change.name])],that.channels=removeNullEntries(that.channels)},!1),("remove"===change.type||"delete"===change.type)&&-1!==that.channels.indexOf(change.object[change.name])&&delete that.channels.indexOf(change.object[change.name]),that.channels=removeNullEntries(that.channels)})})}defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:!0,OfferToReceiveVideo:!0});var peer,renegotiatingPeer=!!config.renegotiatingPeer;config.remoteSdp&&(renegotiatingPeer=!!config.remoteSdp.renegotiatingPeer);var localStreams=[];connection.attachStreams.forEach(function(stream){stream&&localStreams.push(stream)}),renegotiatingPeer?(peer=config.peerRef,peer.getLocalStreams().forEach(function(stream){localStreams.forEach(function(localStream,index){stream==localStream&&delete localStreams[index]}),connection.removeStreams.forEach(function(streamToRemove,index){stream===streamToRemove&&(stream=connection.beforeRemovingStream(stream),stream&&peer.removeStream&&peer.removeStream(stream),localStreams.forEach(function(localStream,index){streamToRemove==localStream&&delete localStreams[index]}))})})):peer=new RTCPeerConnection(navigator.onLine?{iceServers:connection.iceServers,iceTransports:"all"}:null,connection.optionalArgument),"Firefox"===connection.DetectRTC.browser.name&&(peer.removeStream=function(stream){stream.mute(),connection.StreamsHandler.onSyncNeeded(stream.streamid,"stream-removed")}),peer.onicecandidate=function(event){event.candidate&&config.onLocalCandidate({candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex})};var isFirefoxOffered=!isFirefox;config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered&&(isFirefoxOffered=!0),localStreams.forEach(function(localStream){config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream||config.dontAttachLocalStream||(localStream=connection.beforeAddingStream(localStream),localStream&&peer.addStream(localStream))}),peer.oniceconnectionstatechange=peer.onsignalingstatechange=function(){var extra=that.extra;connection.peers[that.remoteUserId]&&(extra=connection.peers[that.remoteUserId].extra||extra),peer&&config.onPeerStateChanged({iceConnectionState:peer.iceConnectionState,iceGatheringState:peer.iceGatheringState,signalingState:peer.signalingState,extra:extra,userid:that.remoteUserId})};var sdpConstraints={OfferToReceiveAudio:!!localStreams.length,OfferToReceiveVideo:!!localStreams.length};config.localPeerSdpConstraints&&(sdpConstraints=config.localPeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),peer.onaddstream=function(event){var streamsToShare={};config.remoteSdp&&config.remoteSdp.streamsToShare?streamsToShare=config.remoteSdp.streamsToShare:config.streamsToShare&&(streamsToShare=config.streamsToShare);var streamToShare=streamsToShare[event.stream.id];streamToShare&&(event.stream.isAudio=streamToShare.isAudio,event.stream.isVideo=streamToShare.isVideo,event.stream.isScreen=streamToShare.isScreen),event.stream.streamid=event.stream.id,event.stream.stop||(event.stream.stop=function(){isFirefox&&fireEvent(this,"ended")}),allRemoteStreams[event.stream.id]=event.stream,config.onRemoteStream(event.stream)},peer.onremovestream=function(event){event.stream.streamid=event.stream.id,allRemoteStreams[event.stream.id]&&delete allRemoteStreams[event.stream.id],config.onRemoteStreamRemoved(event.stream)},this.addRemoteCandidate=function(remoteCandidate){peer.addIceCandidate(new RTCIceCandidate(remoteCandidate))},this.addRemoteSdp=function(remoteSdp){remoteSdp.sdp=connection.processSdp(remoteSdp.sdp),peer.setRemoteDescription(new RTCSessionDescription(remoteSdp),function(){},function(error){connection.enableLogs&&console.error(JSON.stringify(error,null," "))})};var isOfferer=!0;config.remoteSdp&&(isOfferer=!1),connection.session.data===!0&&createDataChannel(),config.remoteSdp&&(config.remoteSdp.remotePeerSdpConstraints&&(sdpConstraints=config.remoteSdp.remotePeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),this.addRemoteSdp(config.remoteSdp)),("two-way"==connection.session.audio||"two-way"==connection.session.video||"two-way"==connection.session.screen)&&(defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:"two-way"==connection.session.audio||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio,OfferToReceiveVideo:"two-way"==connection.session.video||"two-way"==connection.session.screen||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio}));var streamsToShare={};peer.getLocalStreams().forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),peer[isOfferer?"createOffer":"createAnswer"](function(localSdp){localSdp.sdp=connection.processSdp(localSdp.sdp),peer.setLocalDescription(localSdp),config.onLocalSdp({type:localSdp.type,sdp:localSdp.sdp,remotePeerSdpConstraints:config.remotePeerSdpConstraints||!1,renegotiatingPeer:!!config.renegotiatingPeer||!1,connectionDescription:that.connectionDescription,dontGetRemoteStream:!!config.dontGetRemoteStream,extra:connection?connection.extra:{},streamsToShare:streamsToShare,isFirefoxOffered:isFirefox})},function(error){connection.enableLogs&&console.error("sdp-error",error)},defaults.sdpConstraints),peer.nativeClose=peer.close,peer.close=function(){if(peer){try{-1===peer.iceConnectionState.search(/closed|failed/gi)&&peer.getRemoteStreams().forEach(function(stream){stream.stop()}),peer.nativeClose()}catch(e){}peer=null,that.peer=null}},this.peer=peer}function loadIceFrame(callback,skip){if(!loadedIceFrame){ +if(!skip)return loadIceFrame(callback,!0);loadedIceFrame=!0;var iframe=document.createElement("iframe");iframe.onload=function(){function iFrameLoaderCallback(event){event.data&&event.data.iceServers&&(callback(event.data.iceServers),window.removeEventListener("message",iFrameLoaderCallback))}iframe.isLoaded=!0,listenEventHandler("message",iFrameLoaderCallback),iframe.contentWindow.postMessage("get-ice-servers","*")},iframe.src="https://cdn.webrtc-experiment.com/getIceServers/",iframe.style.display="none",(document.body||document.documentElement).appendChild(iframe)}}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}function setStreamType(constraints,stream){constraints.mandatory&&constraints.mandatory.chromeMediaSource?stream.isScreen=!0:constraints.mozMediaSource||constraints.mediaSource?stream.isScreen=!0:constraints.video?stream.isVideo=!0:constraints.audio&&(stream.isAudio=!0)}function getUserMediaHandler(options){function streaming(stream,returnBack){setStreamType(options.localMediaConstraints,stream),options.onGettingLocalMedia(stream,returnBack),stream.addEventListener("ended",function(){delete currentUserMediaRequest.streams[idInstance],currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.indexOf(options)&&(delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)],currentUserMediaRequest.queueRequests=removeNullEntries(currentUserMediaRequest.queueRequests))},!1),currentUserMediaRequest.streams[idInstance]={stream:stream},currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.length&&getUserMediaHandler(currentUserMediaRequest.queueRequests.shift())}if(currentUserMediaRequest.mutex===!0)return void currentUserMediaRequest.queueRequests.push(options);currentUserMediaRequest.mutex=!0;var idInstance=JSON.stringify(options.localMediaConstraints);if(currentUserMediaRequest.streams[idInstance])streaming(currentUserMediaRequest.streams[idInstance].stream,!0);else{if(isPluginRTC){document.createElement("video");return void Plugin.getUserMedia({audio:!0,video:!0},function(stream){stream.streamid=stream.id||getRandomString(),streaming(stream)},function(error){})}navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream){stream.streamid=stream.streamid||stream.id||getRandomString(),stream.idInstance=idInstance,streaming(stream)})["catch"](function(error){options.onLocalMediaError(error,options.localMediaConstraints)})}}function TextReceiver(connection){function receive(data,userid,extra){var uuid=data.uuid;if(content[uuid]||(content[uuid]=[]),content[uuid].push(data.message),data.last){var message=content[uuid].join("");data.isobject&&(message=JSON.parse(message));var receivingTime=(new Date).getTime(),latency=receivingTime-data.sendingTime,e={data:message,userid:userid,extra:extra,latency:latency};connection.autoTranslateText?(e.original=e.data,connection.Translator.TranslateText(e.data,function(translatedText){e.data=translatedText,connection.onmessage(e)})):connection.onmessage(e),delete content[uuid]}}var content={};return{receive:receive}}var isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode,isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);"undefined"!=typeof cordova&&(isMobileDevice=!0,isChrome=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0,isChrome=!0);var isPluginRTC=!isMobileDevice&&(isSafari||isIE),chromeVersion=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),50),matchArray=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);isChrome&&matchArray&&matchArray[2]&&(chromeVersion=parseInt(matchArray[2],10));var firefoxVersion=50;matchArray=navigator.userAgent.match(/Firefox\/(.*)/),isFirefox&&matchArray&&matchArray[1]&&(firefoxVersion=parseInt(matchArray[1],10)),window.addEventListener||(window.addEventListener=function(el,eventName,eventHandler){el.attachEvent&&el.attachEvent("on"+eventName,eventHandler)}),window.attachEventListener=function(video,type,listener,useCapture){video.addEventListener(type,listener,useCapture)};var MediaStream=window.MediaStream;"undefined"==typeof MediaStream&&"undefined"!=typeof webkitMediaStream&&(MediaStream=webkitMediaStream),"undefined"==typeof MediaStream||"stop"in MediaStream.prototype||(MediaStream.prototype.stop=function(){!this.getAudioTracks&&this.getTracks&&(this.getAudioTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("audio")&&array.push(track)}),array}),!this.getVideoTracks&&this.getTracks&&(this.getVideoTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("video")&&array.push(track)}),array}),this.getAudioTracks().forEach(function(track){track.stop&&track.stop()}),this.getVideoTracks().forEach(function(track){track.stop&&track.stop()}),isFirefox&&fireEvent(this,"ended")}),"undefined"!=typeof MediaStream&&("getAudioTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getAudioTracks||(MediaStream.prototype.getAudioTracks=function(){}),"getVideoTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getVideoTracks||(MediaStream.prototype.getVideoTracks=function(){}));var lastChanges="";!function(){function LoadPluginRTC(){function getPlugin(){return document.getElementById("WebrtcEverywherePluginId")}window.PluginRTC={};var extractPluginObj=function(elt){return elt.isWebRtcPlugin?elt:elt.pluginObj},attachEventListener=function(elt,type,listener,useCapture){var _pluginObj=extractPluginObj(elt);_pluginObj?_pluginObj.bindEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEventListener?elt.addEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEvent&&elt.addEventListener("on"+type,listener,useCapture)},installPlugin=function(){if(!document.getElementById("WebrtcEverywherePluginId")){var pluginObj=document.createElement("object");isIE?pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):pluginObj.setAttribute("type","application/webrtc-everywhere"),pluginObj.setAttribute("id","WebrtcEverywherePluginId"),(document.body||document.documentElement).appendChild(pluginObj),pluginObj.setAttribute("width","0"),pluginObj.setAttribute("height","0")}};document.body?installPlugin():(attachEventListener(window,"load",function(){installPlugin()}),attachEventListener(document,"readystatechange",function(){"complete"==document.readyState&&installPlugin()}));var getUserMediaDelayed;window.PluginRTC.getUserMedia=navigator.getUserMedia=function(constraints,successCallback,errorCallback){"complete"!==document.readyState?getUserMediaDelayed||(getUserMediaDelayed=!0,attachEventListener(document,"readystatechange",function(){getUserMediaDelayed&&"complete"==document.readyState&&(getUserMediaDelayed=!1,getPlugin().getUserMedia(constraints,successCallback,errorCallback))})):getPlugin().getUserMedia(constraints,successCallback,errorCallback)},window.PluginRTC.attachMediaStream=function(element,stream){if(element.isWebRtcPlugin)return element.src=stream,element;if("video"===element.nodeName.toLowerCase()){if(!element.pluginObj&&stream){var _pluginObj=document.createElement("object"),_isIE=Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window;_isIE?_pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):_pluginObj.setAttribute("type","application/webrtc-everywhere"),element.pluginObj=_pluginObj,_pluginObj.setAttribute("className",element.className),_pluginObj.setAttribute("innerHTML",element.innerHTML);var width=element.getAttribute("width"),height=element.getAttribute("height"),bounds=element.getBoundingClientRect();if(width||(width=bounds.right-bounds.left),height||(height=bounds.bottom-bounds.top),"getComputedStyle"in window){var computedStyle=window.getComputedStyle(element,null);width||"auto"==computedStyle.width||"0px"==computedStyle.width||(width=computedStyle.width),height||"auto"==computedStyle.height||"0px"==computedStyle.height||(height=computedStyle.height)}width?_pluginObj.setAttribute("width",width):_pluginObj.setAttribute("autowidth",!0),height?_pluginObj.setAttribute("height",height):_pluginObj.setAttribute("autoheight",!0),(document.body||document.documentElement).appendChild(_pluginObj),element.parentNode&&(element.parentNode.replaceChild(_pluginObj,element),document.body.appendChild(element),element.style.visibility="hidden")}return element.pluginObj&&(element.pluginObj.bindEventListener("play",function(objvid){element.pluginObj&&(element.pluginObj.getAttribute("autowidth")&&objvid.videoWidth&&element.pluginObj.setAttribute("width",objvid.videoWidth),element.pluginObj.getAttribute("autoheight")&&objvid.videoHeight&&element.pluginObj.setAttribute("height",objvid.videoHeight))}),element.pluginObj.src=stream),element.pluginObj}return"audio"===element.nodeName.toLowerCase()?element:void 0},window.PluginRTC.MediaStreamTrack={};var getSourcesDelayed;window.PluginRTC.MediaStreamTrack.getSources=function(gotSources){"complete"!==document.readyState?getSourcesDelayed||(getSourcesDelayed=!0,attachEventListener(document,"readystatechange",function(){getSourcesDelayed&&"complete"==document.readyState&&(getSourcesDelayed=!1,getPlugin().getSources(gotSources))})):getPlugin().getSources(gotSources)},window.PluginRTC.RTCPeerConnection=function(configuration,constraints){return getPlugin().createPeerConnection(configuration,constraints)},window.PluginRTC.RTCIceCandidate=function(RTCIceCandidateInit){return getPlugin().createIceCandidate(RTCIceCandidateInit)},window.PluginRTC.RTCSessionDescription=function(RTCSessionDescriptionInit){return getPlugin().createSessionDescription(RTCSessionDescriptionInit)},window.onPluginRTCInitialized&&window.onPluginRTCInitialized(window.PluginRTC)}var ua=navigator.userAgent.toLowerCase(),isSafari=-1!=ua.indexOf("safari")&&-1==ua.indexOf("chrome"),isIE=!!(Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window);(isSafari||isIE)&&window.addEventListener("load",LoadPluginRTC,!1)}(),function(){function getBrowserInfo(){var nameOffset,verOffset,ix,nAgt=(navigator.appVersion,navigator.userAgent),browserName=navigator.appName,fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10);if(isOpera){browserName="Opera";try{fullVersion=navigator.userAgent.split("OPR/")[1].split(" ")[0],majorVersion=fullVersion.split(".")[0]}catch(e){fullVersion="0.0.0.0",majorVersion=0}}else isIE?(verOffset=nAgt.indexOf("MSIE"),browserName="IE",fullVersion=nAgt.substring(verOffset+5)):isChrome?(verOffset=nAgt.indexOf("Chrome"),browserName="Chrome",fullVersion=nAgt.substring(verOffset+7)):isSafari?(verOffset=nAgt.indexOf("Safari"),browserName="Safari",fullVersion=nAgt.substring(verOffset+7),-1!==(verOffset=nAgt.indexOf("Version"))&&(fullVersion=nAgt.substring(verOffset+8))):isFirefox?(verOffset=nAgt.indexOf("Firefox"),browserName="Firefox",fullVersion=nAgt.substring(verOffset+8)):(nameOffset=nAgt.lastIndexOf(" ")+1)<(verOffset=nAgt.lastIndexOf("/"))&&(browserName=nAgt.substring(nameOffset,verOffset),fullVersion=nAgt.substring(verOffset+1),browserName.toLowerCase()===browserName.toUpperCase()&&(browserName=navigator.appName));return isEdge&&(browserName="Edge",fullVersion=parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2],10).toString()),-1!==(ix=fullVersion.indexOf(";"))&&(fullVersion=fullVersion.substring(0,ix)),-1!==(ix=fullVersion.indexOf(" "))&&(fullVersion=fullVersion.substring(0,ix)),majorVersion=parseInt(""+fullVersion,10),isNaN(majorVersion)&&(fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10)),{fullVersion:fullVersion,version:majorVersion,name:browserName,isPrivateBrowsing:!1}}function retry(isDone,next){var currentTrial=0,maxRetry=50,isTimeout=!1,id=window.setInterval(function(){isDone()&&(window.clearInterval(id),next(isTimeout)),currentTrial++>maxRetry&&(window.clearInterval(id),isTimeout=!0,next(isTimeout))},10)}function isIE10OrLater(userAgent){var ua=userAgent.toLowerCase();if(0===ua.indexOf("msie")&&0===ua.indexOf("trident"))return!1;var match=/(?:msie|rv:)\s?([\d\.]+)/.exec(ua);return match&&parseInt(match[1],10)>=10?!0:!1}function detectPrivateMode(callback){var isPrivate;if(window.webkitRequestFileSystem)window.webkitRequestFileSystem(window.TEMPORARY,1,function(){isPrivate=!1},function(e){console.log(e),isPrivate=!0});else if(window.indexedDB&&/Firefox/.test(window.navigator.userAgent)){var db;try{db=window.indexedDB.open("test")}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&retry(function(){return"done"===db.readyState?!0:!1},function(isTimeout){isTimeout||(isPrivate=db.result?!1:!0)})}else if(isIE10OrLater(window.navigator.userAgent)){isPrivate=!1;try{window.indexedDB||(isPrivate=!0)}catch(e){isPrivate=!0}}else if(window.localStorage&&/Safari/.test(window.navigator.userAgent)){try{window.localStorage.setItem("test",1)}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&(isPrivate=!1,window.localStorage.removeItem("test"))}retry(function(){return"undefined"!=typeof isPrivate?!0:!1},function(isTimeout){callback(isPrivate)})}function detectDesktopOS(){var unknown="-",nVer=navigator.appVersion,nAgt=navigator.userAgent,os=unknown,clientStrings=[{s:"Windows 10",r:/(Windows 10.0|Windows NT 10.0)/},{s:"Windows 8.1",r:/(Windows 8.1|Windows NT 6.3)/},{s:"Windows 8",r:/(Windows 8|Windows NT 6.2)/},{s:"Windows 7",r:/(Windows 7|Windows NT 6.1)/},{s:"Windows Vista",r:/Windows NT 6.0/},{s:"Windows Server 2003",r:/Windows NT 5.2/},{s:"Windows XP",r:/(Windows NT 5.1|Windows XP)/},{s:"Windows 2000",r:/(Windows NT 5.0|Windows 2000)/},{s:"Windows ME",r:/(Win 9x 4.90|Windows ME)/},{s:"Windows 98",r:/(Windows 98|Win98)/},{s:"Windows 95",r:/(Windows 95|Win95|Windows_95)/},{s:"Windows NT 4.0",r:/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/},{s:"Windows CE",r:/Windows CE/},{s:"Windows 3.11",r:/Win16/},{s:"Android",r:/Android/},{s:"Open BSD",r:/OpenBSD/},{s:"Sun OS",r:/SunOS/},{s:"Linux",r:/(Linux|X11)/},{s:"iOS",r:/(iPhone|iPad|iPod)/},{s:"Mac OS X",r:/Mac OS X/},{s:"Mac OS",r:/(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},{s:"QNX",r:/QNX/},{s:"UNIX",r:/UNIX/},{s:"BeOS",r:/BeOS/},{s:"OS/2",r:/OS\/2/},{s:"Search Bot",r:/(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/}];for(var id in clientStrings){var cs=clientStrings[id];if(cs.r.test(nAgt)){os=cs.s;break}}var osVersion=unknown;switch(/Windows/.test(os)&&(/Windows (.*)/.test(os)&&(osVersion=/Windows (.*)/.exec(os)[1]),os="Windows"),os){case"Mac OS X":/Mac OS X (10[\.\_\d]+)/.test(nAgt)&&(osVersion=/Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]);break;case"Android":/Android ([\.\_\d]+)/.test(nAgt)&&(osVersion=/Android ([\.\_\d]+)/.exec(nAgt)[1]);break;case"iOS":/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)&&(osVersion=/OS (\d+)_(\d+)_?(\d+)?/.exec(nVer),osVersion=osVersion[1]+"."+osVersion[2]+"."+(0|osVersion[3]))}return{osName:os,osVersion:osVersion}}function DetectLocalIPAddress(callback){DetectRTC.isWebRTCSupported&&(DetectRTC.isORTCSupported||getIPs(function(ip){callback(ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)?"Local: "+ip:"Public: "+ip)}))}function getIPs(callback){function handleCandidate(candidate){var ipRegex=/([0-9]{1,3}(\.[0-9]{1,3}){3})/,match=ipRegex.exec(candidate);if(!match)return void console.warn("Could not match IP address in",candidate);var ipAddress=match[1];void 0===ipDuplicates[ipAddress]&&callback(ipAddress),ipDuplicates[ipAddress]=!0}var ipDuplicates={},RTCPeerConnection=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection,useWebKit=!!window.webkitRTCPeerConnection;if(!RTCPeerConnection){var iframe=document.getElementById("iframe");if(!iframe)throw"NOTE: you need to have an iframe in the page right above the script tag.";var win=iframe.contentWindow;RTCPeerConnection=win.RTCPeerConnection||win.mozRTCPeerConnection||win.webkitRTCPeerConnection,useWebKit=!!win.webkitRTCPeerConnection}if(RTCPeerConnection){var servers,mediaConstraints={optional:[{RtpDataChannels:!0}]};useWebKit&&(servers={iceServers:[{urls:"stun:stun.services.mozilla.com"}]},"undefined"!=typeof DetectRTC&&DetectRTC.browser.isFirefox&&DetectRTC.browser.version<=38&&(servers[0]={url:servers[0].urls}));var pc=new RTCPeerConnection(servers,mediaConstraints);pc.onicecandidate=function(ice){ice.candidate&&handleCandidate(ice.candidate.candidate)},pc.createDataChannel(""),pc.createOffer(function(result){pc.setLocalDescription(result,function(){},function(){})},function(){}),setTimeout(function(){var lines=pc.localDescription.sdp.split("\n");lines.forEach(function(line){0===line.indexOf("a=candidate:")&&handleCandidate(line)})},1e3)}}function checkDeviceSupport(callback){if(canEnumerate){if(!navigator.enumerateDevices&&window.MediaStreamTrack&&window.MediaStreamTrack.getSources&&(navigator.enumerateDevices=window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack)),!navigator.enumerateDevices&&navigator.enumerateDevices&&(navigator.enumerateDevices=navigator.enumerateDevices.bind(navigator)),!navigator.enumerateDevices)return void(callback&&callback());MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[],navigator.enumerateDevices(function(devices){devices.forEach(function(_device){var device={};for(var d in _device)device[d]=_device[d];"audio"===device.kind&&(device.kind="audioinput"),"video"===device.kind&&(device.kind="videoinput");var skip;MediaDevices.forEach(function(d){d.id===device.id&&d.kind===device.kind&&(skip=!0)}),skip||(device.deviceId||(device.deviceId=device.id),device.id||(device.id=device.deviceId),device.label?("videoinput"!==device.kind||isWebsiteHasWebcamPermissions||(isWebsiteHasWebcamPermissions=!0),"audioinput"!==device.kind||isWebsiteHasMicrophonePermissions||(isWebsiteHasMicrophonePermissions=!0)):(device.label="Please invoke getUserMedia once.","https:"!==location.protocol&&document.domain.search&&-1===document.domain.search(/localhost|127.0./g)&&(device.label="HTTPs is required to get label of this "+device.kind+" device.")),"audioinput"===device.kind&&(hasMicrophone=!0,-1===audioInputDevices.indexOf(device)&&audioInputDevices.push(device)),"audiooutput"===device.kind&&(hasSpeakers=!0,-1===audioOutputDevices.indexOf(device)&&audioOutputDevices.push(device)),"videoinput"===device.kind&&(hasWebcam=!0,-1===videoInputDevices.indexOf(device)&&videoInputDevices.push(device)),-1===MediaDevices.indexOf(device)&&MediaDevices.push(device))}),"undefined"!=typeof DetectRTC&&(DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices),callback&&callback()})}}var navigator=window.navigator;"undefined"!=typeof navigator?("undefined"!=typeof navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),"undefined"!=typeof navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia)):navigator={getUserMedia:function(){},userAgent:"Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45"};var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i),isEdge=!(-1===navigator.userAgent.indexOf("Edge")||!navigator.msSaveOrOpenBlob&&!navigator.msSaveBlob),isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode&&!isEdge,isMobile={Android:function(){return navigator.userAgent.match(/Android/i)},BlackBerry:function(){return navigator.userAgent.match(/BlackBerry/i)},iOS:function(){return navigator.userAgent.match(/iPhone|iPad|iPod/i)},Opera:function(){return navigator.userAgent.match(/Opera Mini/i)},Windows:function(){return navigator.userAgent.match(/IEMobile/i)},any:function(){return isMobile.Android()||isMobile.BlackBerry()||isMobile.iOS()||isMobile.Opera()||isMobile.Windows()},getOsName:function(){var osName="Unknown OS";return isMobile.Android()&&(osName="Android"),isMobile.BlackBerry()&&(osName="BlackBerry"),isMobile.iOS()&&(osName="iOS"),isMobile.Opera()&&(osName="Opera Mini"),isMobile.Windows()&&(osName="Windows"),osName}},osName="Unknown OS",osVersion="Unknown OS Version";if(isMobile.any())osName=isMobile.getOsName();else{var osInfo=detectDesktopOS();osName=osInfo.osName,osVersion=osInfo.osVersion}var isCanvasSupportsStreamCapturing=!1,isVideoSupportsStreamCapturing=!1;["captureStream","mozCaptureStream","webkitCaptureStream"].forEach(function(item){!isCanvasSupportsStreamCapturing&&item in document.createElement("canvas")&&(isCanvasSupportsStreamCapturing=!0),!isVideoSupportsStreamCapturing&&item in document.createElement("video")&&(isVideoSupportsStreamCapturing=!0)});var MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[];navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(navigator.enumerateDevices=function(callback){navigator.mediaDevices.enumerateDevices().then(callback)});var canEnumerate=!1;"undefined"!=typeof MediaStreamTrack&&"getSources"in MediaStreamTrack?canEnumerate=!0:navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(canEnumerate=!0);var hasMicrophone=!1,hasSpeakers=!1,hasWebcam=!1,isWebsiteHasMicrophonePermissions=!1,isWebsiteHasWebcamPermissions=!1;checkDeviceSupport();var DetectRTC=window.DetectRTC||{};DetectRTC.browser=getBrowserInfo(),detectPrivateMode(function(isPrivateBrowsing){DetectRTC.browser.isPrivateBrowsing=!!isPrivateBrowsing}),DetectRTC.browser["is"+DetectRTC.browser.name]=!0;var isWebRTCSupported=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),!1);["RTCPeerConnection","webkitRTCPeerConnection","mozRTCPeerConnection","RTCIceGatherer"].forEach(function(item){isWebRTCSupported||item in window&&(isWebRTCSupported=!0)}),DetectRTC.isWebRTCSupported=isWebRTCSupported,DetectRTC.isORTCSupported="undefined"!=typeof RTCIceGatherer;var isScreenCapturingSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>=35?isScreenCapturingSupported=!0:DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=34&&(isScreenCapturingSupported=!0),"https:"!==location.protocol&&(isScreenCapturingSupported=!1),DetectRTC.isScreenCapturingSupported=isScreenCapturingSupported;var webAudio={isSupported:!1,isCreateMediaStreamSourceSupported:!1};["AudioContext","webkitAudioContext","mozAudioContext","msAudioContext"].forEach(function(item){webAudio.isSupported||item in window&&(webAudio.isSupported=!0,"createMediaStreamSource"in window[item].prototype&&(webAudio.isCreateMediaStreamSourceSupported=!0))}),DetectRTC.isAudioContextSupported=webAudio.isSupported,DetectRTC.isCreateMediaStreamSourceSupported=webAudio.isCreateMediaStreamSourceSupported;var isRtpDataChannelsSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>31&&(isRtpDataChannelsSupported=!0),DetectRTC.isRtpDataChannelsSupported=isRtpDataChannelsSupported;var isSCTPSupportd=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>28?isSCTPSupportd=!0:DetectRTC.browser.isChrome&&DetectRTC.browser.version>25?isSCTPSupportd=!0:DetectRTC.browser.isOpera&&DetectRTC.browser.version>=11&&(isSCTPSupportd=!0),DetectRTC.isSctpDataChannelsSupported=isSCTPSupportd,DetectRTC.isMobileDevice=isMobileDevice;var isGetUserMediaSupported=!1;navigator.getUserMedia?isGetUserMediaSupported=!0:navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&(isGetUserMediaSupported=!0),DetectRTC.browser.isChrome&&DetectRTC.browser.version>=46&&"https:"!==location.protocol&&(DetectRTC.isGetUserMediaSupported="Requires HTTPs"),DetectRTC.isGetUserMediaSupported=isGetUserMediaSupported,DetectRTC.osName=osName,DetectRTC.osVersion=osVersion;var displayResolution="";if(screen.width){var width=screen.width?screen.width:"",height=screen.height?screen.height:"";displayResolution+=""+width+" x "+height}DetectRTC.displayResolution=displayResolution,DetectRTC.isCanvasSupportsStreamCapturing=isCanvasSupportsStreamCapturing,DetectRTC.isVideoSupportsStreamCapturing=isVideoSupportsStreamCapturing,DetectRTC.DetectLocalIPAddress=DetectLocalIPAddress,DetectRTC.isWebSocketsSupported="WebSocket"in window&&2===window.WebSocket.CLOSING,DetectRTC.isWebSocketsBlocked=!DetectRTC.isWebSocketsSupported,DetectRTC.checkWebSocketsSupport=function(callback){callback=callback||function(){};try{var websocket=new WebSocket("wss://echo.websocket.org:443/");websocket.onopen=function(){DetectRTC.isWebSocketsBlocked=!1,callback(),websocket.close(),websocket=null},websocket.onerror=function(){DetectRTC.isWebSocketsBlocked=!0,callback()}}catch(e){DetectRTC.isWebSocketsBlocked=!0,callback()}},DetectRTC.load=function(callback){callback=callback||function(){},checkDeviceSupport(callback)},DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices;var isSetSinkIdSupported=!1;"setSinkId"in document.createElement("video")&&(isSetSinkIdSupported=!0),DetectRTC.isSetSinkIdSupported=isSetSinkIdSupported;var isRTPSenderReplaceTracksSupported=!1;DetectRTC.browser.isFirefox?"getSenders"in mozRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0):DetectRTC.browser.isChrome&&"undefined"!=typeof webkitRTCPeerConnection&&"getSenders"in webkitRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0),DetectRTC.isRTPSenderReplaceTracksSupported=isRTPSenderReplaceTracksSupported;var isRemoteStreamProcessingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>38&&(isRemoteStreamProcessingSupported=!0),DetectRTC.isRemoteStreamProcessingSupported=isRemoteStreamProcessingSupported;var isApplyConstraintsSupported=!1;"undefined"!=typeof MediaStreamTrack&&"applyConstraints"in MediaStreamTrack.prototype&&(isApplyConstraintsSupported=!0),DetectRTC.isApplyConstraintsSupported=isApplyConstraintsSupported;var isMultiMonitorScreenCapturingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=43&&(isMultiMonitorScreenCapturingSupported=!0),DetectRTC.isMultiMonitorScreenCapturingSupported=isMultiMonitorScreenCapturingSupported,window.DetectRTC=DetectRTC}(),document.addEventListener("deviceready",setCordovaAPIs,!1),setCordovaAPIs();var RTCPeerConnection,defaults={};"undefined"!=typeof mozRTCPeerConnection?RTCPeerConnection=mozRTCPeerConnection:"undefined"!=typeof webkitRTCPeerConnection?RTCPeerConnection=webkitRTCPeerConnection:"undefined"!=typeof window.RTCPeerConnection?RTCPeerConnection=window.RTCPeerConnection:(console.error("WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser."),RTCPeerConnection=window.RTCSessionDescription=window.RTCIceCandidate=function(){});var RTCSessionDescription=window.RTCSessionDescription||window.mozRTCSessionDescription,RTCIceCandidate=window.RTCIceCandidate||window.mozRTCIceCandidate,MediaStreamTrack=window.MediaStreamTrack,Plugin={};"undefined"!=typeof PluginRTC&&onPluginRTCInitialized(PluginRTC);var CodecsHandler=function(){function removeVPX(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtpmap:100 VP8/90000\r\n",""),sdp=sdp.replace("a=rtpmap:101 VP9/90000\r\n",""),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace("a=rtcp-fb:120 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 ccm fir\r\n","")}function disableNACK(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtcp-fb:126 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:126 nack pli\r\n","a=rtcp-fb:126 pli\r\n"),sdp=sdp.replace("a=rtcp-fb:97 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:97 nack pli\r\n","a=rtcp-fb:97 pli\r\n")}function prioritize(codecMimeType,peer){if(peer&&peer.getSenders&&peer.getSenders().length){if(!codecMimeType||"string"!=typeof codecMimeType)throw"Invalid arguments.";peer.getSenders().forEach(function(sender){for(var params=sender.getParameters(),i=0;ii;++i)if(0===sdpLines[i].indexOf(prefix)&&(!substr||-1!==sdpLines[i].toLowerCase().indexOf(substr.toLowerCase())))return i;return null}function getCodecPayloadType(sdpLine){var pattern=new RegExp("a=rtpmap:(\\d+) \\w+\\/\\d+"),result=sdpLine.match(pattern);return result&&2===result.length?result[1]:null}function setVideoBitrates(sdp,params){if(isMobileDevice)return sdp;params=params||{};var vp8Payload,xgoogle_min_bitrate=params.min,xgoogle_max_bitrate=params.max,sdpLines=sdp.split("\r\n"),vp8Index=findLine(sdpLines,"a=rtpmap","VP8/90000");if(vp8Index&&(vp8Payload=getCodecPayloadType(sdpLines[vp8Index])),!vp8Payload)return sdp;var rtxPayload,rtxIndex=findLine(sdpLines,"a=rtpmap","rtx/90000");if(rtxIndex&&(rtxPayload=getCodecPayloadType(sdpLines[rtxIndex])),!rtxIndex)return sdp;var rtxFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+rtxPayload.toString());if(null!==rtxFmtpLineIndex){var appendrtxNext="\r\n";appendrtxNext+="a=fmtp:"+vp8Payload+" x-google-min-bitrate="+(xgoogle_min_bitrate||"228")+"; x-google-max-bitrate="+(xgoogle_max_bitrate||"228"),sdpLines[rtxFmtpLineIndex]=sdpLines[rtxFmtpLineIndex].concat(appendrtxNext), +sdp=sdpLines.join("\r\n")}return sdp}function setOpusAttributes(sdp,params){if(isMobileDevice)return sdp;params=params||{};var opusPayload,sdpLines=sdp.split("\r\n"),opusIndex=findLine(sdpLines,"a=rtpmap","opus/48000");if(opusIndex&&(opusPayload=getCodecPayloadType(sdpLines[opusIndex])),!opusPayload)return sdp;var opusFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+opusPayload.toString());if(null===opusFmtpLineIndex)return sdp;var appendOpusNext="";return appendOpusNext+="; stereo="+("undefined"!=typeof params.stereo?params.stereo:"1"),appendOpusNext+="; sprop-stereo="+("undefined"!=typeof params["sprop-stereo"]?params["sprop-stereo"]:"1"),"undefined"!=typeof params.maxaveragebitrate&&(appendOpusNext+="; maxaveragebitrate="+(params.maxaveragebitrate||1048576)),"undefined"!=typeof params.maxplaybackrate&&(appendOpusNext+="; maxplaybackrate="+(params.maxplaybackrate||1048576)),"undefined"!=typeof params.cbr&&(appendOpusNext+="; cbr="+("undefined"!=typeof params.cbr?params.cbr:"1")),"undefined"!=typeof params.useinbandfec&&(appendOpusNext+="; useinbandfec="+params.useinbandfec),"undefined"!=typeof params.usedtx&&(appendOpusNext+="; usedtx="+params.usedtx),"undefined"!=typeof params.maxptime&&(appendOpusNext+="\r\na=maxptime:"+params.maxptime),sdpLines[opusFmtpLineIndex]=sdpLines[opusFmtpLineIndex].concat(appendOpusNext),sdp=sdpLines.join("\r\n")}function preferVP9(sdp){return-1===sdp.indexOf("SAVPF 100 101")||-1===sdp.indexOf("VP9/90000")?sdp:sdp.replace("SAVPF 100 101","SAVPF 101 100")}var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);return"undefined"!=typeof cordova&&(isMobileDevice=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0),{removeVPX:removeVPX,disableNACK:disableNACK,prioritize:prioritize,removeNonG722:removeNonG722,setApplicationSpecificBandwidth:function(sdp,bandwidth,isScreen){return setBAS(sdp,bandwidth,isScreen)},setVideoBitrates:function(sdp,params){return setVideoBitrates(sdp,params)},setOpusAttributes:function(sdp,params){return setOpusAttributes(sdp,params)},preferVP9:preferVP9}}();window.BandwidthHandler=CodecsHandler;var loadedIceFrame,OnIceCandidateHandler=function(){function processCandidates(connection,icePair){var candidate=icePair.candidate,iceRestrictions=connection.candidates,stun=iceRestrictions.stun,turn=iceRestrictions.turn;if(isNull(iceRestrictions.reflexive)||(stun=iceRestrictions.reflexive),isNull(iceRestrictions.relay)||(turn=iceRestrictions.relay),(iceRestrictions.host||!candidate.match(/typ host/g))&&(turn||!candidate.match(/typ relay/g))&&(stun||!candidate.match(/typ srflx/g))){var protocol=connection.iceProtocols;if((protocol.udp||!candidate.match(/ udp /g))&&(protocol.tcp||!candidate.match(/ tcp /g)))return connection.enableLogs&&console.debug("Your candidate pairs:",candidate),{candidate:candidate,sdpMid:icePair.sdpMid,sdpMLineIndex:icePair.sdpMLineIndex}}}return{processCandidates:processCandidates}}();"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&loadIceFrame(function(externalIceServers){externalIceServers&&externalIceServers.length&&(window.RMCExternalIceServers=externalIceServers,window.iceServersLoadCallback&&"function"==typeof window.iceServersLoadCallback&&window.iceServersLoadCallback(externalIceServers))});var IceServersHandler=function(){function getIceServers(connection){var iceServers=[];return iceServers.push({urls:"stun:stun.l.google.com:19302"},{urls:"stun:mmt-stun.verkstad.net"},{urls:"stun:stun.anyfirewall.com:3478"}),iceServers.push({urls:"turn:turn.bistri.com:80",credential:"homeo",username:"homeo"}),iceServers.push({urls:"turn:turn.anyfirewall.com:443",credential:"webrtc",username:"webrtc"}),iceServers.push({urls:"turn:mmt-turn.verkstad.net",username:"webrtc",credential:"secret"}),window.RMCExternalIceServers?(iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers):"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&(window.iceServersLoadCallback=function(){iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers}),iceServers}return{getIceServers:getIceServers}}(),getUserMedia=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils=window.webrtcUtils||{};if(webrtcUtils.enableLogs||(webrtcUtils.enableLogs=!0),webrtcUtils.log||(webrtcUtils.log=function(){webrtcUtils.enableLogs&&("undefined"!=typeof module||"function"==typeof require&&"function"==typeof define||console.log.apply(console,arguments))}),webrtcUtils.extractVersion||(webrtcUtils.extractVersion=function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos],10)}),"object"==typeof window&&(!window.HTMLMediaElement||"srcObject"in window.HTMLMediaElement.prototype||Object.defineProperty(window.HTMLMediaElement.prototype,"srcObject",{get:function(){return"mozSrcObject"in this?this.mozSrcObject:this._srcObject},set:function(stream){"mozSrcObject"in this?this.mozSrcObject=stream:(this._srcObject=stream,this.src=stream?URL.createObjectURL(stream):null)}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia&&window.mozRTCPeerConnection){if(webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,getUserMedia=function(constraints,onSuccess,onError){var constraintsToFF37=function(c){if("object"!=typeof c||c.require)return c;var require=[];return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r=c[key]="object"==typeof c[key]?c[key]:{ideal:c[key]};if((void 0!==r.min||void 0!==r.max||void 0!==r.exact)&&require.push(key),void 0!==r.exact&&("number"==typeof r.exact?r.min=r.max=r.exact:c[key]=r.exact,delete r.exact),void 0!==r.ideal){c.advanced=c.advanced||[];var oc={};"number"==typeof r.ideal?oc[key]={min:r.ideal,max:r.ideal}:oc[key]=r.ideal,c.advanced.push(oc),delete r.ideal,Object.keys(r).length||delete c[key]}}}),require.length&&(c.require=require),c};return 38>webrtcDetectedVersion&&(webrtcUtils.log("spec: "+JSON.stringify(constraints)),constraints.audio&&(constraints.audio=constraintsToFF37(constraints.audio)),constraints.video&&(constraints.video=constraintsToFF37(constraints.video)),webrtcUtils.log("ff37: "+JSON.stringify(constraints))),navigator.mozGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia,addEventListener:function(){},removeEventListener:function(){}}),navigator.mediaDevices.enumerateDevices=navigator.mediaDevices.enumerateDevices||function(){return new Promise(function(resolve){var infos=[{kind:"audioinput",deviceId:"default",label:"",groupId:""},{kind:"videoinput",deviceId:"default",label:"",groupId:""}];resolve(infos)})},41>webrtcDetectedVersion){var orgEnumerateDevices=navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);navigator.mediaDevices.enumerateDevices=function(){return orgEnumerateDevices().then(void 0,function(e){if("NotFoundError"===e.name)return[];throw e})}}}else if(navigator.webkitGetUserMedia&&window.webkitRTCPeerConnection){webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38;var constraintsToChrome=function(c){if("object"!=typeof c||c.mandatory||c.optional)return c;var cc={};return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r="object"==typeof c[key]?c[key]:{ideal:c[key]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);var oldname=function(prefix,name){return prefix?prefix+name.charAt(0).toUpperCase()+name.slice(1):"deviceId"===name?"sourceId":name};if(void 0!==r.ideal){cc.optional=cc.optional||[];var oc={};"number"==typeof r.ideal?(oc[oldname("min",key)]=r.ideal,cc.optional.push(oc),oc={},oc[oldname("max",key)]=r.ideal,cc.optional.push(oc)):(oc[oldname("",key)]=r.ideal,cc.optional.push(oc))}void 0!==r.exact&&"number"!=typeof r.exact?(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname("",key)]=r.exact):["min","max"].forEach(function(mix){void 0!==r[mix]&&(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname(mix,key)]=r[mix])})}}),c.advanced&&(cc.optional=(cc.optional||[]).concat(c.advanced)),cc};if(getUserMedia=function(constraints,onSuccess,onError){return constraints.audio&&(constraints.audio=constraintsToChrome(constraints.audio)),constraints.video&&(constraints.video=constraintsToChrome(constraints.video)),webrtcUtils.log("chrome: "+JSON.stringify(constraints)),navigator.webkitGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia}),navigator.mediaDevices.getUserMedia){var origGetUserMedia=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=function(c){return webrtcUtils.log("spec: "+JSON.stringify(c)),c.audio=constraintsToChrome(c.audio),c.video=constraintsToChrome(c.video),webrtcUtils.log("chrome: "+JSON.stringify(c)),origGetUserMedia(c)}}else navigator.mediaDevices.getUserMedia=function(constraints){return requestUserMedia(constraints)};"undefined"==typeof navigator.mediaDevices.addEventListener&&(navigator.mediaDevices.addEventListener=function(){webrtcUtils.log("Dummy mediaDevices.addEventListener called.")}),"undefined"==typeof navigator.mediaDevices.removeEventListener&&(navigator.mediaDevices.removeEventListener=function(){webrtcUtils.log("Dummy mediaDevices.removeEventListener called.")})}else navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?(webrtcUtils.log("This appears to be Edge"),webrtcDetectedBrowser="edge",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),webrtcMinimumVersion=12):webrtcUtils.log("Browser does not appear to be WebRTC-capable");else webrtcDetectedBrowser="not a browser";"undefined"!=typeof module?module.exports={getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}:"function"==typeof require&&"function"==typeof define&&define([],function(){return{getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}}),"undefined"!=typeof webrtcUtils&&(webrtcUtils.enableLogs=!1);var currentUserMediaRequest={streams:[],mutex:!1,queueRequests:[],remove:function(idInstance){this.mutex=!1;var stream=this.streams[idInstance];if(stream){stream=stream.stream;var options=stream.currentUserMediaRequestOptions;this.queueRequests.indexOf(options)&&(delete this.queueRequests[this.queueRequests.indexOf(options)],this.queueRequests=removeNullEntries(this.queueRequests)),this.streams[idInstance].stream=null,delete this.streams[idInstance]}}},StreamsHandler=function(){function handleType(type){return type?"string"==typeof type||"undefined"==typeof type?type:type.audio&&type.video?null:type.audio?"audio":type.video?"video":void 0:void 0}function setHandlers(stream,syncAction,connection){function graduallyIncreaseVolume(){if(connection.streamEvents[stream.streamid].mediaElement){var mediaElement=connection.streamEvents[stream.streamid].mediaElement;mediaElement.volume=0,afterEach(200,5,function(){mediaElement.volume+=.2})}}("undefined"==typeof syncAction||1==syncAction)&&stream.addEventListener("ended",function(){StreamsHandler.onSyncNeeded(this.streamid,"ended")},!1),stream.mute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!1,connection.streamEvents[stream.streamid].isAudioMuted=!0}),("undefined"==typeof type||"video"==type)&&stream.getVideoTracks().forEach(function(track){track.enabled=!1}),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"mute",type),connection.streamEvents[stream.streamid].muteType=type||"both",fireEvent(stream,"mute",type)},stream.unmute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),graduallyIncreaseVolume(),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!0,connection.streamEvents[stream.streamid].isAudioMuted=!1}),("undefined"==typeof type||"video"==type)&&(stream.getVideoTracks().forEach(function(track){track.enabled=!0}),"undefined"!=typeof type&&"video"==type&&connection.streamEvents[stream.streamid].isAudioMuted&&!function looper(times){times||(times=0),times++,100>times&&connection.streamEvents[stream.streamid].isAudioMuted&&(stream.mute("audio"),setTimeout(function(){looper(times)},50))}()),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"unmute",type),connection.streamEvents[stream.streamid].unmuteType=type||"both",fireEvent(stream,"unmute",type)}}function afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes){startedTimes=(startedTimes||0)+1,startedTimes>=numberOfTimes||setTimeout(function(){callback(),afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes)},setTimeoutInteval)}return{setHandlers:setHandlers,onSyncNeeded:function(streamid,action,type){}}}();!function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}(),function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}if(-1!==document.domain.indexOf("webrtc-experiment.com")){window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}}();var TextSender={send:function(config){function sendText(textMessage,text){var data={type:"text",uuid:uuid,sendingTime:sendingTime};textMessage&&(text=textMessage,data.packets=parseInt(text.length/packetSize)),text.length>packetSize?data.message=text.slice(0,packetSize):(data.message=text,data.last=!0,data.isobject=isobject),channel.send(data,remoteUserId),textToTransfer=text.slice(data.message.length),textToTransfer.length&&setTimeout(function(){sendText(null,textToTransfer)},connection.chunkInterval||100)}var connection=config.connection,channel=config.channel,remoteUserId=config.remoteUserId,initialText=config.text,packetSize=connection.chunkSize||1e3,textToTransfer="",isobject=!1;isString(initialText)||(isobject=!0,initialText=JSON.stringify(initialText));var uuid=getRandomString(),sendingTime=(new Date).getTime();sendText(initialText)}},FileProgressBarHandler=function(){function handle(connection){function updateLabel(progress,label){if(-1!==progress.position){var position=+progress.position.toFixed(2).split(".")[1]||100;label.innerHTML=position+"%"}}var progressHelper={};connection.onFileStart=function(file){var div=document.createElement("div");return div.title=file.name,div.innerHTML=" ",file.remoteUserId&&(div.innerHTML+=" (Sharing with:"+file.remoteUserId+")"),connection.filesContainer||(connection.filesContainer=document.body||document.documentElement),connection.filesContainer.insertBefore(div,connection.filesContainer.firstChild),file.remoteUserId?(progressHelper[file.uuid]||(progressHelper[file.uuid]={}),progressHelper[file.uuid][file.remoteUserId]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid][file.remoteUserId].progress.max=file.maxChunks)):(progressHelper[file.uuid]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid].progress.max=file.maxChunks))},connection.onFileProgress=function(chunk){var helper=progressHelper[chunk.uuid];helper&&(!chunk.remoteUserId||(helper=progressHelper[chunk.uuid][chunk.remoteUserId]))&&(helper.progress.value=chunk.currentPosition||chunk.maxChunks||helper.progress.max,updateLabel(helper.progress,helper.label))},connection.onFileEnd=function(file){var helper=progressHelper[file.uuid];if(!helper)return void console.error("No such progress-helper element exists.",file);if(!file.remoteUserId||(helper=progressHelper[file.uuid][file.remoteUserId])){var div=helper.div;-1!=file.type.indexOf("image")?div.innerHTML='Download '+file.name+'
':div.innerHTML='Download '+file.name+'
'}}}return{handle:handle}}(),TranslationHandler=function(){function handle(connection){connection.autoTranslateText=!1,connection.language="en",connection.googKey="AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE",connection.Translator={TranslateText:function(text,callback){var newScript=document.createElement("script");newScript.type="text/javascript";var sourceText=encodeURIComponent(text),randomNumber="method"+connection.token();window[randomNumber]=function(response){response.data&&response.data.translations[0]&&callback&&callback(response.data.translations[0].translatedText),response.error&&"Daily Limit Exceeded"===response.error.message&&(warn('Text translation failed. Error message: "Daily Limit Exceeded."'),callback(text))};var source="https://www.googleapis.com/language/translate/v2?key="+connection.googKey+"&target="+(connection.language||"en-US")+"&callback=window."+randomNumber+"&q="+sourceText;newScript.src=source,document.getElementsByTagName("head")[0].appendChild(newScript)}}}return{handle:handle}}();window.RTCMultiConnection=RTCMultiConnection}(); \ No newline at end of file diff --git a/Signaling-Server.js b/Signaling-Server.js index 4dcb1bbb..c283691b 100755 --- a/Signaling-Server.js +++ b/Signaling-Server.js @@ -3,22 +3,31 @@ // Documentation - github.com/muaz-khan/RTCMultiConnection module.exports = exports = function(app, socketCallback) { - var io = require('socket.io').listen(app, { - log: false, - origins: '*:*' - }); - - io.set('transports', [ - 'websocket', // 'disconnect' EVENT will work only with 'websocket' - 'xhr-polling', - 'jsonp-polling' - ]); - var listOfUsers = {}; var shiftedModerationControls = {}; var ScalableBroadcast; - io.sockets.on('connection', function(socket) { + var io = require('socket.io'); + + try { + io = io(app); + io.on('connection', onConnection); + } catch (e) { + io = io.listen(app, { + log: false, + origins: '*:*' + }); + + io.set('transports', [ + 'websocket', // 'disconnect' EVENT will work only with 'websocket' + 'xhr-polling', + 'jsonp-polling' + ]); + + io.sockets.on('connection', onConnection); + } + + function onConnection(socket) { var params = socket.handshake.query; var socketMessageEvent = params.msgEvent || 'RTCMultiConnection-Message'; @@ -277,5 +286,5 @@ module.exports = exports = function(app, socketCallback) { if (socketCallback) { socketCallback(socket); } - }); + } }; diff --git a/dev/DetectRTC.js b/dev/DetectRTC.js index 728ba197..95fd7500 100644 --- a/dev/DetectRTC.js +++ b/dev/DetectRTC.js @@ -1,4 +1,4 @@ -// Last time updated at Monday, January 4th, 2016, 1:17:50 PM +// Last time updated: 2016-02-26 11:47:17 AM UTC // Latest file can be found here: https://cdn.webrtc-experiment.com/DetectRTC.js @@ -377,22 +377,28 @@ var osVersion = unknown; if (/Windows/.test(os)) { - osVersion = /Windows (.*)/.exec(os)[1]; + if (/Windows (.*)/.test(os)) { + osVersion = /Windows (.*)/.exec(os)[1]; + } os = 'Windows'; } switch (os) { case 'Mac OS X': - osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; + if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) { + osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; + } break; - case 'Android': - osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; + if (/Android ([\.\_\d]+)/.test(nAgt)) { + osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; + } break; - case 'iOS': - osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); - osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); + if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) { + osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); + osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); + } break; } @@ -652,7 +658,9 @@ if (!device.label) { device.label = 'Please invoke getUserMedia once.'; if (location.protocol !== 'https:') { - device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; + if (document.domain.search && document.domain.search(/localhost|127.0./g) === -1) { + device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; + } } } else { if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) { @@ -893,7 +901,7 @@ if ('getSenders' in mozRTCPeerConnection.prototype) { isRTPSenderReplaceTracksSupported = true; } - } else if (DetectRTC.browser.isChrome) { + } else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') { /*global webkitRTCPeerConnection:true */ if ('getSenders' in webkitRTCPeerConnection.prototype) { isRTPSenderReplaceTracksSupported = true; diff --git a/dev/MultiPeersHandler.js b/dev/MultiPeersHandler.js index f03ad2d2..a1bbafed 100755 --- a/dev/MultiPeersHandler.js +++ b/dev/MultiPeersHandler.js @@ -293,7 +293,7 @@ function MultiPeers(connection) { } if (message.enableMedia) { - if (connection.attachStreams.length) { + if (connection.attachStreams.length || connection.dontCaptureUserMedia) { var streamsToShare = {}; connection.attachStreams.forEach(function(stream) { streamsToShare[stream.streamid] = { diff --git a/dev/RTCPeerConnection.js b/dev/RTCPeerConnection.js index 0322b1bf..582ea61d 100755 --- a/dev/RTCPeerConnection.js +++ b/dev/RTCPeerConnection.js @@ -54,10 +54,6 @@ function onPluginRTCInitialized(pluginRTCObject) { } if (typeof PluginRTC !== 'undefined') onPluginRTCInitialized(PluginRTC); -var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; -var isIE = !!document.documentMode; -var isPluginRTC = isSafari || isIE; - function PeerInitiator(config) { var connection = config.rtcMultiConnection; diff --git a/dev/SocketConnection.js b/dev/SocketConnection.js index 5893eac9..9949b559 100755 --- a/dev/SocketConnection.js +++ b/dev/SocketConnection.js @@ -10,7 +10,13 @@ function SocketConnection(connection, connectCallback) { parameters += '&maxRelayLimitPerUser=' + (connection.maxRelayLimitPerUser || 2); } - var socket = io.connect((connection.socketURL || '/') + parameters, connection.socketOptions); + var socket; + + try { + socket = io((connection.socketURL || '/') + parameters); + } catch (e) { + socket = io.connect((connection.socketURL || '/') + parameters, connection.socketOptions); + } var mPeer = connection.multiPeersHandler; diff --git a/dev/getUserMedia.js b/dev/getUserMedia.js index 636c2b7a..a7d65d5a 100755 --- a/dev/getUserMedia.js +++ b/dev/getUserMedia.js @@ -92,16 +92,6 @@ function getUserMediaHandler(options) { return; } - if (typeof DetectRTC !== 'undefined') { - if (!DetectRTC.hasMicrophone) { - options.localMediaConstraints.audio = false; - } - - if (!DetectRTC.hasWebcam) { - options.localMediaConstraints.video = false; - } - } - navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream) { stream.streamid = stream.streamid || stream.id || getRandomString(); stream.idInstance = idInstance; diff --git a/dev/globals.js b/dev/globals.js index e612f8ec..845cced8 100755 --- a/dev/globals.js +++ b/dev/globals.js @@ -10,10 +10,12 @@ var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|Black if (typeof cordova !== 'undefined') { isMobileDevice = true; + isChrome = true; } if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { isMobileDevice = true; + isChrome = true; } var isPluginRTC = !isMobileDevice && (isSafari || isIE); diff --git a/dev/ios-hacks.js b/dev/ios-hacks.js index 0cef542f..5b426064 100644 --- a/dev/ios-hacks.js +++ b/dev/ios-hacks.js @@ -1,15 +1,16 @@ // ios-hacks.js function setCordovaAPIs() { + if (DetectRTC.osName !== 'iOS') return; if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return; - if (!window.device || window.device.platform !== 'iOS') return; var iosrtc = cordova.plugins.iosrtc; - RTCPeerConnection = iosrtc.RTCPeerConnection; - RTCSessionDescription = iosrtc.RTCSessionDescription; - RTCIceCandidate = iosrtc.RTCIceCandidate; - MediaStream = iosrtc.MediaStream; - MediaStreamTrack = iosrtc.MediaStreamTrack; + window.webkitRTCPeerConnection = iosrtc.RTCPeerConnection; + window.RTCSessionDescription = iosrtc.RTCSessionDescription; + window.RTCIceCandidate = iosrtc.RTCIceCandidate; + window.MediaStream = iosrtc.MediaStream; + window.MediaStreamTrack = iosrtc.MediaStreamTrack; + navigator.getUserMedia = navigator.webkitGetUserMedia = iosrtc.getUserMedia; iosrtc.debug.enable('iosrtc*'); iosrtc.registerGlobals(); diff --git a/dist/rmc3.fbr.min.js b/dist/rmc3.fbr.min.js index be7639a3..68293481 100644 --- a/dist/rmc3.fbr.min.js +++ b/dist/rmc3.fbr.min.js @@ -1,3 +1,3 @@ -// Last time updated: 2016-02-25 12:41:18 PM UTC +// Last time updated: 2016-03-02 9:37:17 AM UTC function FileBufferReader(){function fbrClone(from,to){if(null==from||"object"!=typeof from)return from;if(from.constructor!=Object&&from.constructor!=Array)return from;if(from.constructor==Date||from.constructor==RegExp||from.constructor==Function||from.constructor==String||from.constructor==Number||from.constructor==Boolean)return new from.constructor(from);to=to||new from.constructor;for(var name in from)to[name]="undefined"==typeof to[name]?fbrClone(from[name],null):to[name];return to}var fbr=this,fbrHelper=new FileBufferReaderHelper;fbr.chunks={},fbr.users={},fbr.readAsArrayBuffer=function(file,earlyCallback,extra){if(!file.slice)return void console.warn("Not a real File object.",file);if(extra=extra||{userid:0},file.extra)if("string"==typeof file.extra)extra.extra=file.extra;else for(var e in file.extra)extra[e]=file.extra[e];extra.fileName=file.name,file.uuid&&(extra.fileUniqueId=file.uuid);var options={uuid:file.uuid||0,file:file,earlyCallback:earlyCallback,extra:extra,chunkSize:extra.chunkSize};fbrHelper.readAsArrayBuffer(fbr,options)},fbr.getNextChunk=function(fileUUID,callback,userid){var allFileChunks=fbr.chunks[fileUUID];if(allFileChunks){var currentPosition;"undefined"!=typeof userid?(fbr.users[userid+""]||(fbr.users[userid+""]={fileUUID:fileUUID,userid:userid,currentPosition:-1}),fbr.users[userid+""].currentPosition++,currentPosition=fbr.users[userid+""].currentPosition):(fbr.chunks[fileUUID].currentPosition++,currentPosition=fbr.chunks[fileUUID].currentPosition);var nextChunk=allFileChunks[currentPosition];nextChunk&&(nextChunk=fbrClone(nextChunk),"undefined"!=typeof userid&&(nextChunk.remoteUserId=userid+""),nextChunk.start&&fbr.onBegin(nextChunk),nextChunk.end&&fbr.onEnd(nextChunk),fbr.onProgress(nextChunk),fbr.convertToArrayBuffer(nextChunk,function(buffer){return nextChunk.currentPosition==nextChunk.maxChunks?void callback(buffer,!0):void callback(buffer,!1)}))}};var fbReceiver=new FileBufferReceiver(fbr);fbr.addChunk=function(chunk,callback){return chunk?void fbReceiver.receive(chunk,function(uuid){fbr.convertToArrayBuffer({readyForNextChunk:!0,uuid:uuid},callback)}):void console.error("Chunk is missing.")},fbr.onBegin=function(){},fbr.onEnd=function(){},fbr.onProgress=function(){},fbr.convertToObject=FileConverter.ConvertToObject,fbr.convertToArrayBuffer=FileConverter.ConvertToArrayBuffer,fbr.setMultipleUsers=function(){}}function FileBufferReaderHelper(){function processInWebWorker(_function){var blob=URL.createObjectURL(new Blob([_function.toString(),"this.onmessage = function (e) {"+_function.name+"(e.data);}"],{type:"application/javascript"}));return window.fileBufferWorker||(window.fileBufferWorker=new Worker(blob)),window.fileBufferWorker}function fileReaderWrapper(options,callback){function addChunks(fileName,binarySlice,addChunkCallback){numOfChunksInSlice=Math.ceil(binarySlice.byteLength/chunkSize);for(var i=0;numOfChunksInSlice>i;i++){var start=i*chunkSize;chunks[currentPosition]=binarySlice.slice(start,Math.min(start+chunkSize,binarySlice.byteLength)),callback({uuid:file.uuid,buffer:chunks[currentPosition],currentPosition:currentPosition,maxChunks:maxChunks,size:file.size,name:file.name||options.extra.fileName,lastModifiedDate:file.lastModifiedDate?file.lastModifiedDate.toString():"",type:file.type,extra:options.extra||options}),currentPosition++}currentPosition==maxChunks&&(hasEntireFile=!0),addChunkCallback()}callback=callback||function(chunk){postMessage(chunk)};var file=options.file;file.uuid||(file.uuid=options.fileUniqueId||(100*Math.random()).toString().replace(/\./g,""));var chunkSize=options.chunkSize||15e3,sliceId=0,cacheSize=chunkSize,chunksPerSlice=Math.floor(Math.min(1e8,cacheSize)/chunkSize),sliceSize=chunksPerSlice*chunkSize,maxChunks=Math.ceil(file.size/chunkSize);file.maxChunks=maxChunks;var numOfChunksInSlice,hasEntireFile,currentPosition=0,chunks=[];callback({currentPosition:currentPosition,uuid:file.uuid,maxChunks:maxChunks,size:file.size,name:file.name||options.extra.fileName,type:file.type,lastModifiedDate:file.lastModifiedDate?file.lastModifiedDate.toString():"",start:!0,extra:options.extra||options,url:URL.createObjectURL(file)});var blob,reader=new FileReader;reader.onloadend=function(evt){evt.target.readyState==FileReader.DONE&&addChunks(file.name,evt.target.result,function(){sliceId++,(sliceId+1)*sliceSize5&&5==chunk.currentPosition&&earlyCallback&&(earlyCallback(chunk.uuid),earlyCallback=null)}var earlyCallback=options.earlyCallback;if(delete options.earlyCallback,navigator.mozGetUserMedia&&(window.___Worker=window.Worker,delete window.Worker),window.Worker&&"function"==typeof Worker){var webWorker=processInWebWorker(fileReaderWrapper);webWorker.onmessage=function(event){processChunk(event.data)},webWorker.postMessage(options)}else fileReaderWrapper(options,processChunk),navigator.mozGetUserMedia&&(window.Worker=window.___Worker)}}function FileBufferReceiver(fbr){function receive(chunk,callback){if(!chunk.uuid)return void fbr.convertToObject(chunk,function(object){receive(object)});if(chunk.start&&!packets[chunk.uuid]&&(packets[chunk.uuid]=[],missedChunks[chunk.uuid]&&(packets[chunk.uuid].push(chunk.buffer),missedChunks[chunk.uuid].forEach(function(chunk){receive(chunk,callback)}),delete missedChunks[chunk.uuid]),fbr.onBegin&&fbr.onBegin(chunk)),!chunk.end&&chunk.buffer){if(!packets[chunk.uuid])return missedChunks[chunk.uuid]||(missedChunks[chunk.uuid]=[]),void missedChunks[chunk.uuid].push(chunk);-1==packets[chunk.uuid].indexOf(chunk.buffer)&&packets[chunk.uuid].push(chunk.buffer)}if(chunk.end){for(var _packets=packets[chunk.uuid],finalArray=[],length=_packets.length,i=0;length>i;i++)_packets[i]&&finalArray.push(_packets[i]);var blob=new Blob(finalArray,{type:chunk.type});blob=merge(blob,chunk),blob.url=URL.createObjectURL(blob),blob.uuid=chunk.uuid||blob.extra.fileUniqueId,blob.name=blob.name||blob.extra.fileName,blob.size||console.error("Something went wrong. Blob Size is 0."),fbr.onEnd&&fbr.onEnd(blob)}chunk.buffer&&fbr.onProgress&&fbr.onProgress(chunk),chunk.end||callback(chunk.uuid)}function merge(mergein,mergeto){if(mergein||(mergein={}),!mergeto)return mergein;for(var item in mergeto)mergein[item]=mergeto[item];return mergein}var packets={},missedChunks=[];this.receive=receive}function merge(mergein,mergeto){if(mergein||(mergein={}),!mergeto)return mergein;for(var item in mergeto)mergein[item]=mergeto[item];return mergein}window.FileSelector=function(){function selectFile(callback,multiple){var file=document.createElement("input");file.type="file",multiple&&(file.multiple=!0),file.onchange=function(){return multiple?file.files.length?void callback(file.files):void console.error("No file selected."):file.files[0]?(callback(file.files[0]),void file.parentNode.removeChild(file)):void console.error("No file selected.")},file.style.display="none",(document.body||document.documentElement).appendChild(file),fireClickEvent(file)}function fireClickEvent(element){var evt=new window.MouseEvent("click",{view:window,bubbles:!0,cancelable:!0,button:0,buttons:0,mozInputSource:1});element.dispatchEvent(evt)}var selector=this;selector.selectSingleFile=selectFile,selector.selectMultipleFiles=function(callback){selectFile(callback,!0)}};var FileConverter={ConvertToArrayBuffer:function(object,callback){binarize.pack(object,function(dataView){callback(dataView.buffer)})},ConvertToObject:function(buffer,callback){binarize.unpack(buffer,callback)}};!function(root){var debug=!1,BIG_ENDIAN=!1,LITTLE_ENDIAN=!0,TYPE_LENGTH=Uint8Array.BYTES_PER_ELEMENT,LENGTH_LENGTH=Uint16Array.BYTES_PER_ELEMENT,BYTES_LENGTH=Uint32Array.BYTES_PER_ELEMENT,Types={NULL:0,UNDEFINED:1,STRING:2,NUMBER:3,BOOLEAN:4,ARRAY:5,OBJECT:6,INT8ARRAY:7,INT16ARRAY:8,INT32ARRAY:9,UINT8ARRAY:10,UINT16ARRAY:11,UINT32ARRAY:12,FLOAT32ARRAY:13,FLOAT64ARRAY:14,ARRAYBUFFER:15,BLOB:16,FILE:16,BUFFER:17};if(debug)var TypeNames=["NULL","UNDEFINED","STRING","NUMBER","BOOLEAN","ARRAY","OBJECT","INT8ARRAY","INT16ARRAY","INT32ARRAY","UINT8ARRAY","UINT16ARRAY","UINT32ARRAY","FLOAT32ARRAY","FLOAT64ARRAY","ARRAYBUFFER","BLOB","BUFFER"];var Length=[null,null,"Uint16","Float64","Uint8",null,null,"Int8","Int16","Int32","Uint8","Uint16","Uint32","Float32","Float64","Uint8","Uint8","Uint8"],binary_dump=function(view,start,length){var table=[],endianness=BIG_ENDIAN,ROW_LENGTH=40;table[0]=[];for(var i=0;ROW_LENGTH>i;i++)table[0][i]=10>i?"0"+i.toString(10):i.toString(10);for(i=0;length>i;i++){var code=view.getUint8(start+i,endianness),index=~~(i/ROW_LENGTH)+1;"undefined"==typeof table[index]&&(table[index]=[]),table[index][i%ROW_LENGTH]=16>code?"0"+code.toString(16):code.toString(16)}for(console.log("%c"+table[0].join(" "),"font-weight: bold;"),i=1;ij;j++,cursor+=unit)view.setUint16(cursor,value.charCodeAt(j),endianness);break;case Types.NUMBER:case Types.BOOLEAN:debug&&console.info("%c"+value.toString(),"font-weight:bold;"),view["set"+type_name](cursor,value,endianness),cursor+=unit;break;case Types.INT8ARRAY:case Types.INT16ARRAY:case Types.INT32ARRAY:case Types.UINT8ARRAY:case Types.UINT16ARRAY:case Types.UINT32ARRAY:case Types.FLOAT32ARRAY:case Types.FLOAT64ARRAY:var _view=new Uint8Array(view.buffer,cursor,byte_length);_view.set(new Uint8Array(value.buffer)),cursor+=byte_length;break;case Types.ARRAYBUFFER:case Types.BUFFER:var _view=new Uint8Array(view.buffer,cursor,byte_length);_view.set(new Uint8Array(value)),cursor+=byte_length;break;case Types.BLOB:case Types.ARRAY:case Types.OBJECT:break;default:throw"TypeError: Unexpected type found."}debug&&binary_dump(view,start,cursor-start)}return view},unpack=function(view,cursor){var type,length,byte_length,value,elem,i=0,endianness=BIG_ENDIAN,start=cursor;type=view.getUint8(cursor,endianness),cursor+=TYPE_LENGTH,debug&&console.info("Unpacking",type,TypeNames[type]),(type===Types.ARRAY||type===Types.OBJECT)&&(length=view.getUint16(cursor,endianness),cursor+=LENGTH_LENGTH,debug&&console.info("Content Length",length)),byte_length=view.getUint32(cursor,endianness),cursor+=BYTES_LENGTH,debug&&console.info("Byte Length",byte_length,"bytes");var type_name=Length[type],unit=null===type_name?0:root[type_name+"Array"].BYTES_PER_ELEMENT;switch(type){case Types.NULL:case Types.UNDEFINED:debug&&binary_dump(view,start,cursor-start),value=null;break;case Types.STRING:length=byte_length/unit;var string=[];for(i=0;length>i;i++){var code=view.getUint16(cursor,endianness);cursor+=unit,string.push(String.fromCharCode(code))}value=string.join(""),debug&&(console.info('Actual Content %c"'+value+'"',"font-weight:bold;"),binary_dump(view,start,cursor-start));break;case Types.NUMBER:value=view.getFloat64(cursor,endianness),cursor+=unit,debug&&(console.info('Actual Content %c"'+value.toString()+'"',"font-weight:bold;"),binary_dump(view,start,cursor-start));break;case Types.BOOLEAN:value=1===view.getUint8(cursor,endianness)?!0:!1,cursor+=unit,debug&&(console.info('Actual Content %c"'+value.toString()+'"',"font-weight:bold;"),binary_dump(view,start,cursor-start));break;case Types.INT8ARRAY:case Types.INT16ARRAY:case Types.INT32ARRAY:case Types.UINT8ARRAY:case Types.UINT16ARRAY:case Types.UINT32ARRAY:case Types.FLOAT32ARRAY:case Types.FLOAT64ARRAY:case Types.ARRAYBUFFER:elem=view.buffer.slice(cursor,cursor+byte_length),cursor+=byte_length,value=type===Types.ARRAYBUFFER?elem:new root[type_name+"Array"](elem),debug&&binary_dump(view,start,cursor-start);break;case Types.BLOB:if(debug&&binary_dump(view,start,cursor-start),root.Blob){var mime=unpack(view,cursor),buffer=unpack(view,mime.cursor);cursor=buffer.cursor,value=new Blob([buffer.value],{type:mime.value})}else elem=view.buffer.slice(cursor,cursor+byte_length),cursor+=byte_length,value=new Buffer(elem);break;case Types.ARRAY:for(debug&&binary_dump(view,start,cursor-start),value=[],i=0;length>i;i++)elem=unpack(view,cursor),cursor=elem.cursor,value.push(elem.value);break;case Types.OBJECT:for(debug&&binary_dump(view,start,cursor-start),value={},i=0;length>i;i++){var key=unpack(view,cursor),val=unpack(view,key.cursor);cursor=val.cursor,value[key.value]=val.value}break;default:throw"TypeError: Type not supported."}return{value:value,cursor:cursor}},deferredSerialize=function(array,callback){for(var length=array.length,results=[],count=0,byte_length=0,i=0;i 34) { - sdpConstraints = { - OfferToReceiveAudio: !!config.OfferToReceiveAudio, - OfferToReceiveVideo: !!config.OfferToReceiveVideo - }; - } + 'use strict'; - return sdpConstraints; - } + var navigator = window.navigator; - var RTCPeerConnection; - if (typeof mozRTCPeerConnection !== 'undefined') { - RTCPeerConnection = mozRTCPeerConnection; - } else if (typeof webkitRTCPeerConnection !== 'undefined') { - RTCPeerConnection = webkitRTCPeerConnection; - } else if (typeof window.RTCPeerConnection !== 'undefined') { - RTCPeerConnection = window.RTCPeerConnection; - } else { - console.error('WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.'); - RTCPeerConnection = window.RTCSessionDescription = window.RTCIceCandidate = function() {}; - } + if (typeof navigator !== 'undefined') { + if (typeof navigator.webkitGetUserMedia !== 'undefined') { + navigator.getUserMedia = navigator.webkitGetUserMedia; + } - var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription; - var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; - var MediaStreamTrack = window.MediaStreamTrack; + if (typeof navigator.mozGetUserMedia !== 'undefined') { + navigator.getUserMedia = navigator.mozGetUserMedia; + } + } else { + navigator = { + getUserMedia: function() {}, + userAgent: 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45' + }; + } - var Plugin = {}; + var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); + var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob); - function onPluginRTCInitialized(pluginRTCObject) { - Plugin = pluginRTCObject; - MediaStreamTrack = Plugin.MediaStreamTrack; - RTCPeerConnection = Plugin.RTCPeerConnection; - RTCIceCandidate = Plugin.RTCIceCandidate; - RTCSessionDescription = Plugin.RTCSessionDescription; - } - if (typeof PluginRTC !== 'undefined') onPluginRTCInitialized(PluginRTC); + var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; + var isFirefox = typeof window.InstallTrigger !== 'undefined'; + var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; + var isChrome = !!window.chrome && !isOpera; + var isIE = !!document.documentMode && !isEdge; - var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; - var isIE = !!document.documentMode; - var isPluginRTC = isSafari || isIE; + // this one can also be used: + // https://www.websocket.org/js/stuff.js (DetectBrowser.js) - function PeerInitiator(config) { - var connection = config.rtcMultiConnection; + function getBrowserInfo() { + var nVer = navigator.appVersion; + var nAgt = navigator.userAgent; + var browserName = navigator.appName; + var fullVersion = '' + parseFloat(navigator.appVersion); + var majorVersion = parseInt(navigator.appVersion, 10); + var nameOffset, verOffset, ix; - this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra; - this.remoteUserId = config.remoteUserId; - this.streams = []; - this.channels = []; - this.connectionDescription = config.connectionDescription; + // In Opera, the true version is after 'Opera' or after 'Version' + if (isOpera) { + browserName = 'Opera'; + try { + fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0]; + majorVersion = fullVersion.split('.')[0]; + } catch (e) { + fullVersion = '0.0.0.0'; + majorVersion = 0; + } + } + // In MSIE, the true version is after 'MSIE' in userAgent + else if (isIE) { + verOffset = nAgt.indexOf('MSIE'); + browserName = 'IE'; + fullVersion = nAgt.substring(verOffset + 5); + } + // In Chrome, the true version is after 'Chrome' + else if (isChrome) { + verOffset = nAgt.indexOf('Chrome'); + browserName = 'Chrome'; + fullVersion = nAgt.substring(verOffset + 7); + } + // In Safari, the true version is after 'Safari' or after 'Version' + else if (isSafari) { + verOffset = nAgt.indexOf('Safari'); + browserName = 'Safari'; + fullVersion = nAgt.substring(verOffset + 7); - var that = this; + if ((verOffset = nAgt.indexOf('Version')) !== -1) { + fullVersion = nAgt.substring(verOffset + 8); + } + } + // In Firefox, the true version is after 'Firefox' + else if (isFirefox) { + verOffset = nAgt.indexOf('Firefox'); + browserName = 'Firefox'; + fullVersion = nAgt.substring(verOffset + 8); + } - if (config.remoteSdp) { - this.connectionDescription = config.remoteSdp.connectionDescription; - } + // In most other browsers, 'name/version' is at the end of userAgent + else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) { + browserName = nAgt.substring(nameOffset, verOffset); + fullVersion = nAgt.substring(verOffset + 1); - var allRemoteStreams = {}; + if (browserName.toLowerCase() === browserName.toUpperCase()) { + browserName = navigator.appName; + } + } - if (Object.observe) { - var that = this; - Object.observe(this.channels, function(changes) { - changes.forEach(function(change) { - if (change.type === 'add') { - change.object[change.name].addEventListener('close', function() { - delete that.channels[that.channels.indexOf(change.object[change.name])]; - that.channels = removeNullEntries(that.channels); - }, false); - } - if (change.type === 'remove' || change.type === 'delete') { - if (that.channels.indexOf(change.object[change.name]) !== -1) { - delete that.channels.indexOf(change.object[change.name]); - } - } + if (isEdge) { + browserName = 'Edge'; + // fullVersion = navigator.userAgent.split('Edge/')[1]; + fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString(); + } - that.channels = removeNullEntries(that.channels); - }); - }); - } + // trim the fullVersion string at semicolon/space if present + if ((ix = fullVersion.indexOf(';')) !== -1) { + fullVersion = fullVersion.substring(0, ix); + } - defaults.sdpConstraints = setSdpConstraints({ - OfferToReceiveAudio: true, - OfferToReceiveVideo: true - }); + if ((ix = fullVersion.indexOf(' ')) !== -1) { + fullVersion = fullVersion.substring(0, ix); + } - var peer; + majorVersion = parseInt('' + fullVersion, 10); - var renegotiatingPeer = !!config.renegotiatingPeer; - if (config.remoteSdp) { - renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer; - } + if (isNaN(majorVersion)) { + fullVersion = '' + parseFloat(navigator.appVersion); + majorVersion = parseInt(navigator.appVersion, 10); + } - var localStreams = []; - connection.attachStreams.forEach(function(stream) { - if (!!stream) localStreams.push(stream); - }); + return { + fullVersion: fullVersion, + version: majorVersion, + name: browserName, + isPrivateBrowsing: false + }; + } - if (!renegotiatingPeer) { - peer = new RTCPeerConnection(navigator.onLine ? { - iceServers: connection.iceServers, - iceTransports: 'all' - } : null, connection.optionalArgument); - } else { - peer = config.peerRef; + // via: https://gist.github.com/cou929/7973956 - peer.getLocalStreams().forEach(function(stream) { - localStreams.forEach(function(localStream, index) { - if (stream == localStream) { - delete localStreams[index]; + function retry(isDone, next) { + var currentTrial = 0, + maxRetry = 50, + interval = 10, + isTimeout = false; + var id = window.setInterval( + function() { + if (isDone()) { + window.clearInterval(id); + next(isTimeout); } - }); - - connection.removeStreams.forEach(function(streamToRemove, index) { - if (stream === streamToRemove) { - stream = connection.beforeRemovingStream(stream); - if (stream && !!peer.removeStream) { - peer.removeStream(stream); - } - - localStreams.forEach(function(localStream, index) { - if (streamToRemove == localStream) { - delete localStreams[index]; - } - }); + if (currentTrial++ > maxRetry) { + window.clearInterval(id); + isTimeout = true; + next(isTimeout); } - }); - }); + }, + 10 + ); } - if (connection.DetectRTC.browser.name === 'Firefox') { - peer.removeStream = function(stream) { - stream.mute(); - connection.StreamsHandler.onSyncNeeded(stream.streamid, 'stream-removed'); - }; - } - - peer.onicecandidate = function(event) { - if (!event.candidate) return; - config.onLocalCandidate({ - candidate: event.candidate.candidate, - sdpMid: event.candidate.sdpMid, - sdpMLineIndex: event.candidate.sdpMLineIndex - }); - }; - - var isFirefoxOffered = !isFirefox; - if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered) { - isFirefoxOffered = true; - } - - localStreams.forEach(function(localStream) { - if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) { - return; - } - - if (config.dontAttachLocalStream) { - return; - } - - localStream = connection.beforeAddingStream(localStream); - if (localStream) { - peer.addStream(localStream); - } - }); - - peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() { - var extra = that.extra; - if (connection.peers[that.remoteUserId]) { - extra = connection.peers[that.remoteUserId].extra || extra; - } - - if (!peer) { - return; + function isIE10OrLater(userAgent) { + var ua = userAgent.toLowerCase(); + if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) { + return false; } - - config.onPeerStateChanged({ - iceConnectionState: peer.iceConnectionState, - iceGatheringState: peer.iceGatheringState, - signalingState: peer.signalingState, - extra: extra, - userid: that.remoteUserId - }); - }; - - var sdpConstraints = { - OfferToReceiveAudio: !!localStreams.length, - OfferToReceiveVideo: !!localStreams.length - }; - - if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints; - - defaults.sdpConstraints = setSdpConstraints(sdpConstraints); - - peer.onaddstream = function(event) { - var streamsToShare = {}; - if (config.remoteSdp && config.remoteSdp.streamsToShare) { - streamsToShare = config.remoteSdp.streamsToShare; - } else if (config.streamsToShare) { - streamsToShare = config.streamsToShare; + var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua); + if (match && parseInt(match[1], 10) >= 10) { + return true; } + return false; + } - var streamToShare = streamsToShare[event.stream.id]; - if (streamToShare) { - event.stream.isAudio = streamToShare.isAudio; - event.stream.isVideo = streamToShare.isVideo; - event.stream.isScreen = streamToShare.isScreen; - } + function detectPrivateMode(callback) { + var isPrivate; - event.stream.streamid = event.stream.id; - if (!event.stream.stop) { - event.stream.stop = function() { - if (isFirefox) { - fireEvent(this, 'ended'); + if (window.webkitRequestFileSystem) { + window.webkitRequestFileSystem( + window.TEMPORARY, 1, + function() { + isPrivate = false; + }, + function(e) { + console.log(e); + isPrivate = true; } - }; - } - allRemoteStreams[event.stream.id] = event.stream; - config.onRemoteStream(event.stream); - }; + ); + } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) { + var db; + try { + db = window.indexedDB.open('test'); + } catch (e) { + isPrivate = true; + } - peer.onremovestream = function(event) { - event.stream.streamid = event.stream.id; + if (typeof isPrivate === 'undefined') { + retry( + function isDone() { + return db.readyState === 'done' ? true : false; + }, + function next(isTimeout) { + if (!isTimeout) { + isPrivate = db.result ? false : true; + } + } + ); + } + } else if (isIE10OrLater(window.navigator.userAgent)) { + isPrivate = false; + try { + if (!window.indexedDB) { + isPrivate = true; + } + } catch (e) { + isPrivate = true; + } + } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) { + try { + window.localStorage.setItem('test', 1); + } catch (e) { + isPrivate = true; + } - if (allRemoteStreams[event.stream.id]) { - delete allRemoteStreams[event.stream.id]; + if (typeof isPrivate === 'undefined') { + isPrivate = false; + window.localStorage.removeItem('test'); + } } - config.onRemoteStreamRemoved(event.stream); - }; - - this.addRemoteCandidate = function(remoteCandidate) { - peer.addIceCandidate(new RTCIceCandidate(remoteCandidate)); - }; - - this.addRemoteSdp = function(remoteSdp) { - remoteSdp.sdp = connection.processSdp(remoteSdp.sdp); - peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), function() {}, function(error) { - if (!!connection.enableLogs) { - console.error(JSON.stringify(error, null, '\t')); + retry( + function isDone() { + return typeof isPrivate !== 'undefined' ? true : false; + }, + function next(isTimeout) { + callback(isPrivate); } - }); - }; - - var isOfferer = true; - - if (config.remoteSdp) { - isOfferer = false; - } - - if (connection.session.data === true) { - createDataChannel(); - } - - if (config.remoteSdp) { - if (config.remoteSdp.remotePeerSdpConstraints) { - sdpConstraints = config.remoteSdp.remotePeerSdpConstraints; - } - defaults.sdpConstraints = setSdpConstraints(sdpConstraints); - this.addRemoteSdp(config.remoteSdp); + ); } - function createDataChannel() { - if (!isOfferer) { - peer.ondatachannel = function(event) { - var channel = event.channel; - setChannelEvents(channel); - }; - return; - } + var isMobile = { + Android: function() { + return navigator.userAgent.match(/Android/i); + }, + BlackBerry: function() { + return navigator.userAgent.match(/BlackBerry/i); + }, + iOS: function() { + return navigator.userAgent.match(/iPhone|iPad|iPod/i); + }, + Opera: function() { + return navigator.userAgent.match(/Opera Mini/i); + }, + Windows: function() { + return navigator.userAgent.match(/IEMobile/i); + }, + any: function() { + return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); + }, + getOsName: function() { + var osName = 'Unknown OS'; + if (isMobile.Android()) { + osName = 'Android'; + } - var channel = peer.createDataChannel('RTCDataChannel', {}); - setChannelEvents(channel); - } + if (isMobile.BlackBerry()) { + osName = 'BlackBerry'; + } - function setChannelEvents(channel) { - // force ArrayBuffer in Firefox; which uses "Blob" by default. - channel.binaryType = 'arraybuffer'; + if (isMobile.iOS()) { + osName = 'iOS'; + } - channel.onmessage = function(event) { - config.onDataChannelMessage(event.data); - }; + if (isMobile.Opera()) { + osName = 'Opera Mini'; + } - channel.onopen = function() { - config.onDataChannelOpened(channel); - }; + if (isMobile.Windows()) { + osName = 'Windows'; + } - channel.onerror = function(error) { - config.onDataChannelError(error); - }; + return osName; + } + }; - channel.onclose = function(event) { - config.onDataChannelClosed(event); - }; + // via: http://jsfiddle.net/ChristianL/AVyND/ + function detectDesktopOS() { + var unknown = '-'; - channel.internalSend = channel.send; - channel.send = function(data) { - if (channel.readyState !== 'open') { - return; - } + var nVer = navigator.appVersion; + var nAgt = navigator.userAgent; - channel.internalSend(data); - }; + var os = unknown; + var clientStrings = [{ + s: 'Windows 10', + r: /(Windows 10.0|Windows NT 10.0)/ + }, { + s: 'Windows 8.1', + r: /(Windows 8.1|Windows NT 6.3)/ + }, { + s: 'Windows 8', + r: /(Windows 8|Windows NT 6.2)/ + }, { + s: 'Windows 7', + r: /(Windows 7|Windows NT 6.1)/ + }, { + s: 'Windows Vista', + r: /Windows NT 6.0/ + }, { + s: 'Windows Server 2003', + r: /Windows NT 5.2/ + }, { + s: 'Windows XP', + r: /(Windows NT 5.1|Windows XP)/ + }, { + s: 'Windows 2000', + r: /(Windows NT 5.0|Windows 2000)/ + }, { + s: 'Windows ME', + r: /(Win 9x 4.90|Windows ME)/ + }, { + s: 'Windows 98', + r: /(Windows 98|Win98)/ + }, { + s: 'Windows 95', + r: /(Windows 95|Win95|Windows_95)/ + }, { + s: 'Windows NT 4.0', + r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ + }, { + s: 'Windows CE', + r: /Windows CE/ + }, { + s: 'Windows 3.11', + r: /Win16/ + }, { + s: 'Android', + r: /Android/ + }, { + s: 'Open BSD', + r: /OpenBSD/ + }, { + s: 'Sun OS', + r: /SunOS/ + }, { + s: 'Linux', + r: /(Linux|X11)/ + }, { + s: 'iOS', + r: /(iPhone|iPad|iPod)/ + }, { + s: 'Mac OS X', + r: /Mac OS X/ + }, { + s: 'Mac OS', + r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ + }, { + s: 'QNX', + r: /QNX/ + }, { + s: 'UNIX', + r: /UNIX/ + }, { + s: 'BeOS', + r: /BeOS/ + }, { + s: 'OS/2', + r: /OS\/2/ + }, { + s: 'Search Bot', + r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ + }]; + for (var id in clientStrings) { + var cs = clientStrings[id]; + if (cs.r.test(nAgt)) { + os = cs.s; + break; + } + } - peer.channel = channel; + var osVersion = unknown; + + if (/Windows/.test(os)) { + if (/Windows (.*)/.test(os)) { + osVersion = /Windows (.*)/.exec(os)[1]; + } + os = 'Windows'; + } + + switch (os) { + case 'Mac OS X': + if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) { + osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; + } + break; + case 'Android': + if (/Android ([\.\_\d]+)/.test(nAgt)) { + osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; + } + break; + case 'iOS': + if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) { + osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); + osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); + } + break; + } + + return { + osName: os, + osVersion: osVersion + }; } - if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') { - defaults.sdpConstraints = setSdpConstraints({ - OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio), - OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio) - }); + var osName = 'Unknown OS'; + var osVersion = 'Unknown OS Version'; + + if (isMobile.any()) { + osName = isMobile.getOsName(); + } else { + var osInfo = detectDesktopOS(); + osName = osInfo.osName; + osVersion = osInfo.osVersion; } - var streamsToShare = {}; - peer.getLocalStreams().forEach(function(stream) { - streamsToShare[stream.streamid] = { - isAudio: !!stream.isAudio, - isVideo: !!stream.isVideo, - isScreen: !!stream.isScreen - }; + var isCanvasSupportsStreamCapturing = false; + var isVideoSupportsStreamCapturing = false; + ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) { + if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) { + isCanvasSupportsStreamCapturing = true; + } + + if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) { + isVideoSupportsStreamCapturing = true; + } }); - peer[isOfferer ? 'createOffer' : 'createAnswer'](function(localSdp) { - localSdp.sdp = connection.processSdp(localSdp.sdp); - peer.setLocalDescription(localSdp); - config.onLocalSdp({ - type: localSdp.type, - sdp: localSdp.sdp, - remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, - renegotiatingPeer: !!config.renegotiatingPeer || false, - connectionDescription: that.connectionDescription, - dontGetRemoteStream: !!config.dontGetRemoteStream, - extra: connection ? connection.extra : {}, - streamsToShare: streamsToShare, - isFirefoxOffered: isFirefox - }); - }, function(error) { - if (!!connection.enableLogs) { - console.error('sdp-error', error); + // via: https://github.com/diafygi/webrtc-ips + function DetectLocalIPAddress(callback) { + if (!DetectRTC.isWebRTCSupported) { + return; } - }, defaults.sdpConstraints); - peer.nativeClose = peer.close; - peer.close = function() { - if (!peer) { + if (DetectRTC.isORTCSupported) { return; } - try { - if (peer.iceConnectionState.search(/closed|failed/gi) === -1) { - peer.getRemoteStreams().forEach(function(stream) { - stream.stop(); - }); + getIPs(function(ip) { + //local IPs + if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) { + callback('Local: ' + ip); } - peer.nativeClose(); - } catch (e) {} - peer = null; - that.peer = null; - }; + //assume the rest are public IPs + else { + callback('Public: ' + ip); + } + }); + } - this.peer = peer; - } + //get the IP addresses associated with an account + function getIPs(callback) { + var ipDuplicates = {}; - // ios-hacks.js + //compatibility for firefox and chrome + var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; + var useWebKit = !!window.webkitRTCPeerConnection; - function setCordovaAPIs() { - if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return; - if (!window.device || window.device.platform !== 'iOS') return; + // bypass naive webrtc blocking using an iframe + if (!RTCPeerConnection) { + var iframe = document.getElementById('iframe'); + if (!iframe) { + // + throw 'NOTE: you need to have an iframe in the page right above the script tag.'; + } + var win = iframe.contentWindow; + RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection; + useWebKit = !!win.webkitRTCPeerConnection; + } - var iosrtc = cordova.plugins.iosrtc; - RTCPeerConnection = iosrtc.RTCPeerConnection; - RTCSessionDescription = iosrtc.RTCSessionDescription; - RTCIceCandidate = iosrtc.RTCIceCandidate; - MediaStream = iosrtc.MediaStream; - MediaStreamTrack = iosrtc.MediaStreamTrack; + // if still no RTCPeerConnection then it is not supported by the browser so just return + if (!RTCPeerConnection) { + return; + } - iosrtc.debug.enable('iosrtc*'); - iosrtc.registerGlobals(); - } + //minimal requirements for data connection + var mediaConstraints = { + optional: [{ + RtpDataChannels: true + }] + }; - document.addEventListener('deviceready', setCordovaAPIs, false); - setCordovaAPIs(); + //firefox already has a default stun server in about:config + // media.peerconnection.default_iceservers = + // [{"url": "stun:stun.services.mozilla.com"}] + var servers; - // CodecsHandler.js + //add same stun server for chrome + if (useWebKit) { + servers = { + iceServers: [{ + urls: 'stun:stun.services.mozilla.com' + }] + }; - var CodecsHandler = (function() { - var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); - if (typeof cordova !== 'undefined') { - isMobileDevice = true; - } + if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isFirefox && DetectRTC.browser.version <= 38) { + servers[0] = { + url: servers[0].urls + }; + } + } - if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { - isMobileDevice = true; - } + //construct a new RTCPeerConnection + var pc = new RTCPeerConnection(servers, mediaConstraints); - // "removeVPX" and "removeNonG722" methods are taken from github/mozilla/webrtc-landing - function removeVPX(sdp) { - if (!sdp || typeof sdp !== 'string') { - throw 'Invalid arguments.'; + function handleCandidate(candidate) { + //match just the IP address + var ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/; + var match = ipRegex.exec(candidate); + if (!match) { + console.warn('Could not match IP address in', candidate); + return; + } + var ipAddress = match[1]; + + //remove duplicates + if (ipDuplicates[ipAddress] === undefined) { + callback(ipAddress); + } + + ipDuplicates[ipAddress] = true; } - // this method is NOT reliable + //listen for candidate events + pc.onicecandidate = function(ice) { + //skip non-candidate events + if (ice.candidate) { + handleCandidate(ice.candidate.candidate); + } + }; - sdp = sdp.replace('a=rtpmap:100 VP8/90000\r\n', ''); - sdp = sdp.replace('a=rtpmap:101 VP9/90000\r\n', ''); + //create a bogus data channel + pc.createDataChannel(''); - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g, 'm=video $1 RTP\/SAVPF $2'); - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g, 'm=video $1 RTP\/SAVPF $2'); + //create an offer sdp + pc.createOffer(function(result) { - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); - sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); + //trigger the stun server request + pc.setLocalDescription(result, function() {}, function() {}); - sdp = sdp.replace('a=rtcp-fb:120 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:120 nack pli\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:120 ccm fir\r\n', ''); + }, function() {}); - sdp = sdp.replace('a=rtcp-fb:101 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:101 nack pli\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:101 ccm fir\r\n', ''); + //wait for a while to let everything done + setTimeout(function() { + //read candidate info from local description + var lines = pc.localDescription.sdp.split('\n'); - return sdp; + lines.forEach(function(line) { + if (line.indexOf('a=candidate:') === 0) { + handleCandidate(line); + } + }); + }, 1000); } - function disableNACK(sdp) { - if (!sdp || typeof sdp !== 'string') { - throw 'Invalid arguments.'; - } + var MediaDevices = []; - sdp = sdp.replace('a=rtcp-fb:126 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:126 nack pli\r\n', 'a=rtcp-fb:126 pli\r\n'); - sdp = sdp.replace('a=rtcp-fb:97 nack\r\n', ''); - sdp = sdp.replace('a=rtcp-fb:97 nack pli\r\n', 'a=rtcp-fb:97 pli\r\n'); + var audioInputDevices = []; + var audioOutputDevices = []; + var videoInputDevices = []; - return sdp; + if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { + // Firefox 38+ seems having support of enumerateDevices + // Thanks @xdumaine/enumerateDevices + navigator.enumerateDevices = function(callback) { + navigator.mediaDevices.enumerateDevices().then(callback); + }; } - function prioritize(codecMimeType, peer) { - if (!peer || !peer.getSenders || !peer.getSenders().length) { - return; - } - - if (!codecMimeType || typeof codecMimeType !== 'string') { - throw 'Invalid arguments.'; - } + // ---------- Media Devices detection + var canEnumerate = false; - peer.getSenders().forEach(function(sender) { - var params = sender.getParameters(); - for (var i = 0; i < params.codecs.length; i++) { - if (params.codecs[i].mimeType == codecMimeType) { - params.codecs.unshift(params.codecs.splice(i, 1)); - break; - } - } - sender.setParameters(params); - }); + /*global MediaStreamTrack:true */ + if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) { + canEnumerate = true; + } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) { + canEnumerate = true; } - function removeNonG722(sdp) { - return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, 'm=audio $1 RTP\/SAVPF 9'); - } + var hasMicrophone = false; + var hasSpeakers = false; + var hasWebcam = false; - function setBAS(sdp, bandwidth, isScreen) { - if (!bandwidth) { - return sdp; - } + var isWebsiteHasMicrophonePermissions = false; + var isWebsiteHasWebcamPermissions = false; - if (typeof isFirefox !== 'undefined' && isFirefox) { - return sdp; + // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices + // todo: switch to enumerateDevices when landed in canary. + function checkDeviceSupport(callback) { + if (!canEnumerate) { + return; } - if (isMobileDevice) { - return sdp; - } + // This method is useful only for Chrome! - if (isScreen) { - if (!bandwidth.screen) { - console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.'); - } else if (bandwidth.screen < 300) { - console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.'); - } + if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) { + navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack); } - // if screen; must use at least 300kbs - if (bandwidth.screen && isScreen) { - sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); - sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n'); + if (!navigator.enumerateDevices && navigator.enumerateDevices) { + navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator); } - // remove existing bandwidth lines - if (bandwidth.audio || bandwidth.video || bandwidth.data) { - sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + if (!navigator.enumerateDevices) { + if (callback) { + callback(); + } + return; } - if (bandwidth.audio) { - sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n'); - } + MediaDevices = []; - if (bandwidth.video) { - sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + (isScreen ? bandwidth.screen : bandwidth.video) + '\r\n'); - } + audioInputDevices = []; + audioOutputDevices = []; + videoInputDevices = []; - return sdp; - } + navigator.enumerateDevices(function(devices) { + devices.forEach(function(_device) { + var device = {}; + for (var d in _device) { + device[d] = _device[d]; + } - // Find the line in sdpLines that starts with |prefix|, and, if specified, - // contains |substr| (case-insensitive search). - function findLine(sdpLines, prefix, substr) { - return findLineInRange(sdpLines, 0, -1, prefix, substr); - } + // if it is MediaStreamTrack.getSources + if (device.kind === 'audio') { + device.kind = 'audioinput'; + } - // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix| - // and, if specified, contains |substr| (case-insensitive search). - function findLineInRange(sdpLines, startLine, endLine, prefix, substr) { - var realEndLine = endLine !== -1 ? endLine : sdpLines.length; - for (var i = startLine; i < realEndLine; ++i) { - if (sdpLines[i].indexOf(prefix) === 0) { - if (!substr || - sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) { - return i; + if (device.kind === 'video') { + device.kind = 'videoinput'; } - } - } - return null; - } - // Gets the codec payload type from an a=rtpmap:X line. - function getCodecPayloadType(sdpLine) { - var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); - var result = sdpLine.match(pattern); - return (result && result.length === 2) ? result[1] : null; - } + var skip; + MediaDevices.forEach(function(d) { + if (d.id === device.id && d.kind === device.kind) { + skip = true; + } + }); - function setVideoBitrates(sdp, params) { - if (isMobileDevice) { - return sdp; - } + if (skip) { + return; + } - params = params || {}; - var xgoogle_min_bitrate = params.min; - var xgoogle_max_bitrate = params.max; + if (!device.deviceId) { + device.deviceId = device.id; + } - var sdpLines = sdp.split('\r\n'); + if (!device.id) { + device.id = device.deviceId; + } - // VP8 - var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000'); - var vp8Payload; - if (vp8Index) { - vp8Payload = getCodecPayloadType(sdpLines[vp8Index]); - } + if (!device.label) { + device.label = 'Please invoke getUserMedia once.'; + if (location.protocol !== 'https:') { + if (document.domain.search && document.domain.search(/localhost|127.0./g) === -1) { + device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; + } + } + } else { + if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) { + isWebsiteHasWebcamPermissions = true; + } - if (!vp8Payload) { - return sdp; - } + if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) { + isWebsiteHasMicrophonePermissions = true; + } + } - var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000'); - var rtxPayload; - if (rtxIndex) { - rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]); - } + if (device.kind === 'audioinput') { + hasMicrophone = true; - if (!rtxIndex) { - return sdp; - } + if (audioInputDevices.indexOf(device) === -1) { + audioInputDevices.push(device); + } + } - var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString()); - if (rtxFmtpLineIndex !== null) { - var appendrtxNext = '\r\n'; - appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228'); - sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext); - sdp = sdpLines.join('\r\n'); - } + if (device.kind === 'audiooutput') { + hasSpeakers = true; - return sdp; - } + if (audioOutputDevices.indexOf(device) === -1) { + audioOutputDevices.push(device); + } + } - function setOpusAttributes(sdp, params) { - if (isMobileDevice) { - return sdp; - } + if (device.kind === 'videoinput') { + hasWebcam = true; - params = params || {}; + if (videoInputDevices.indexOf(device) === -1) { + videoInputDevices.push(device); + } + } - var sdpLines = sdp.split('\r\n'); + // there is no 'videoouput' in the spec. - // Opus - var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000'); - var opusPayload; - if (opusIndex) { - opusPayload = getCodecPayloadType(sdpLines[opusIndex]); - } + if (MediaDevices.indexOf(device) === -1) { + MediaDevices.push(device); + } + }); - if (!opusPayload) { - return sdp; - } + if (typeof DetectRTC !== 'undefined') { + // to sync latest outputs + DetectRTC.MediaDevices = MediaDevices; + DetectRTC.hasMicrophone = hasMicrophone; + DetectRTC.hasSpeakers = hasSpeakers; + DetectRTC.hasWebcam = hasWebcam; - var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString()); - if (opusFmtpLineIndex === null) { - return sdp; - } + DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; + DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; - var appendOpusNext = ''; - appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1'); - appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1'); + DetectRTC.audioInputDevices = audioInputDevices; + DetectRTC.audioOutputDevices = audioOutputDevices; + DetectRTC.videoInputDevices = videoInputDevices; + } - if (typeof params.maxaveragebitrate != 'undefined') { - appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8); - } + if (callback) { + callback(); + } + }); + } - if (typeof params.maxplaybackrate != 'undefined') { - appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8); - } + // check for microphone/camera support! + checkDeviceSupport(); - if (typeof params.cbr != 'undefined') { - appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1'); - } + var DetectRTC = window.DetectRTC || {}; - if (typeof params.useinbandfec != 'undefined') { - appendOpusNext += '; useinbandfec=' + params.useinbandfec; - } + // ---------- + // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion + DetectRTC.browser = getBrowserInfo(); - if (typeof params.usedtx != 'undefined') { - appendOpusNext += '; usedtx=' + params.usedtx; + detectPrivateMode(function(isPrivateBrowsing) { + DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing; + }); + + // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge + DetectRTC.browser['is' + DetectRTC.browser.name] = true; + + var isNodeWebkit = !!(window.process && (typeof window.process === 'object') && window.process.versions && window.process.versions['node-webkit']); + + // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1. + var isWebRTCSupported = false; + ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) { + if (isWebRTCSupported) { + return; } - if (typeof params.maxptime != 'undefined') { - appendOpusNext += '\r\na=maxptime:' + params.maxptime; + if (item in window) { + isWebRTCSupported = true; } + }); + DetectRTC.isWebRTCSupported = isWebRTCSupported; - sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext); + //------- + DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined'; - sdp = sdpLines.join('\r\n'); - return sdp; + // --------- Detect if system supports screen capturing API + var isScreenCapturingSupported = false; + if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) { + isScreenCapturingSupported = true; + } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) { + isScreenCapturingSupported = true; } - function preferVP9(sdp) { - if (sdp.indexOf('SAVPF 100 101') === -1 || sdp.indexOf('VP9/90000') === -1) { - return sdp; - } - - return sdp.replace('SAVPF 100 101', 'SAVPF 101 100'); + if (location.protocol !== 'https:') { + isScreenCapturingSupported = false; } + DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported; - return { - removeVPX: removeVPX, - disableNACK: disableNACK, - prioritize: prioritize, - removeNonG722: removeNonG722, - setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) { - return setBAS(sdp, bandwidth, isScreen); - }, - setVideoBitrates: function(sdp, params) { - return setVideoBitrates(sdp, params); - }, - setOpusAttributes: function(sdp, params) { - return setOpusAttributes(sdp, params); - }, - preferVP9: preferVP9 + // --------- Detect if WebAudio API are supported + var webAudio = { + isSupported: false, + isCreateMediaStreamSourceSupported: false }; - })(); - - // backward compatibility - window.BandwidthHandler = CodecsHandler; - - // OnIceCandidateHandler.js - - var OnIceCandidateHandler = (function() { - function processCandidates(connection, icePair) { - var candidate = icePair.candidate; - - var iceRestrictions = connection.candidates; - var stun = iceRestrictions.stun; - var turn = iceRestrictions.turn; - - if (!isNull(iceRestrictions.reflexive)) { - stun = iceRestrictions.reflexive; - } - - if (!isNull(iceRestrictions.relay)) { - turn = iceRestrictions.relay; - } - - if (!iceRestrictions.host && !!candidate.match(/typ host/g)) { - return; - } - - if (!turn && !!candidate.match(/typ relay/g)) { - return; - } - if (!stun && !!candidate.match(/typ srflx/g)) { + ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) { + if (webAudio.isSupported) { return; } - var protocol = connection.iceProtocols; + if (item in window) { + webAudio.isSupported = true; - if (!protocol.udp && !!candidate.match(/ udp /g)) { - return; + if ('createMediaStreamSource' in window[item].prototype) { + webAudio.isCreateMediaStreamSourceSupported = true; + } } + }); + DetectRTC.isAudioContextSupported = webAudio.isSupported; + DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported; - if (!protocol.tcp && !!candidate.match(/ tcp /g)) { - return; - } + // ---------- Detect if SCTP/RTP channels are supported. - if (connection.enableLogs) { - console.debug('Your candidate pairs:', candidate); - } + var isRtpDataChannelsSupported = false; + if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) { + isRtpDataChannelsSupported = true; + } + DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported; - return { - candidate: candidate, - sdpMid: icePair.sdpMid, - sdpMLineIndex: icePair.sdpMLineIndex - }; + var isSCTPSupportd = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) { + isSCTPSupportd = true; + } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) { + isSCTPSupportd = true; + } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) { + isSCTPSupportd = true; } + DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd; - return { - processCandidates: processCandidates - }; - })(); + // --------- - // IceServersHandler.js + DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js" - var iceFrame, loadedIceFrame; + // ------ + var isGetUserMediaSupported = false; + if (navigator.getUserMedia) { + isGetUserMediaSupported = true; + } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + isGetUserMediaSupported = true; + } + if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && location.protocol !== 'https:') { + DetectRTC.isGetUserMediaSupported = 'Requires HTTPs'; + } + DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported; - function loadIceFrame(callback, skip) { - if (loadedIceFrame) return; - if (!skip) return loadIceFrame(callback, true); + // ----------- + DetectRTC.osName = osName; + DetectRTC.osVersion = osVersion; - loadedIceFrame = true; + var displayResolution = ''; + if (screen.width) { + var width = (screen.width) ? screen.width : ''; + var height = (screen.height) ? screen.height : ''; + displayResolution += '' + width + ' x ' + height; + } + DetectRTC.displayResolution = displayResolution; - var iframe = document.createElement('iframe'); - iframe.onload = function() { - iframe.isLoaded = true; + // ---------- + DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing; + DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing; - listenEventHandler('message', iFrameLoaderCallback); + // ------ + DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress; - function iFrameLoaderCallback(event) { - if (!event.data || !event.data.iceServers) return; - callback(event.data.iceServers); - window.removeEventListener('message', iFrameLoaderCallback); + DetectRTC.isWebSocketsSupported = 'WebSocket' in window && 2 === window.WebSocket.CLOSING; + DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported; + + DetectRTC.checkWebSocketsSupport = function(callback) { + callback = callback || function() {}; + try { + var websocket = new WebSocket('wss://echo.websocket.org:443/'); + websocket.onopen = function() { + DetectRTC.isWebSocketsBlocked = false; + callback(); + websocket.close(); + websocket = null; + }; + websocket.onerror = function() { + DetectRTC.isWebSocketsBlocked = true; + callback(); + }; + } catch (e) { + DetectRTC.isWebSocketsBlocked = true; + callback(); } + }; - iframe.contentWindow.postMessage('get-ice-servers', '*'); + // ------- + DetectRTC.load = function(callback) { + callback = callback || function() {}; + checkDeviceSupport(callback); }; - iframe.src = 'https://cdn.webrtc-experiment.com/getIceServers/'; - iframe.style.display = 'none'; - (document.body || document.documentElement).appendChild(iframe); - } - if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { - loadIceFrame(function(externalIceServers) { - if (!externalIceServers || !externalIceServers.length) return; - window.RMCExternalIceServers = externalIceServers; + DetectRTC.MediaDevices = MediaDevices; + DetectRTC.hasMicrophone = hasMicrophone; + DetectRTC.hasSpeakers = hasSpeakers; + DetectRTC.hasWebcam = hasWebcam; - if (window.iceServersLoadCallback && typeof window.iceServersLoadCallback === 'function') { - window.iceServersLoadCallback(externalIceServers); - } - }); - } + DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; + DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; - var IceServersHandler = (function() { - function getIceServers(connection) { - var iceServers = []; + DetectRTC.audioInputDevices = audioInputDevices; + DetectRTC.audioOutputDevices = audioOutputDevices; + DetectRTC.videoInputDevices = videoInputDevices; - iceServers.push({ - urls: 'stun:stun.l.google.com:19302' - }, { - urls: 'stun:mmt-stun.verkstad.net' - }, { - urls: 'stun:stun.anyfirewall.com:3478' - }); + // ------ + var isSetSinkIdSupported = false; + if ('setSinkId' in document.createElement('video')) { + isSetSinkIdSupported = true; + } + DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported; - iceServers.push({ - urls: 'turn:turn.bistri.com:80', - credential: 'homeo', - username: 'homeo' - }); + // ----- + var isRTPSenderReplaceTracksSupported = false; + if (DetectRTC.browser.isFirefox /*&& DetectRTC.browser.version > 39*/ ) { + /*global mozRTCPeerConnection:true */ + if ('getSenders' in mozRTCPeerConnection.prototype) { + isRTPSenderReplaceTracksSupported = true; + } + } else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') { + /*global webkitRTCPeerConnection:true */ + if ('getSenders' in webkitRTCPeerConnection.prototype) { + isRTPSenderReplaceTracksSupported = true; + } + } + DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported; - iceServers.push({ - urls: 'turn:turn.anyfirewall.com:443', - credential: 'webrtc', - username: 'webrtc' - }); + //------ + var isRemoteStreamProcessingSupported = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) { + isRemoteStreamProcessingSupported = true; + } + DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported; - // copyright of mmt-turn.verkstad: Ericsson - iceServers.push({ - urls: 'turn:mmt-turn.verkstad.net', - username: 'webrtc', - credential: 'secret' - }); + //------- + var isApplyConstraintsSupported = false; - if (window.RMCExternalIceServers) { - iceServers = window.RMCExternalIceServers.concat(iceServers); - connection.iceServers = iceServers; - } else if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { - window.iceServersLoadCallback = function() { - iceServers = window.RMCExternalIceServers.concat(iceServers); - connection.iceServers = iceServers; - }; - } + /*global MediaStreamTrack:true */ + if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) { + isApplyConstraintsSupported = true; + } + DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported; - return iceServers; + //------- + var isMultiMonitorScreenCapturingSupported = false; + if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) { + // version 43 merely supports platforms for multi-monitors + // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing. + isMultiMonitorScreenCapturingSupported = true; } + DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported; - return { - getIceServers: getIceServers - }; - })(); + window.DetectRTC = DetectRTC; - // Last time updated at Fri Jan 08 2016 14:06 + })(); - // gumadapter.js - // https://cdn.webrtc-experiment.com/gumadapter.js + // ios-hacks.js - // getUserMedia hacks from git/webrtc/adapter; - // removed redundant codes - // A-to-Zee, all copyrights goes to: - // https://github.com/webrtc/adapter/blob/master/LICENSE.md + function setCordovaAPIs() { + if (DetectRTC.osName !== 'iOS') return; + if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return; - var getUserMedia = null; - var webrtcDetectedBrowser = null; - var webrtcDetectedVersion = null; - var webrtcMinimumVersion = null; + var iosrtc = cordova.plugins.iosrtc; + window.webkitRTCPeerConnection = iosrtc.RTCPeerConnection; + window.RTCSessionDescription = iosrtc.RTCSessionDescription; + window.RTCIceCandidate = iosrtc.RTCIceCandidate; + window.MediaStream = iosrtc.MediaStream; + window.MediaStreamTrack = iosrtc.MediaStreamTrack; + navigator.getUserMedia = navigator.webkitGetUserMedia = iosrtc.getUserMedia; - var webrtcUtils = window.webrtcUtils || {}; - if (!webrtcUtils.enableLogs) { - webrtcUtils.enableLogs = true; + iosrtc.debug.enable('iosrtc*'); + iosrtc.registerGlobals(); } - if (!webrtcUtils.log) { - webrtcUtils.log = function() { - if (!webrtcUtils.enableLogs) { - return; - } - // suppress console.log output when being included as a module. - if (typeof module !== 'undefined' || - typeof require === 'function' && typeof define === 'function') { - return; - } - console.log.apply(console, arguments); + document.addEventListener('deviceready', setCordovaAPIs, false); + setCordovaAPIs(); + + // RTCPeerConnection.js + + var defaults = {}; + + function setSdpConstraints(config) { + var sdpConstraints; + + var sdpConstraints_mandatory = { + OfferToReceiveAudio: !!config.OfferToReceiveAudio, + OfferToReceiveVideo: !!config.OfferToReceiveVideo }; - } - if (!webrtcUtils.extractVersion) { - webrtcUtils.extractVersion = function(uastring, expr, pos) { - var match = uastring.match(expr); - return match && match.length >= pos && parseInt(match[pos], 10); + sdpConstraints = { + mandatory: sdpConstraints_mandatory, + optional: [{ + VoiceActivityDetection: false + }] }; - } - if (typeof window === 'object') { - if (window.HTMLMediaElement && - !('srcObject' in window.HTMLMediaElement.prototype)) { - // Shim the srcObject property, once, when HTMLMediaElement is found. - Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', { - get: function() { - // If prefixed srcObject property exists, return it. - // Otherwise use the shimmed property, _srcObject - return 'mozSrcObject' in this ? this.mozSrcObject : this._srcObject; - }, - set: function(stream) { - if ('mozSrcObject' in this) { - this.mozSrcObject = stream; - } else { - // Use _srcObject as a private property for this shim - this._srcObject = stream; - // TODO: revokeObjectUrl(this.src) when !stream to release resources? - this.src = stream ? URL.createObjectURL(stream) : null; - } - } - }); + if (!!navigator.mozGetUserMedia && firefoxVersion > 34) { + sdpConstraints = { + OfferToReceiveAudio: !!config.OfferToReceiveAudio, + OfferToReceiveVideo: !!config.OfferToReceiveVideo + }; } - // Proxy existing globals - getUserMedia = window.navigator && window.navigator.getUserMedia; + + return sdpConstraints; } - if (typeof window === 'undefined' || !window.navigator) { - webrtcDetectedBrowser = 'not a browser'; - } else if (navigator.mozGetUserMedia && window.mozRTCPeerConnection) { - webrtcDetectedBrowser = 'firefox'; + var RTCPeerConnection; + if (typeof mozRTCPeerConnection !== 'undefined') { + RTCPeerConnection = mozRTCPeerConnection; + } else if (typeof webkitRTCPeerConnection !== 'undefined') { + RTCPeerConnection = webkitRTCPeerConnection; + } else if (typeof window.RTCPeerConnection !== 'undefined') { + RTCPeerConnection = window.RTCPeerConnection; + } else { + console.error('WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.'); + RTCPeerConnection = window.RTCSessionDescription = window.RTCIceCandidate = function() {}; + } - // the detected firefox version. - webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, - /Firefox\/([0-9]+)\./, 1); + var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription; + var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate; + var MediaStreamTrack = window.MediaStreamTrack; - // the minimum firefox version still supported by adapter. - webrtcMinimumVersion = 31; + var Plugin = {}; - // getUserMedia constraints shim. - getUserMedia = function(constraints, onSuccess, onError) { - var constraintsToFF37 = function(c) { - if (typeof c !== 'object' || c.require) { - return c; - } - var require = []; - Object.keys(c).forEach(function(key) { - if (key === 'require' || key === 'advanced' || key === 'mediaSource') { - return; - } - var r = c[key] = (typeof c[key] === 'object') ? - c[key] : { - ideal: c[key] - }; - if (r.min !== undefined || - r.max !== undefined || r.exact !== undefined) { - require.push(key); - } - if (r.exact !== undefined) { - if (typeof r.exact === 'number') { - r.min = r.max = r.exact; - } else { - c[key] = r.exact; - } - delete r.exact; + function onPluginRTCInitialized(pluginRTCObject) { + Plugin = pluginRTCObject; + MediaStreamTrack = Plugin.MediaStreamTrack; + RTCPeerConnection = Plugin.RTCPeerConnection; + RTCIceCandidate = Plugin.RTCIceCandidate; + RTCSessionDescription = Plugin.RTCSessionDescription; + } + if (typeof PluginRTC !== 'undefined') onPluginRTCInitialized(PluginRTC); + + function PeerInitiator(config) { + var connection = config.rtcMultiConnection; + + this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra; + this.remoteUserId = config.remoteUserId; + this.streams = []; + this.channels = []; + this.connectionDescription = config.connectionDescription; + + var that = this; + + if (config.remoteSdp) { + this.connectionDescription = config.remoteSdp.connectionDescription; + } + + var allRemoteStreams = {}; + + if (Object.observe) { + var that = this; + Object.observe(this.channels, function(changes) { + changes.forEach(function(change) { + if (change.type === 'add') { + change.object[change.name].addEventListener('close', function() { + delete that.channels[that.channels.indexOf(change.object[change.name])]; + that.channels = removeNullEntries(that.channels); + }, false); } - if (r.ideal !== undefined) { - c.advanced = c.advanced || []; - var oc = {}; - if (typeof r.ideal === 'number') { - oc[key] = { - min: r.ideal, - max: r.ideal - }; - } else { - oc[key] = r.ideal; - } - c.advanced.push(oc); - delete r.ideal; - if (!Object.keys(r).length) { - delete c[key]; + if (change.type === 'remove' || change.type === 'delete') { + if (that.channels.indexOf(change.object[change.name]) !== -1) { + delete that.channels.indexOf(change.object[change.name]); } } + + that.channels = removeNullEntries(that.channels); }); - if (require.length) { - c.require = require; - } - return c; - }; - if (webrtcDetectedVersion < 38) { - webrtcUtils.log('spec: ' + JSON.stringify(constraints)); - if (constraints.audio) { - constraints.audio = constraintsToFF37(constraints.audio); - } - if (constraints.video) { - constraints.video = constraintsToFF37(constraints.video); - } - webrtcUtils.log('ff37: ' + JSON.stringify(constraints)); - } - return navigator.mozGetUserMedia(constraints, onSuccess, onError); - }; + }); + } - navigator.getUserMedia = getUserMedia; + defaults.sdpConstraints = setSdpConstraints({ + OfferToReceiveAudio: true, + OfferToReceiveVideo: true + }); - // Shim for mediaDevices on older versions. - if (!navigator.mediaDevices) { - navigator.mediaDevices = { - getUserMedia: requestUserMedia, - addEventListener: function() {}, - removeEventListener: function() {} - }; + var peer; + + var renegotiatingPeer = !!config.renegotiatingPeer; + if (config.remoteSdp) { + renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer; } - navigator.mediaDevices.enumerateDevices = - navigator.mediaDevices.enumerateDevices || function() { - return new Promise(function(resolve) { - var infos = [{ - kind: 'audioinput', - deviceId: 'default', - label: '', - groupId: '' - }, { - kind: 'videoinput', - deviceId: 'default', - label: '', - groupId: '' - }]; - resolve(infos); + + var localStreams = []; + connection.attachStreams.forEach(function(stream) { + if (!!stream) localStreams.push(stream); + }); + + if (!renegotiatingPeer) { + peer = new RTCPeerConnection(navigator.onLine ? { + iceServers: connection.iceServers, + iceTransports: 'all' + } : null, connection.optionalArgument); + } else { + peer = config.peerRef; + + peer.getLocalStreams().forEach(function(stream) { + localStreams.forEach(function(localStream, index) { + if (stream == localStream) { + delete localStreams[index]; + } }); - }; - if (webrtcDetectedVersion < 41) { - // Work around http://bugzil.la/1169665 - var orgEnumerateDevices = - navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices); - navigator.mediaDevices.enumerateDevices = function() { - return orgEnumerateDevices().then(undefined, function(e) { - if (e.name === 'NotFoundError') { - return []; + connection.removeStreams.forEach(function(streamToRemove, index) { + if (stream === streamToRemove) { + stream = connection.beforeRemovingStream(stream); + if (stream && !!peer.removeStream) { + peer.removeStream(stream); + } + + localStreams.forEach(function(localStream, index) { + if (streamToRemove == localStream) { + delete localStreams[index]; + } + }); } - throw e; }); - }; + }); } - } else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) { - webrtcDetectedBrowser = 'chrome'; + if (connection.DetectRTC.browser.name === 'Firefox') { + peer.removeStream = function(stream) { + stream.mute(); + connection.StreamsHandler.onSyncNeeded(stream.streamid, 'stream-removed'); + }; + } - webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, - /Chrom(e|ium)\/([0-9]+)\./, 2); + peer.onicecandidate = function(event) { + if (!event.candidate) return; + config.onLocalCandidate({ + candidate: event.candidate.candidate, + sdpMid: event.candidate.sdpMid, + sdpMLineIndex: event.candidate.sdpMLineIndex + }); + }; - // the minimum chrome version still supported by adapter. - webrtcMinimumVersion = 38; + var isFirefoxOffered = !isFirefox; + if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered) { + isFirefoxOffered = true; + } - // getUserMedia constraints shim. - var constraintsToChrome = function(c) { - if (typeof c !== 'object' || c.mandatory || c.optional) { - return c; + localStreams.forEach(function(localStream) { + if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) { + return; } - var cc = {}; - Object.keys(c).forEach(function(key) { - if (key === 'require' || key === 'advanced' || key === 'mediaSource') { - return; - } - var r = (typeof c[key] === 'object') ? c[key] : { - ideal: c[key] - }; - if (r.exact !== undefined && typeof r.exact === 'number') { - r.min = r.max = r.exact; - } - var oldname = function(prefix, name) { - if (prefix) { - return prefix + name.charAt(0).toUpperCase() + name.slice(1); - } - return (name === 'deviceId') ? 'sourceId' : name; - }; - if (r.ideal !== undefined) { - cc.optional = cc.optional || []; - var oc = {}; - if (typeof r.ideal === 'number') { - oc[oldname('min', key)] = r.ideal; - cc.optional.push(oc); - oc = {}; - oc[oldname('max', key)] = r.ideal; - cc.optional.push(oc); - } else { - oc[oldname('', key)] = r.ideal; - cc.optional.push(oc); - } - } - if (r.exact !== undefined && typeof r.exact !== 'number') { - cc.mandatory = cc.mandatory || {}; - cc.mandatory[oldname('', key)] = r.exact; - } else { - ['min', 'max'].forEach(function(mix) { - if (r[mix] !== undefined) { - cc.mandatory = cc.mandatory || {}; - cc.mandatory[oldname(mix, key)] = r[mix]; - } - }); - } - }); - if (c.advanced) { - cc.optional = (cc.optional || []).concat(c.advanced); + + if (config.dontAttachLocalStream) { + return; } - return cc; - }; - getUserMedia = function(constraints, onSuccess, onError) { - if (constraints.audio) { - constraints.audio = constraintsToChrome(constraints.audio); + localStream = connection.beforeAddingStream(localStream); + if (localStream) { + peer.addStream(localStream); } - if (constraints.video) { - constraints.video = constraintsToChrome(constraints.video); + }); + + peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() { + var extra = that.extra; + if (connection.peers[that.remoteUserId]) { + extra = connection.peers[that.remoteUserId].extra || extra; } - webrtcUtils.log('chrome: ' + JSON.stringify(constraints)); - return navigator.webkitGetUserMedia(constraints, onSuccess, onError); - }; - navigator.getUserMedia = getUserMedia; - if (!navigator.mediaDevices) { - navigator.mediaDevices = { - getUserMedia: requestUserMedia - }; - } + if (!peer) { + return; + } - // A shim for getUserMedia method on the mediaDevices object. - // TODO(KaptenJansson) remove once implemented in Chrome stable. - if (!navigator.mediaDevices.getUserMedia) { - navigator.mediaDevices.getUserMedia = function(constraints) { - return requestUserMedia(constraints); - }; - } else { - // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia - // function which returns a Promise, it does not accept spec-style - // constraints. - var origGetUserMedia = navigator.mediaDevices.getUserMedia. - bind(navigator.mediaDevices); - navigator.mediaDevices.getUserMedia = function(c) { - webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment - c.audio = constraintsToChrome(c.audio); - c.video = constraintsToChrome(c.video); - webrtcUtils.log('chrome: ' + JSON.stringify(c)); - return origGetUserMedia(c); - }; - } + config.onPeerStateChanged({ + iceConnectionState: peer.iceConnectionState, + iceGatheringState: peer.iceGatheringState, + signalingState: peer.signalingState, + extra: extra, + userid: that.remoteUserId + }); + }; - // Dummy devicechange event methods. - // TODO(KaptenJansson) remove once implemented in Chrome stable. - if (typeof navigator.mediaDevices.addEventListener === 'undefined') { - navigator.mediaDevices.addEventListener = function() { - webrtcUtils.log('Dummy mediaDevices.addEventListener called.'); - }; - } - if (typeof navigator.mediaDevices.removeEventListener === 'undefined') { - navigator.mediaDevices.removeEventListener = function() { - webrtcUtils.log('Dummy mediaDevices.removeEventListener called.'); - }; - } + var sdpConstraints = { + OfferToReceiveAudio: !!localStreams.length, + OfferToReceiveVideo: !!localStreams.length + }; - } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { - webrtcUtils.log('This appears to be Edge'); - webrtcDetectedBrowser = 'edge'; + if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints; - webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2); + defaults.sdpConstraints = setSdpConstraints(sdpConstraints); - // the minimum version still supported by adapter. - webrtcMinimumVersion = 12; - } else { - webrtcUtils.log('Browser does not appear to be WebRTC-capable'); - } + peer.onaddstream = function(event) { + var streamsToShare = {}; + if (config.remoteSdp && config.remoteSdp.streamsToShare) { + streamsToShare = config.remoteSdp.streamsToShare; + } else if (config.streamsToShare) { + streamsToShare = config.streamsToShare; + } - // Returns the result of getUserMedia as a Promise. - function requestUserMedia(constraints) { - return new Promise(function(resolve, reject) { - getUserMedia(constraints, resolve, reject); - }); - } + var streamToShare = streamsToShare[event.stream.id]; + if (streamToShare) { + event.stream.isAudio = streamToShare.isAudio; + event.stream.isVideo = streamToShare.isVideo; + event.stream.isScreen = streamToShare.isScreen; + } - if (typeof module !== 'undefined') { - module.exports = { - getUserMedia: getUserMedia, - webrtcDetectedBrowser: webrtcDetectedBrowser, - webrtcDetectedVersion: webrtcDetectedVersion, - webrtcMinimumVersion: webrtcMinimumVersion, - webrtcUtils: webrtcUtils + event.stream.streamid = event.stream.id; + if (!event.stream.stop) { + event.stream.stop = function() { + if (isFirefox) { + fireEvent(this, 'ended'); + } + }; + } + allRemoteStreams[event.stream.id] = event.stream; + config.onRemoteStream(event.stream); }; - } else if ((typeof require === 'function') && (typeof define === 'function')) { - // Expose objects and functions when RequireJS is doing the loading. - define([], function() { - return { - getUserMedia: getUserMedia, - webrtcDetectedBrowser: webrtcDetectedBrowser, - webrtcDetectedVersion: webrtcDetectedVersion, - webrtcMinimumVersion: webrtcMinimumVersion, - webrtcUtils: webrtcUtils - }; - }); - } - - // getUserMediaHandler.js - - if (typeof webrtcUtils !== 'undefined') { - webrtcUtils.enableLogs = false; - } - - function setStreamType(constraints, stream) { - if (constraints.mandatory && constraints.mandatory.chromeMediaSource) { - stream.isScreen = true; - } else if (constraints.mozMediaSource || constraints.mediaSource) { - stream.isScreen = true; - } else if (constraints.video) { - stream.isVideo = true; - } else if (constraints.audio) { - stream.isAudio = true; - } - } - var currentUserMediaRequest = { - streams: [], - mutex: false, - queueRequests: [], - remove: function(idInstance) { - this.mutex = false; + peer.onremovestream = function(event) { + event.stream.streamid = event.stream.id; - var stream = this.streams[idInstance]; - if (!stream) { - return; + if (allRemoteStreams[event.stream.id]) { + delete allRemoteStreams[event.stream.id]; } - stream = stream.stream; + config.onRemoteStreamRemoved(event.stream); + }; - var options = stream.currentUserMediaRequestOptions; + this.addRemoteCandidate = function(remoteCandidate) { + peer.addIceCandidate(new RTCIceCandidate(remoteCandidate)); + }; - if (this.queueRequests.indexOf(options)) { - delete this.queueRequests[this.queueRequests.indexOf(options)]; - this.queueRequests = removeNullEntries(this.queueRequests); - } + this.addRemoteSdp = function(remoteSdp) { + remoteSdp.sdp = connection.processSdp(remoteSdp.sdp); + peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), function() {}, function(error) { + if (!!connection.enableLogs) { + console.error(JSON.stringify(error, null, '\t')); + } + }); + }; - this.streams[idInstance].stream = null; - delete this.streams[idInstance]; + var isOfferer = true; + + if (config.remoteSdp) { + isOfferer = false; } - }; - function getUserMediaHandler(options) { - if (currentUserMediaRequest.mutex === true) { - currentUserMediaRequest.queueRequests.push(options); - return; + if (connection.session.data === true) { + createDataChannel(); } - currentUserMediaRequest.mutex = true; - // easy way to match - var idInstance = JSON.stringify(options.localMediaConstraints); + if (config.remoteSdp) { + if (config.remoteSdp.remotePeerSdpConstraints) { + sdpConstraints = config.remoteSdp.remotePeerSdpConstraints; + } + defaults.sdpConstraints = setSdpConstraints(sdpConstraints); + this.addRemoteSdp(config.remoteSdp); + } - function streaming(stream, returnBack) { - setStreamType(options.localMediaConstraints, stream); - options.onGettingLocalMedia(stream, returnBack); + function createDataChannel() { + if (!isOfferer) { + peer.ondatachannel = function(event) { + var channel = event.channel; + setChannelEvents(channel); + }; + return; + } - stream.addEventListener('ended', function() { - delete currentUserMediaRequest.streams[idInstance]; + var channel = peer.createDataChannel('RTCDataChannel', {}); + setChannelEvents(channel); + } - currentUserMediaRequest.mutex = false; - if (currentUserMediaRequest.queueRequests.indexOf(options)) { - delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)]; - currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests); - } - }, false); + function setChannelEvents(channel) { + // force ArrayBuffer in Firefox; which uses "Blob" by default. + channel.binaryType = 'arraybuffer'; - currentUserMediaRequest.streams[idInstance] = { - stream: stream + channel.onmessage = function(event) { + config.onDataChannelMessage(event.data); }; - currentUserMediaRequest.mutex = false; - if (currentUserMediaRequest.queueRequests.length) { - getUserMediaHandler(currentUserMediaRequest.queueRequests.shift()); - } - } + channel.onopen = function() { + config.onDataChannelOpened(channel); + }; - if (currentUserMediaRequest.streams[idInstance]) { - streaming(currentUserMediaRequest.streams[idInstance].stream, true); - } else { - if (isPluginRTC) { - var mediaElement = document.createElement('video'); - Plugin.getUserMedia({ - audio: true, - video: true - }, function(stream) { - stream.streamid = stream.id || getRandomString(); - streaming(stream); - }, function(error) {}); + channel.onerror = function(error) { + config.onDataChannelError(error); + }; - return; - } + channel.onclose = function(event) { + config.onDataChannelClosed(event); + }; - if (typeof DetectRTC !== 'undefined') { - if (!DetectRTC.hasMicrophone) { - options.localMediaConstraints.audio = false; + channel.internalSend = channel.send; + channel.send = function(data) { + if (channel.readyState !== 'open') { + return; } - if (!DetectRTC.hasWebcam) { - options.localMediaConstraints.video = false; - } - } + channel.internalSend(data); + }; - navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream) { - stream.streamid = stream.streamid || stream.id || getRandomString(); - stream.idInstance = idInstance; - streaming(stream); - }).catch(function(error) { - options.onLocalMediaError(error, options.localMediaConstraints); - }); + peer.channel = channel; } - } - // StreamsHandler.js + if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') { + defaults.sdpConstraints = setSdpConstraints({ + OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio), + OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio) + }); + } - var StreamsHandler = (function() { - function handleType(type) { - if (!type) { - return; - } + var streamsToShare = {}; + peer.getLocalStreams().forEach(function(stream) { + streamsToShare[stream.streamid] = { + isAudio: !!stream.isAudio, + isVideo: !!stream.isVideo, + isScreen: !!stream.isScreen + }; + }); - if (typeof type === 'string' || typeof type === 'undefined') { - return type; + peer[isOfferer ? 'createOffer' : 'createAnswer'](function(localSdp) { + localSdp.sdp = connection.processSdp(localSdp.sdp); + peer.setLocalDescription(localSdp); + config.onLocalSdp({ + type: localSdp.type, + sdp: localSdp.sdp, + remotePeerSdpConstraints: config.remotePeerSdpConstraints || false, + renegotiatingPeer: !!config.renegotiatingPeer || false, + connectionDescription: that.connectionDescription, + dontGetRemoteStream: !!config.dontGetRemoteStream, + extra: connection ? connection.extra : {}, + streamsToShare: streamsToShare, + isFirefoxOffered: isFirefox + }); + }, function(error) { + if (!!connection.enableLogs) { + console.error('sdp-error', error); } + }, defaults.sdpConstraints); - if (type.audio && type.video) { - return null; + peer.nativeClose = peer.close; + peer.close = function() { + if (!peer) { + return; } - if (type.audio) { - return 'audio'; - } + try { + if (peer.iceConnectionState.search(/closed|failed/gi) === -1) { + peer.getRemoteStreams().forEach(function(stream) { + stream.stop(); + }); + } + peer.nativeClose(); + } catch (e) {} - if (type.video) { - return 'video'; - } + peer = null; + that.peer = null; + }; - return; - } + this.peer = peer; + } - function setHandlers(stream, syncAction, connection) { - if (typeof syncAction == 'undefined' || syncAction == true) { - stream.addEventListener('ended', function() { - StreamsHandler.onSyncNeeded(this.streamid, 'ended'); - }, false); - } + // CodecsHandler.js - stream.mute = function(type, isSyncAction) { - type = handleType(type); + var CodecsHandler = (function() { + var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); + if (typeof cordova !== 'undefined') { + isMobileDevice = true; + } - if (typeof isSyncAction !== 'undefined') { - syncAction = isSyncAction; - } + if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) { + isMobileDevice = true; + } - if (typeof type == 'undefined' || type == 'audio') { - stream.getAudioTracks().forEach(function(track) { - track.enabled = false; - connection.streamEvents[stream.streamid].isAudioMuted = true; - }); - } + // "removeVPX" and "removeNonG722" methods are taken from github/mozilla/webrtc-landing + function removeVPX(sdp) { + if (!sdp || typeof sdp !== 'string') { + throw 'Invalid arguments.'; + } - if (typeof type == 'undefined' || type == 'video') { - stream.getVideoTracks().forEach(function(track) { - track.enabled = false; - }); - } + // this method is NOT reliable - if (typeof syncAction == 'undefined' || syncAction == true) { - StreamsHandler.onSyncNeeded(stream.streamid, 'mute', type); - } + sdp = sdp.replace('a=rtpmap:100 VP8/90000\r\n', ''); + sdp = sdp.replace('a=rtpmap:101 VP9/90000\r\n', ''); - connection.streamEvents[stream.streamid].muteType = type || 'both'; + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g, 'm=video $1 RTP\/SAVPF $2'); + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g, 'm=video $1 RTP\/SAVPF $2'); - fireEvent(stream, 'mute', type); - }; + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); + sdp = sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g, 'm=video $1 RTP\/SAVPF$2'); - stream.unmute = function(type, isSyncAction) { - type = handleType(type); + sdp = sdp.replace('a=rtcp-fb:120 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:120 nack pli\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:120 ccm fir\r\n', ''); - if (typeof isSyncAction !== 'undefined') { - syncAction = isSyncAction; - } + sdp = sdp.replace('a=rtcp-fb:101 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:101 nack pli\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:101 ccm fir\r\n', ''); - graduallyIncreaseVolume(); + return sdp; + } - if (typeof type == 'undefined' || type == 'audio') { - stream.getAudioTracks().forEach(function(track) { - track.enabled = true; - connection.streamEvents[stream.streamid].isAudioMuted = false; - }); - } + function disableNACK(sdp) { + if (!sdp || typeof sdp !== 'string') { + throw 'Invalid arguments.'; + } - if (typeof type == 'undefined' || type == 'video') { - stream.getVideoTracks().forEach(function(track) { - track.enabled = true; - }); + sdp = sdp.replace('a=rtcp-fb:126 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:126 nack pli\r\n', 'a=rtcp-fb:126 pli\r\n'); + sdp = sdp.replace('a=rtcp-fb:97 nack\r\n', ''); + sdp = sdp.replace('a=rtcp-fb:97 nack pli\r\n', 'a=rtcp-fb:97 pli\r\n'); - // make sure that video unmute doesn't affects audio - if (typeof type !== 'undefined' && type == 'video' && connection.streamEvents[stream.streamid].isAudioMuted) { - (function looper(times) { - if (!times) { - times = 0; - } + return sdp; + } - times++; + function prioritize(codecMimeType, peer) { + if (!peer || !peer.getSenders || !peer.getSenders().length) { + return; + } - // check until five-seconds - if (times < 100 && connection.streamEvents[stream.streamid].isAudioMuted) { - stream.mute('audio'); + if (!codecMimeType || typeof codecMimeType !== 'string') { + throw 'Invalid arguments.'; + } - setTimeout(function() { - looper(times); - }, 50); - } - })(); + peer.getSenders().forEach(function(sender) { + var params = sender.getParameters(); + for (var i = 0; i < params.codecs.length; i++) { + if (params.codecs[i].mimeType == codecMimeType) { + params.codecs.unshift(params.codecs.splice(i, 1)); + break; } } + sender.setParameters(params); + }); + } - if (typeof syncAction == 'undefined' || syncAction == true) { - StreamsHandler.onSyncNeeded(stream.streamid, 'unmute', type); - } + function removeNonG722(sdp) { + return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, 'm=audio $1 RTP\/SAVPF 9'); + } - connection.streamEvents[stream.streamid].unmuteType = type || 'both'; + function setBAS(sdp, bandwidth, isScreen) { + if (!bandwidth) { + return sdp; + } - fireEvent(stream, 'unmute', type); - }; + if (typeof isFirefox !== 'undefined' && isFirefox) { + return sdp; + } - function graduallyIncreaseVolume() { - if (!connection.streamEvents[stream.streamid].mediaElement) { - return; + if (isMobileDevice) { + return sdp; + } + + if (isScreen) { + if (!bandwidth.screen) { + console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.'); + } else if (bandwidth.screen < 300) { + console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.'); } + } - var mediaElement = connection.streamEvents[stream.streamid].mediaElement; - mediaElement.volume = 0; - afterEach(200, 5, function() { - mediaElement.volume += .20; - }); + // if screen; must use at least 300kbs + if (bandwidth.screen && isScreen) { + sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n'); } - } - function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) { - startedTimes = (startedTimes || 0) + 1; - if (startedTimes >= numberOfTimes) return; + // remove existing bandwidth lines + if (bandwidth.audio || bandwidth.video || bandwidth.data) { + sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, ''); + } - setTimeout(function() { - callback(); - afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes); - }, setTimeoutInteval); - } + if (bandwidth.audio) { + sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n'); + } - return { - setHandlers: setHandlers, - onSyncNeeded: function(streamid, action, type) {} - }; - })(); + if (bandwidth.video) { + sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + (isScreen ? bandwidth.screen : bandwidth.video) + '\r\n'); + } - // Last time updated at Monday, January 4th, 2016, 1:17:50 PM + return sdp; + } - // Latest file can be found here: https://cdn.webrtc-experiment.com/DetectRTC.js + // Find the line in sdpLines that starts with |prefix|, and, if specified, + // contains |substr| (case-insensitive search). + function findLine(sdpLines, prefix, substr) { + return findLineInRange(sdpLines, 0, -1, prefix, substr); + } - // Muaz Khan - www.MuazKhan.com - // MIT License - www.WebRTC-Experiment.com/licence - // Documentation - github.com/muaz-khan/DetectRTC - // ____________ - // DetectRTC.js - - // DetectRTC.hasWebcam (has webcam device!) - // DetectRTC.hasMicrophone (has microphone device!) - // DetectRTC.hasSpeakers (has speakers!) - - (function() { - - 'use strict'; - - var navigator = window.navigator; - - if (typeof navigator !== 'undefined') { - if (typeof navigator.webkitGetUserMedia !== 'undefined') { - navigator.getUserMedia = navigator.webkitGetUserMedia; - } - - if (typeof navigator.mozGetUserMedia !== 'undefined') { - navigator.getUserMedia = navigator.mozGetUserMedia; + // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix| + // and, if specified, contains |substr| (case-insensitive search). + function findLineInRange(sdpLines, startLine, endLine, prefix, substr) { + var realEndLine = endLine !== -1 ? endLine : sdpLines.length; + for (var i = startLine; i < realEndLine; ++i) { + if (sdpLines[i].indexOf(prefix) === 0) { + if (!substr || + sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) { + return i; + } + } } - } else { - navigator = { - getUserMedia: function() {}, - userAgent: 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45' - }; + return null; } - var isMobileDevice = !!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i); - var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob); + // Gets the codec payload type from an a=rtpmap:X line. + function getCodecPayloadType(sdpLine) { + var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+'); + var result = sdpLine.match(pattern); + return (result && result.length === 2) ? result[1] : null; + } - var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; - var isFirefox = typeof window.InstallTrigger !== 'undefined'; - var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; - var isChrome = !!window.chrome && !isOpera; - var isIE = !!document.documentMode && !isEdge; + function setVideoBitrates(sdp, params) { + if (isMobileDevice) { + return sdp; + } - // this one can also be used: - // https://www.websocket.org/js/stuff.js (DetectBrowser.js) + params = params || {}; + var xgoogle_min_bitrate = params.min; + var xgoogle_max_bitrate = params.max; - function getBrowserInfo() { - var nVer = navigator.appVersion; - var nAgt = navigator.userAgent; - var browserName = navigator.appName; - var fullVersion = '' + parseFloat(navigator.appVersion); - var majorVersion = parseInt(navigator.appVersion, 10); - var nameOffset, verOffset, ix; + var sdpLines = sdp.split('\r\n'); - // In Opera, the true version is after 'Opera' or after 'Version' - if (isOpera) { - browserName = 'Opera'; - try { - fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0]; - majorVersion = fullVersion.split('.')[0]; - } catch (e) { - fullVersion = '0.0.0.0'; - majorVersion = 0; - } + // VP8 + var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000'); + var vp8Payload; + if (vp8Index) { + vp8Payload = getCodecPayloadType(sdpLines[vp8Index]); } - // In MSIE, the true version is after 'MSIE' in userAgent - else if (isIE) { - verOffset = nAgt.indexOf('MSIE'); - browserName = 'IE'; - fullVersion = nAgt.substring(verOffset + 5); + + if (!vp8Payload) { + return sdp; } - // In Chrome, the true version is after 'Chrome' - else if (isChrome) { - verOffset = nAgt.indexOf('Chrome'); - browserName = 'Chrome'; - fullVersion = nAgt.substring(verOffset + 7); + + var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000'); + var rtxPayload; + if (rtxIndex) { + rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]); } - // In Safari, the true version is after 'Safari' or after 'Version' - else if (isSafari) { - verOffset = nAgt.indexOf('Safari'); - browserName = 'Safari'; - fullVersion = nAgt.substring(verOffset + 7); - if ((verOffset = nAgt.indexOf('Version')) !== -1) { - fullVersion = nAgt.substring(verOffset + 8); - } + if (!rtxIndex) { + return sdp; } - // In Firefox, the true version is after 'Firefox' - else if (isFirefox) { - verOffset = nAgt.indexOf('Firefox'); - browserName = 'Firefox'; - fullVersion = nAgt.substring(verOffset + 8); + + var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString()); + if (rtxFmtpLineIndex !== null) { + var appendrtxNext = '\r\n'; + appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228'); + sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext); + sdp = sdpLines.join('\r\n'); } - // In most other browsers, 'name/version' is at the end of userAgent - else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) { - browserName = nAgt.substring(nameOffset, verOffset); - fullVersion = nAgt.substring(verOffset + 1); + return sdp; + } - if (browserName.toLowerCase() === browserName.toUpperCase()) { - browserName = navigator.appName; - } + function setOpusAttributes(sdp, params) { + if (isMobileDevice) { + return sdp; } - if (isEdge) { - browserName = 'Edge'; - // fullVersion = navigator.userAgent.split('Edge/')[1]; - fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString(); + params = params || {}; + + var sdpLines = sdp.split('\r\n'); + + // Opus + var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000'); + var opusPayload; + if (opusIndex) { + opusPayload = getCodecPayloadType(sdpLines[opusIndex]); } - // trim the fullVersion string at semicolon/space if present - if ((ix = fullVersion.indexOf(';')) !== -1) { - fullVersion = fullVersion.substring(0, ix); + if (!opusPayload) { + return sdp; } - if ((ix = fullVersion.indexOf(' ')) !== -1) { - fullVersion = fullVersion.substring(0, ix); + var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString()); + if (opusFmtpLineIndex === null) { + return sdp; } - majorVersion = parseInt('' + fullVersion, 10); + var appendOpusNext = ''; + appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1'); + appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1'); - if (isNaN(majorVersion)) { - fullVersion = '' + parseFloat(navigator.appVersion); - majorVersion = parseInt(navigator.appVersion, 10); + if (typeof params.maxaveragebitrate != 'undefined') { + appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8); } - return { - fullVersion: fullVersion, - version: majorVersion, - name: browserName, - isPrivateBrowsing: false - }; - } - - // via: https://gist.github.com/cou929/7973956 + if (typeof params.maxplaybackrate != 'undefined') { + appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8); + } - function retry(isDone, next) { - var currentTrial = 0, - maxRetry = 50, - interval = 10, - isTimeout = false; - var id = window.setInterval( - function() { - if (isDone()) { - window.clearInterval(id); - next(isTimeout); - } - if (currentTrial++ > maxRetry) { - window.clearInterval(id); - isTimeout = true; - next(isTimeout); - } - }, - 10 - ); - } + if (typeof params.cbr != 'undefined') { + appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1'); + } - function isIE10OrLater(userAgent) { - var ua = userAgent.toLowerCase(); - if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) { - return false; + if (typeof params.useinbandfec != 'undefined') { + appendOpusNext += '; useinbandfec=' + params.useinbandfec; } - var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua); - if (match && parseInt(match[1], 10) >= 10) { - return true; + + if (typeof params.usedtx != 'undefined') { + appendOpusNext += '; usedtx=' + params.usedtx; } - return false; - } - function detectPrivateMode(callback) { - var isPrivate; + if (typeof params.maxptime != 'undefined') { + appendOpusNext += '\r\na=maxptime:' + params.maxptime; + } - if (window.webkitRequestFileSystem) { - window.webkitRequestFileSystem( - window.TEMPORARY, 1, - function() { - isPrivate = false; - }, - function(e) { - console.log(e); - isPrivate = true; - } - ); - } else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) { - var db; - try { - db = window.indexedDB.open('test'); - } catch (e) { - isPrivate = true; - } + sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext); - if (typeof isPrivate === 'undefined') { - retry( - function isDone() { - return db.readyState === 'done' ? true : false; - }, - function next(isTimeout) { - if (!isTimeout) { - isPrivate = db.result ? false : true; - } - } - ); - } - } else if (isIE10OrLater(window.navigator.userAgent)) { - isPrivate = false; - try { - if (!window.indexedDB) { - isPrivate = true; - } - } catch (e) { - isPrivate = true; - } - } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) { - try { - window.localStorage.setItem('test', 1); - } catch (e) { - isPrivate = true; - } + sdp = sdpLines.join('\r\n'); + return sdp; + } - if (typeof isPrivate === 'undefined') { - isPrivate = false; - window.localStorage.removeItem('test'); - } + function preferVP9(sdp) { + if (sdp.indexOf('SAVPF 100 101') === -1 || sdp.indexOf('VP9/90000') === -1) { + return sdp; } - retry( - function isDone() { - return typeof isPrivate !== 'undefined' ? true : false; - }, - function next(isTimeout) { - callback(isPrivate); - } - ); + return sdp.replace('SAVPF 100 101', 'SAVPF 101 100'); } - var isMobile = { - Android: function() { - return navigator.userAgent.match(/Android/i); - }, - BlackBerry: function() { - return navigator.userAgent.match(/BlackBerry/i); - }, - iOS: function() { - return navigator.userAgent.match(/iPhone|iPad|iPod/i); - }, - Opera: function() { - return navigator.userAgent.match(/Opera Mini/i); + return { + removeVPX: removeVPX, + disableNACK: disableNACK, + prioritize: prioritize, + removeNonG722: removeNonG722, + setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) { + return setBAS(sdp, bandwidth, isScreen); }, - Windows: function() { - return navigator.userAgent.match(/IEMobile/i); + setVideoBitrates: function(sdp, params) { + return setVideoBitrates(sdp, params); }, - any: function() { - return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); + setOpusAttributes: function(sdp, params) { + return setOpusAttributes(sdp, params); }, - getOsName: function() { - var osName = 'Unknown OS'; - if (isMobile.Android()) { - osName = 'Android'; - } - - if (isMobile.BlackBerry()) { - osName = 'BlackBerry'; - } - - if (isMobile.iOS()) { - osName = 'iOS'; - } - - if (isMobile.Opera()) { - osName = 'Opera Mini'; - } - - if (isMobile.Windows()) { - osName = 'Windows'; - } - - return osName; - } + preferVP9: preferVP9 }; + })(); - // via: http://jsfiddle.net/ChristianL/AVyND/ - function detectDesktopOS() { - var unknown = '-'; + // backward compatibility + window.BandwidthHandler = CodecsHandler; - var nVer = navigator.appVersion; - var nAgt = navigator.userAgent; + // OnIceCandidateHandler.js - var os = unknown; - var clientStrings = [{ - s: 'Windows 10', - r: /(Windows 10.0|Windows NT 10.0)/ - }, { - s: 'Windows 8.1', - r: /(Windows 8.1|Windows NT 6.3)/ - }, { - s: 'Windows 8', - r: /(Windows 8|Windows NT 6.2)/ - }, { - s: 'Windows 7', - r: /(Windows 7|Windows NT 6.1)/ - }, { - s: 'Windows Vista', - r: /Windows NT 6.0/ - }, { - s: 'Windows Server 2003', - r: /Windows NT 5.2/ - }, { - s: 'Windows XP', - r: /(Windows NT 5.1|Windows XP)/ - }, { - s: 'Windows 2000', - r: /(Windows NT 5.0|Windows 2000)/ - }, { - s: 'Windows ME', - r: /(Win 9x 4.90|Windows ME)/ - }, { - s: 'Windows 98', - r: /(Windows 98|Win98)/ - }, { - s: 'Windows 95', - r: /(Windows 95|Win95|Windows_95)/ - }, { - s: 'Windows NT 4.0', - r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/ - }, { - s: 'Windows CE', - r: /Windows CE/ - }, { - s: 'Windows 3.11', - r: /Win16/ - }, { - s: 'Android', - r: /Android/ - }, { - s: 'Open BSD', - r: /OpenBSD/ - }, { - s: 'Sun OS', - r: /SunOS/ - }, { - s: 'Linux', - r: /(Linux|X11)/ - }, { - s: 'iOS', - r: /(iPhone|iPad|iPod)/ - }, { - s: 'Mac OS X', - r: /Mac OS X/ - }, { - s: 'Mac OS', - r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/ - }, { - s: 'QNX', - r: /QNX/ - }, { - s: 'UNIX', - r: /UNIX/ - }, { - s: 'BeOS', - r: /BeOS/ - }, { - s: 'OS/2', - r: /OS\/2/ - }, { - s: 'Search Bot', - r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/ - }]; - for (var id in clientStrings) { - var cs = clientStrings[id]; - if (cs.r.test(nAgt)) { - os = cs.s; - break; - } + var OnIceCandidateHandler = (function() { + function processCandidates(connection, icePair) { + var candidate = icePair.candidate; + + var iceRestrictions = connection.candidates; + var stun = iceRestrictions.stun; + var turn = iceRestrictions.turn; + + if (!isNull(iceRestrictions.reflexive)) { + stun = iceRestrictions.reflexive; } - var osVersion = unknown; + if (!isNull(iceRestrictions.relay)) { + turn = iceRestrictions.relay; + } - if (/Windows/.test(os)) { - osVersion = /Windows (.*)/.exec(os)[1]; - os = 'Windows'; + if (!iceRestrictions.host && !!candidate.match(/typ host/g)) { + return; } - switch (os) { - case 'Mac OS X': - osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]; - break; + if (!turn && !!candidate.match(/typ relay/g)) { + return; + } - case 'Android': - osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1]; - break; + if (!stun && !!candidate.match(/typ srflx/g)) { + return; + } - case 'iOS': - osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer); - osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0); - break; + var protocol = connection.iceProtocols; + + if (!protocol.udp && !!candidate.match(/ udp /g)) { + return; + } + + if (!protocol.tcp && !!candidate.match(/ tcp /g)) { + return; + } + + if (connection.enableLogs) { + console.debug('Your candidate pairs:', candidate); } return { - osName: os, - osVersion: osVersion + candidate: candidate, + sdpMid: icePair.sdpMid, + sdpMLineIndex: icePair.sdpMLineIndex }; } - var osName = 'Unknown OS'; - var osVersion = 'Unknown OS Version'; + return { + processCandidates: processCandidates + }; + })(); - if (isMobile.any()) { - osName = isMobile.getOsName(); - } else { - var osInfo = detectDesktopOS(); - osName = osInfo.osName; - osVersion = osInfo.osVersion; - } + // IceServersHandler.js - var isCanvasSupportsStreamCapturing = false; - var isVideoSupportsStreamCapturing = false; - ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) { - if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) { - isCanvasSupportsStreamCapturing = true; - } + var iceFrame, loadedIceFrame; - if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) { - isVideoSupportsStreamCapturing = true; - } - }); + function loadIceFrame(callback, skip) { + if (loadedIceFrame) return; + if (!skip) return loadIceFrame(callback, true); - // via: https://github.com/diafygi/webrtc-ips - function DetectLocalIPAddress(callback) { - if (!DetectRTC.isWebRTCSupported) { - return; + loadedIceFrame = true; + + var iframe = document.createElement('iframe'); + iframe.onload = function() { + iframe.isLoaded = true; + + listenEventHandler('message', iFrameLoaderCallback); + + function iFrameLoaderCallback(event) { + if (!event.data || !event.data.iceServers) return; + callback(event.data.iceServers); + window.removeEventListener('message', iFrameLoaderCallback); } - if (DetectRTC.isORTCSupported) { - return; + iframe.contentWindow.postMessage('get-ice-servers', '*'); + }; + iframe.src = 'https://cdn.webrtc-experiment.com/getIceServers/'; + iframe.style.display = 'none'; + (document.body || document.documentElement).appendChild(iframe); + } + + if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { + loadIceFrame(function(externalIceServers) { + if (!externalIceServers || !externalIceServers.length) return; + window.RMCExternalIceServers = externalIceServers; + + if (window.iceServersLoadCallback && typeof window.iceServersLoadCallback === 'function') { + window.iceServersLoadCallback(externalIceServers); } + }); + } - getIPs(function(ip) { - //local IPs - if (ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)) { - callback('Local: ' + ip); - } + var IceServersHandler = (function() { + function getIceServers(connection) { + var iceServers = []; - //assume the rest are public IPs - else { - callback('Public: ' + ip); - } + iceServers.push({ + urls: 'stun:stun.l.google.com:19302' + }, { + urls: 'stun:mmt-stun.verkstad.net' + }, { + urls: 'stun:stun.anyfirewall.com:3478' }); - } - //get the IP addresses associated with an account - function getIPs(callback) { - var ipDuplicates = {}; + iceServers.push({ + urls: 'turn:turn.bistri.com:80', + credential: 'homeo', + username: 'homeo' + }); - //compatibility for firefox and chrome - var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; - var useWebKit = !!window.webkitRTCPeerConnection; + iceServers.push({ + urls: 'turn:turn.anyfirewall.com:443', + credential: 'webrtc', + username: 'webrtc' + }); - // bypass naive webrtc blocking using an iframe - if (!RTCPeerConnection) { - var iframe = document.getElementById('iframe'); - if (!iframe) { - // - throw 'NOTE: you need to have an iframe in the page right above the script tag.'; - } - var win = iframe.contentWindow; - RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection; - useWebKit = !!win.webkitRTCPeerConnection; - } + // copyright of mmt-turn.verkstad: Ericsson + iceServers.push({ + urls: 'turn:mmt-turn.verkstad.net', + username: 'webrtc', + credential: 'secret' + }); - // if still no RTCPeerConnection then it is not supported by the browser so just return - if (!RTCPeerConnection) { - return; + if (window.RMCExternalIceServers) { + iceServers = window.RMCExternalIceServers.concat(iceServers); + connection.iceServers = iceServers; + } else if (typeof window.getExternalIceServers !== 'undefined' && window.getExternalIceServers == true) { + window.iceServersLoadCallback = function() { + iceServers = window.RMCExternalIceServers.concat(iceServers); + connection.iceServers = iceServers; + }; } - //minimal requirements for data connection - var mediaConstraints = { - optional: [{ - RtpDataChannels: true - }] - }; + return iceServers; + } - //firefox already has a default stun server in about:config - // media.peerconnection.default_iceservers = - // [{"url": "stun:stun.services.mozilla.com"}] - var servers; + return { + getIceServers: getIceServers + }; + })(); - //add same stun server for chrome - if (useWebKit) { - servers = { - iceServers: [{ - urls: 'stun:stun.services.mozilla.com' - }] - }; + // Last time updated at Fri Jan 08 2016 14:06 - if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isFirefox && DetectRTC.browser.version <= 38) { - servers[0] = { - url: servers[0].urls - }; - } + // gumadapter.js + // https://cdn.webrtc-experiment.com/gumadapter.js + + // getUserMedia hacks from git/webrtc/adapter; + // removed redundant codes + // A-to-Zee, all copyrights goes to: + // https://github.com/webrtc/adapter/blob/master/LICENSE.md + + var getUserMedia = null; + var webrtcDetectedBrowser = null; + var webrtcDetectedVersion = null; + var webrtcMinimumVersion = null; + + var webrtcUtils = window.webrtcUtils || {}; + if (!webrtcUtils.enableLogs) { + webrtcUtils.enableLogs = true; + } + if (!webrtcUtils.log) { + webrtcUtils.log = function() { + if (!webrtcUtils.enableLogs) { + return; } - //construct a new RTCPeerConnection - var pc = new RTCPeerConnection(servers, mediaConstraints); + // suppress console.log output when being included as a module. + if (typeof module !== 'undefined' || + typeof require === 'function' && typeof define === 'function') { + return; + } + console.log.apply(console, arguments); + }; + } - function handleCandidate(candidate) { - //match just the IP address - var ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3})/; - var match = ipRegex.exec(candidate); - if (!match) { - console.warn('Could not match IP address in', candidate); - return; + if (!webrtcUtils.extractVersion) { + webrtcUtils.extractVersion = function(uastring, expr, pos) { + var match = uastring.match(expr); + return match && match.length >= pos && parseInt(match[pos], 10); + }; + } + + if (typeof window === 'object') { + if (window.HTMLMediaElement && + !('srcObject' in window.HTMLMediaElement.prototype)) { + // Shim the srcObject property, once, when HTMLMediaElement is found. + Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', { + get: function() { + // If prefixed srcObject property exists, return it. + // Otherwise use the shimmed property, _srcObject + return 'mozSrcObject' in this ? this.mozSrcObject : this._srcObject; + }, + set: function(stream) { + if ('mozSrcObject' in this) { + this.mozSrcObject = stream; + } else { + // Use _srcObject as a private property for this shim + this._srcObject = stream; + // TODO: revokeObjectUrl(this.src) when !stream to release resources? + this.src = stream ? URL.createObjectURL(stream) : null; + } } - var ipAddress = match[1]; + }); + } + // Proxy existing globals + getUserMedia = window.navigator && window.navigator.getUserMedia; + } - //remove duplicates - if (ipDuplicates[ipAddress] === undefined) { - callback(ipAddress); - } + if (typeof window === 'undefined' || !window.navigator) { + webrtcDetectedBrowser = 'not a browser'; + } else if (navigator.mozGetUserMedia && window.mozRTCPeerConnection) { + webrtcDetectedBrowser = 'firefox'; - ipDuplicates[ipAddress] = true; - } + // the detected firefox version. + webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, + /Firefox\/([0-9]+)\./, 1); - //listen for candidate events - pc.onicecandidate = function(ice) { - //skip non-candidate events - if (ice.candidate) { - handleCandidate(ice.candidate.candidate); + // the minimum firefox version still supported by adapter. + webrtcMinimumVersion = 31; + + // getUserMedia constraints shim. + getUserMedia = function(constraints, onSuccess, onError) { + var constraintsToFF37 = function(c) { + if (typeof c !== 'object' || c.require) { + return c; + } + var require = []; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = c[key] = (typeof c[key] === 'object') ? + c[key] : { + ideal: c[key] + }; + if (r.min !== undefined || + r.max !== undefined || r.exact !== undefined) { + require.push(key); + } + if (r.exact !== undefined) { + if (typeof r.exact === 'number') { + r.min = r.max = r.exact; + } else { + c[key] = r.exact; + } + delete r.exact; + } + if (r.ideal !== undefined) { + c.advanced = c.advanced || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[key] = { + min: r.ideal, + max: r.ideal + }; + } else { + oc[key] = r.ideal; + } + c.advanced.push(oc); + delete r.ideal; + if (!Object.keys(r).length) { + delete c[key]; + } + } + }); + if (require.length) { + c.require = require; } + return c; }; + if (webrtcDetectedVersion < 38) { + webrtcUtils.log('spec: ' + JSON.stringify(constraints)); + if (constraints.audio) { + constraints.audio = constraintsToFF37(constraints.audio); + } + if (constraints.video) { + constraints.video = constraintsToFF37(constraints.video); + } + webrtcUtils.log('ff37: ' + JSON.stringify(constraints)); + } + return navigator.mozGetUserMedia(constraints, onSuccess, onError); + }; - //create a bogus data channel - pc.createDataChannel(''); - - //create an offer sdp - pc.createOffer(function(result) { - - //trigger the stun server request - pc.setLocalDescription(result, function() {}, function() {}); - - }, function() {}); + navigator.getUserMedia = getUserMedia; - //wait for a while to let everything done - setTimeout(function() { - //read candidate info from local description - var lines = pc.localDescription.sdp.split('\n'); + // Shim for mediaDevices on older versions. + if (!navigator.mediaDevices) { + navigator.mediaDevices = { + getUserMedia: requestUserMedia, + addEventListener: function() {}, + removeEventListener: function() {} + }; + } + navigator.mediaDevices.enumerateDevices = + navigator.mediaDevices.enumerateDevices || function() { + return new Promise(function(resolve) { + var infos = [{ + kind: 'audioinput', + deviceId: 'default', + label: '', + groupId: '' + }, { + kind: 'videoinput', + deviceId: 'default', + label: '', + groupId: '' + }]; + resolve(infos); + }); + }; - lines.forEach(function(line) { - if (line.indexOf('a=candidate:') === 0) { - handleCandidate(line); + if (webrtcDetectedVersion < 41) { + // Work around http://bugzil.la/1169665 + var orgEnumerateDevices = + navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices); + navigator.mediaDevices.enumerateDevices = function() { + return orgEnumerateDevices().then(undefined, function(e) { + if (e.name === 'NotFoundError') { + return []; } + throw e; }); - }, 1000); - } - - var MediaDevices = []; - - var audioInputDevices = []; - var audioOutputDevices = []; - var videoInputDevices = []; - - if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) { - // Firefox 38+ seems having support of enumerateDevices - // Thanks @xdumaine/enumerateDevices - navigator.enumerateDevices = function(callback) { - navigator.mediaDevices.enumerateDevices().then(callback); }; } - // ---------- Media Devices detection - var canEnumerate = false; - - /*global MediaStreamTrack:true */ - if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) { - canEnumerate = true; - } else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) { - canEnumerate = true; - } + } else if (navigator.webkitGetUserMedia && window.webkitRTCPeerConnection) { + webrtcDetectedBrowser = 'chrome'; - var hasMicrophone = false; - var hasSpeakers = false; - var hasWebcam = false; + webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, + /Chrom(e|ium)\/([0-9]+)\./, 2); - var isWebsiteHasMicrophonePermissions = false; - var isWebsiteHasWebcamPermissions = false; + // the minimum chrome version still supported by adapter. + webrtcMinimumVersion = 38; - // http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices - // todo: switch to enumerateDevices when landed in canary. - function checkDeviceSupport(callback) { - if (!canEnumerate) { - return; + // getUserMedia constraints shim. + var constraintsToChrome = function(c) { + if (typeof c !== 'object' || c.mandatory || c.optional) { + return c; } - - // This method is useful only for Chrome! - - if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) { - navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack); + var cc = {}; + Object.keys(c).forEach(function(key) { + if (key === 'require' || key === 'advanced' || key === 'mediaSource') { + return; + } + var r = (typeof c[key] === 'object') ? c[key] : { + ideal: c[key] + }; + if (r.exact !== undefined && typeof r.exact === 'number') { + r.min = r.max = r.exact; + } + var oldname = function(prefix, name) { + if (prefix) { + return prefix + name.charAt(0).toUpperCase() + name.slice(1); + } + return (name === 'deviceId') ? 'sourceId' : name; + }; + if (r.ideal !== undefined) { + cc.optional = cc.optional || []; + var oc = {}; + if (typeof r.ideal === 'number') { + oc[oldname('min', key)] = r.ideal; + cc.optional.push(oc); + oc = {}; + oc[oldname('max', key)] = r.ideal; + cc.optional.push(oc); + } else { + oc[oldname('', key)] = r.ideal; + cc.optional.push(oc); + } + } + if (r.exact !== undefined && typeof r.exact !== 'number') { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname('', key)] = r.exact; + } else { + ['min', 'max'].forEach(function(mix) { + if (r[mix] !== undefined) { + cc.mandatory = cc.mandatory || {}; + cc.mandatory[oldname(mix, key)] = r[mix]; + } + }); + } + }); + if (c.advanced) { + cc.optional = (cc.optional || []).concat(c.advanced); } + return cc; + }; - if (!navigator.enumerateDevices && navigator.enumerateDevices) { - navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator); + getUserMedia = function(constraints, onSuccess, onError) { + if (constraints.audio) { + constraints.audio = constraintsToChrome(constraints.audio); } - - if (!navigator.enumerateDevices) { - if (callback) { - callback(); - } - return; + if (constraints.video) { + constraints.video = constraintsToChrome(constraints.video); } + webrtcUtils.log('chrome: ' + JSON.stringify(constraints)); + return navigator.webkitGetUserMedia(constraints, onSuccess, onError); + }; + navigator.getUserMedia = getUserMedia; - MediaDevices = []; - - audioInputDevices = []; - audioOutputDevices = []; - videoInputDevices = []; + if (!navigator.mediaDevices) { + navigator.mediaDevices = { + getUserMedia: requestUserMedia + }; + } - navigator.enumerateDevices(function(devices) { - devices.forEach(function(_device) { - var device = {}; - for (var d in _device) { - device[d] = _device[d]; - } + // A shim for getUserMedia method on the mediaDevices object. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (!navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia = function(constraints) { + return requestUserMedia(constraints); + }; + } else { + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia + // function which returns a Promise, it does not accept spec-style + // constraints. + var origGetUserMedia = navigator.mediaDevices.getUserMedia. + bind(navigator.mediaDevices); + navigator.mediaDevices.getUserMedia = function(c) { + webrtcUtils.log('spec: ' + JSON.stringify(c)); // whitespace for alignment + c.audio = constraintsToChrome(c.audio); + c.video = constraintsToChrome(c.video); + webrtcUtils.log('chrome: ' + JSON.stringify(c)); + return origGetUserMedia(c); + }; + } - // if it is MediaStreamTrack.getSources - if (device.kind === 'audio') { - device.kind = 'audioinput'; - } + // Dummy devicechange event methods. + // TODO(KaptenJansson) remove once implemented in Chrome stable. + if (typeof navigator.mediaDevices.addEventListener === 'undefined') { + navigator.mediaDevices.addEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.addEventListener called.'); + }; + } + if (typeof navigator.mediaDevices.removeEventListener === 'undefined') { + navigator.mediaDevices.removeEventListener = function() { + webrtcUtils.log('Dummy mediaDevices.removeEventListener called.'); + }; + } - if (device.kind === 'video') { - device.kind = 'videoinput'; - } + } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { + webrtcUtils.log('This appears to be Edge'); + webrtcDetectedBrowser = 'edge'; - var skip; - MediaDevices.forEach(function(d) { - if (d.id === device.id && d.kind === device.kind) { - skip = true; - } - }); + webrtcDetectedVersion = webrtcUtils.extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2); - if (skip) { - return; - } + // the minimum version still supported by adapter. + webrtcMinimumVersion = 12; + } else { + webrtcUtils.log('Browser does not appear to be WebRTC-capable'); + } - if (!device.deviceId) { - device.deviceId = device.id; - } + // Returns the result of getUserMedia as a Promise. + function requestUserMedia(constraints) { + return new Promise(function(resolve, reject) { + getUserMedia(constraints, resolve, reject); + }); + } - if (!device.id) { - device.id = device.deviceId; - } + if (typeof module !== 'undefined') { + module.exports = { + getUserMedia: getUserMedia, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcUtils: webrtcUtils + }; + } else if ((typeof require === 'function') && (typeof define === 'function')) { + // Expose objects and functions when RequireJS is doing the loading. + define([], function() { + return { + getUserMedia: getUserMedia, + webrtcDetectedBrowser: webrtcDetectedBrowser, + webrtcDetectedVersion: webrtcDetectedVersion, + webrtcMinimumVersion: webrtcMinimumVersion, + webrtcUtils: webrtcUtils + }; + }); + } - if (!device.label) { - device.label = 'Please invoke getUserMedia once.'; - if (location.protocol !== 'https:') { - device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.'; - } - } else { - if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) { - isWebsiteHasWebcamPermissions = true; - } + // getUserMediaHandler.js - if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) { - isWebsiteHasMicrophonePermissions = true; - } - } + if (typeof webrtcUtils !== 'undefined') { + webrtcUtils.enableLogs = false; + } - if (device.kind === 'audioinput') { - hasMicrophone = true; + function setStreamType(constraints, stream) { + if (constraints.mandatory && constraints.mandatory.chromeMediaSource) { + stream.isScreen = true; + } else if (constraints.mozMediaSource || constraints.mediaSource) { + stream.isScreen = true; + } else if (constraints.video) { + stream.isVideo = true; + } else if (constraints.audio) { + stream.isAudio = true; + } + } - if (audioInputDevices.indexOf(device) === -1) { - audioInputDevices.push(device); - } - } + var currentUserMediaRequest = { + streams: [], + mutex: false, + queueRequests: [], + remove: function(idInstance) { + this.mutex = false; - if (device.kind === 'audiooutput') { - hasSpeakers = true; + var stream = this.streams[idInstance]; + if (!stream) { + return; + } - if (audioOutputDevices.indexOf(device) === -1) { - audioOutputDevices.push(device); - } - } + stream = stream.stream; - if (device.kind === 'videoinput') { - hasWebcam = true; + var options = stream.currentUserMediaRequestOptions; - if (videoInputDevices.indexOf(device) === -1) { - videoInputDevices.push(device); - } - } + if (this.queueRequests.indexOf(options)) { + delete this.queueRequests[this.queueRequests.indexOf(options)]; + this.queueRequests = removeNullEntries(this.queueRequests); + } - // there is no 'videoouput' in the spec. + this.streams[idInstance].stream = null; + delete this.streams[idInstance]; + } + }; - if (MediaDevices.indexOf(device) === -1) { - MediaDevices.push(device); - } - }); + function getUserMediaHandler(options) { + if (currentUserMediaRequest.mutex === true) { + currentUserMediaRequest.queueRequests.push(options); + return; + } + currentUserMediaRequest.mutex = true; - if (typeof DetectRTC !== 'undefined') { - // to sync latest outputs - DetectRTC.MediaDevices = MediaDevices; - DetectRTC.hasMicrophone = hasMicrophone; - DetectRTC.hasSpeakers = hasSpeakers; - DetectRTC.hasWebcam = hasWebcam; + // easy way to match + var idInstance = JSON.stringify(options.localMediaConstraints); - DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; - DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; + function streaming(stream, returnBack) { + setStreamType(options.localMediaConstraints, stream); + options.onGettingLocalMedia(stream, returnBack); - DetectRTC.audioInputDevices = audioInputDevices; - DetectRTC.audioOutputDevices = audioOutputDevices; - DetectRTC.videoInputDevices = videoInputDevices; - } + stream.addEventListener('ended', function() { + delete currentUserMediaRequest.streams[idInstance]; - if (callback) { - callback(); + currentUserMediaRequest.mutex = false; + if (currentUserMediaRequest.queueRequests.indexOf(options)) { + delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)]; + currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests); } - }); - } - - // check for microphone/camera support! - checkDeviceSupport(); + }, false); - var DetectRTC = window.DetectRTC || {}; + currentUserMediaRequest.streams[idInstance] = { + stream: stream + }; + currentUserMediaRequest.mutex = false; - // ---------- - // DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion - DetectRTC.browser = getBrowserInfo(); + if (currentUserMediaRequest.queueRequests.length) { + getUserMediaHandler(currentUserMediaRequest.queueRequests.shift()); + } + } - detectPrivateMode(function(isPrivateBrowsing) { - DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing; - }); + if (currentUserMediaRequest.streams[idInstance]) { + streaming(currentUserMediaRequest.streams[idInstance].stream, true); + } else { + if (isPluginRTC) { + var mediaElement = document.createElement('video'); + Plugin.getUserMedia({ + audio: true, + video: true + }, function(stream) { + stream.streamid = stream.id || getRandomString(); + streaming(stream); + }, function(error) {}); - // DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge - DetectRTC.browser['is' + DetectRTC.browser.name] = true; + return; + } - var isNodeWebkit = !!(window.process && (typeof window.process === 'object') && window.process.versions && window.process.versions['node-webkit']); + navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream) { + stream.streamid = stream.streamid || stream.id || getRandomString(); + stream.idInstance = idInstance; + streaming(stream); + }).catch(function(error) { + options.onLocalMediaError(error, options.localMediaConstraints); + }); + } + } - // --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1. - var isWebRTCSupported = false; - ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) { - if (isWebRTCSupported) { + // StreamsHandler.js + + var StreamsHandler = (function() { + function handleType(type) { + if (!type) { return; } - if (item in window) { - isWebRTCSupported = true; + if (typeof type === 'string' || typeof type === 'undefined') { + return type; } - }); - DetectRTC.isWebRTCSupported = isWebRTCSupported; - //------- - DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined'; + if (type.audio && type.video) { + return null; + } - // --------- Detect if system supports screen capturing API - var isScreenCapturingSupported = false; - if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) { - isScreenCapturingSupported = true; - } else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) { - isScreenCapturingSupported = true; - } + if (type.audio) { + return 'audio'; + } - if (location.protocol !== 'https:') { - isScreenCapturingSupported = false; - } - DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported; + if (type.video) { + return 'video'; + } - // --------- Detect if WebAudio API are supported - var webAudio = { - isSupported: false, - isCreateMediaStreamSourceSupported: false - }; + return; + } - ['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) { - if (webAudio.isSupported) { - return; + function setHandlers(stream, syncAction, connection) { + if (typeof syncAction == 'undefined' || syncAction == true) { + stream.addEventListener('ended', function() { + StreamsHandler.onSyncNeeded(this.streamid, 'ended'); + }, false); } - if (item in window) { - webAudio.isSupported = true; + stream.mute = function(type, isSyncAction) { + type = handleType(type); - if ('createMediaStreamSource' in window[item].prototype) { - webAudio.isCreateMediaStreamSourceSupported = true; + if (typeof isSyncAction !== 'undefined') { + syncAction = isSyncAction; } - } - }); - DetectRTC.isAudioContextSupported = webAudio.isSupported; - DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported; - // ---------- Detect if SCTP/RTP channels are supported. + if (typeof type == 'undefined' || type == 'audio') { + stream.getAudioTracks().forEach(function(track) { + track.enabled = false; + connection.streamEvents[stream.streamid].isAudioMuted = true; + }); + } - var isRtpDataChannelsSupported = false; - if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) { - isRtpDataChannelsSupported = true; - } - DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported; + if (typeof type == 'undefined' || type == 'video') { + stream.getVideoTracks().forEach(function(track) { + track.enabled = false; + }); + } - var isSCTPSupportd = false; - if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) { - isSCTPSupportd = true; - } else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) { - isSCTPSupportd = true; - } else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) { - isSCTPSupportd = true; - } - DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd; + if (typeof syncAction == 'undefined' || syncAction == true) { + StreamsHandler.onSyncNeeded(stream.streamid, 'mute', type); + } - // --------- + connection.streamEvents[stream.streamid].muteType = type || 'both'; - DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js" + fireEvent(stream, 'mute', type); + }; - // ------ - var isGetUserMediaSupported = false; - if (navigator.getUserMedia) { - isGetUserMediaSupported = true; - } else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { - isGetUserMediaSupported = true; - } - if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && location.protocol !== 'https:') { - DetectRTC.isGetUserMediaSupported = 'Requires HTTPs'; - } - DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported; + stream.unmute = function(type, isSyncAction) { + type = handleType(type); - // ----------- - DetectRTC.osName = osName; - DetectRTC.osVersion = osVersion; + if (typeof isSyncAction !== 'undefined') { + syncAction = isSyncAction; + } - var displayResolution = ''; - if (screen.width) { - var width = (screen.width) ? screen.width : ''; - var height = (screen.height) ? screen.height : ''; - displayResolution += '' + width + ' x ' + height; - } - DetectRTC.displayResolution = displayResolution; + graduallyIncreaseVolume(); - // ---------- - DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing; - DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing; + if (typeof type == 'undefined' || type == 'audio') { + stream.getAudioTracks().forEach(function(track) { + track.enabled = true; + connection.streamEvents[stream.streamid].isAudioMuted = false; + }); + } - // ------ - DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress; + if (typeof type == 'undefined' || type == 'video') { + stream.getVideoTracks().forEach(function(track) { + track.enabled = true; + }); - DetectRTC.isWebSocketsSupported = 'WebSocket' in window && 2 === window.WebSocket.CLOSING; - DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported; + // make sure that video unmute doesn't affects audio + if (typeof type !== 'undefined' && type == 'video' && connection.streamEvents[stream.streamid].isAudioMuted) { + (function looper(times) { + if (!times) { + times = 0; + } - DetectRTC.checkWebSocketsSupport = function(callback) { - callback = callback || function() {}; - try { - var websocket = new WebSocket('wss://echo.websocket.org:443/'); - websocket.onopen = function() { - DetectRTC.isWebSocketsBlocked = false; - callback(); - websocket.close(); - websocket = null; - }; - websocket.onerror = function() { - DetectRTC.isWebSocketsBlocked = true; - callback(); - }; - } catch (e) { - DetectRTC.isWebSocketsBlocked = true; - callback(); - } - }; + times++; - // ------- - DetectRTC.load = function(callback) { - callback = callback || function() {}; - checkDeviceSupport(callback); - }; + // check until five-seconds + if (times < 100 && connection.streamEvents[stream.streamid].isAudioMuted) { + stream.mute('audio'); - DetectRTC.MediaDevices = MediaDevices; - DetectRTC.hasMicrophone = hasMicrophone; - DetectRTC.hasSpeakers = hasSpeakers; - DetectRTC.hasWebcam = hasWebcam; + setTimeout(function() { + looper(times); + }, 50); + } + })(); + } + } - DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions; - DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions; + if (typeof syncAction == 'undefined' || syncAction == true) { + StreamsHandler.onSyncNeeded(stream.streamid, 'unmute', type); + } - DetectRTC.audioInputDevices = audioInputDevices; - DetectRTC.audioOutputDevices = audioOutputDevices; - DetectRTC.videoInputDevices = videoInputDevices; + connection.streamEvents[stream.streamid].unmuteType = type || 'both'; - // ------ - var isSetSinkIdSupported = false; - if ('setSinkId' in document.createElement('video')) { - isSetSinkIdSupported = true; - } - DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported; + fireEvent(stream, 'unmute', type); + }; - // ----- - var isRTPSenderReplaceTracksSupported = false; - if (DetectRTC.browser.isFirefox /*&& DetectRTC.browser.version > 39*/ ) { - /*global mozRTCPeerConnection:true */ - if ('getSenders' in mozRTCPeerConnection.prototype) { - isRTPSenderReplaceTracksSupported = true; - } - } else if (DetectRTC.browser.isChrome) { - /*global webkitRTCPeerConnection:true */ - if ('getSenders' in webkitRTCPeerConnection.prototype) { - isRTPSenderReplaceTracksSupported = true; - } - } - DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported; + function graduallyIncreaseVolume() { + if (!connection.streamEvents[stream.streamid].mediaElement) { + return; + } - //------ - var isRemoteStreamProcessingSupported = false; - if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) { - isRemoteStreamProcessingSupported = true; + var mediaElement = connection.streamEvents[stream.streamid].mediaElement; + mediaElement.volume = 0; + afterEach(200, 5, function() { + mediaElement.volume += .20; + }); + } } - DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported; - //------- - var isApplyConstraintsSupported = false; - - /*global MediaStreamTrack:true */ - if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) { - isApplyConstraintsSupported = true; - } - DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported; + function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) { + startedTimes = (startedTimes || 0) + 1; + if (startedTimes >= numberOfTimes) return; - //------- - var isMultiMonitorScreenCapturingSupported = false; - if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) { - // version 43 merely supports platforms for multi-monitors - // version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing. - isMultiMonitorScreenCapturingSupported = true; + setTimeout(function() { + callback(); + afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes); + }, setTimeoutInteval); } - DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported; - - window.DetectRTC = DetectRTC; + return { + setHandlers: setHandlers, + onSyncNeeded: function(streamid, action, type) {} + }; })(); // Last time updated at Oct 24, 2015, 08:32:23 diff --git a/dist/rmc3.min.js b/dist/rmc3.min.js index 8a7710f7..f0f89c25 100644 --- a/dist/rmc3.min.js +++ b/dist/rmc3.min.js @@ -1,6 +1,6 @@ -// Last time updated: 2016-02-25 12:41:18 PM UTC +// Last time updated: 2016-03-02 9:37:17 AM UTC "use strict";!function(){function RTCMultiConnection(roomid,forceOptions){function onUserLeft(remoteUserId){connection.deletePeer(remoteUserId)}function connectSocket(connectCallback){if(socket)return void(connectCallback&&connectCallback(socket));if("undefined"==typeof SocketConnection)if("undefined"!=typeof FirebaseConnection)window.SocketConnection=FirebaseConnection;else{if("undefined"==typeof PubNubConnection)throw"SocketConnection.js seems missed.";window.SocketConnection=PubNubConnection}socket=new SocketConnection(connection,function(s){socket=s,connectCallback&&connectCallback(socket)})}function beforeUnload(shiftModerationControlOnLeave,dontCloseSocket){connection.closeBeforeUnload&&(connection.isInitiator===!0&&connection.dontMakeMeModerator(),connection.peers.getAllParticipants().forEach(function(participant){mPeer.onNegotiationNeeded({userLeft:!0},participant),connection.peers[participant]&&connection.peers[participant].peer&&connection.peers[participant].peer.close()}),dontCloseSocket||connection.closeSocket(),connection.broadcasters=[],connection.isInitiator=!1)}function applyConstraints(stream,mediaConstraints){return stream?(mediaConstraints.audio&&stream.getAudioTracks().forEach(function(track){track.applyConstraints(mediaConstraints.audio)}),void(mediaConstraints.video&&stream.getVideoTracks().forEach(function(track){track.applyConstraints(mediaConstraints.video)}))):void(connection.enableLogs&&console.error("No stream to applyConstraints."))}function replaceTrack(track,remoteUserId,isVideoTrack){return remoteUserId?void mPeer.replaceTrack(track,remoteUserId,isVideoTrack):void connection.peers.getAllParticipants().forEach(function(participant){mPeer.replaceTrack(track,participant,isVideoTrack)})}function setStreamEndHandler(stream,isRemote){isRemote=!!isRemote,stream.alreadySetEndHandler||(stream.alreadySetEndHandler=!0,stream&&stream.addEventListener&&stream.addEventListener("ended",function(){stream.idInstance&¤tUserMediaRequest.remove(stream.idInstance),isRemote||(delete connection.attachStreams[connection.attachStreams.indexOf(stream)],-1===connection.removeStreams.indexOf(stream)&&connection.removeStreams.push(stream),connection.attachStreams=removeNullEntries(connection.attachStreams),connection.removeStreams=removeNullEntries(connection.removeStreams),connection.observers.all());var streamEvent=connection.streamEvents[stream.streamid];streamEvent||(streamEvent={stream:stream,streamid:stream.streamid,type:isRemote?"remote":"local",userid:connection.userid,extra:connection.extra,mediaElement:connection.streamEvents[stream.streamid]?connection.streamEvents[stream.streamid].mediaElement:null}),(streamEvent.userid!==connection.userid||"remote"!==streamEvent.type)&&(connection.onstreamended(streamEvent),delete connection.streamEvents[stream.streamid])},!1))}function keepNextBroadcasterOnServer(){if(connection.isInitiator&&!connection.session.oneway&&!connection.session.broadcast&&"many-to-many"===connection.direction){var firstBroadcaster=connection.broadcasters[0],otherBroadcasters=[];connection.broadcasters.forEach(function(broadcaster){broadcaster!==firstBroadcaster&&otherBroadcasters.push(broadcaster)}),connection.autoCloseEntireSession||connection.shiftModerationControl(firstBroadcaster,otherBroadcasters,!0)}}forceOptions=forceOptions||{};var connection=this;connection.channel=connection.sessionid=(roomid||location.href.replace(/\/|:|#|\?|\$|\^|%|\.|`|~|!|\+|@|\[|\||]|\|*. /g,"").split("\n").join("").split("\r").join(""))+"";var mPeer=new MultiPeers(connection);mPeer.onGettingLocalMedia=function(stream){stream.type="local",setStreamEndHandler(stream),getRMCMediaElement(stream,function(mediaElement){mediaElement.id=stream.streamid,mediaElement.muted=!0,mediaElement.volume=0,-1===connection.attachStreams.indexOf(stream)&&connection.attachStreams.push(stream),"undefined"!=typeof StreamsHandler&&StreamsHandler.setHandlers(stream,!0,connection),connection.streamEvents[stream.streamid]={stream:stream,type:"local",mediaElement:mediaElement,userid:connection.userid,extra:connection.extra,streamid:stream.streamid,blobURL:mediaElement.src||URL.createObjectURL(stream),isAudioMuted:!0},setHarkEvents(connection,connection.streamEvents[stream.streamid]),setMuteHandlers(connection,connection.streamEvents[stream.streamid]),connection.onstream(connection.streamEvents[stream.streamid])},connection),connection.observers.all()},mPeer.onGettingRemoteMedia=function(stream,remoteUserId){stream.type="remote",setStreamEndHandler(stream,"remote-stream"),getRMCMediaElement(stream,function(mediaElement){mediaElement.id=stream.streamid,"undefined"!=typeof StreamsHandler&&StreamsHandler.setHandlers(stream,!1,connection),connection.streamEvents[stream.streamid]={stream:stream,type:"remote",userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},mediaElement:mediaElement,streamid:stream.streamid,blobURL:mediaElement.src||URL.createObjectURL(stream)},setMuteHandlers(connection,connection.streamEvents[stream.streamid]),connection.onstream(connection.streamEvents[stream.streamid])},connection)},mPeer.onRemovingRemoteMedia=function(stream,remoteUserId){var streamEvent=connection.streamEvents[stream.streamid];streamEvent||(streamEvent={stream:stream,type:"remote",userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},streamid:stream.streamid,mediaElement:connection.streamEvents[stream.streamid]?connection.streamEvents[stream.streamid].mediaElement:null}),connection.onstreamended(streamEvent),delete connection.streamEvents[stream.streamid]},mPeer.onNegotiationNeeded=function(message,remoteUserId,callback){connectSocket(function(){socket.emit(connection.socketMessageEvent,"password"in message?message:{remoteUserId:message.remoteUserId||remoteUserId,message:message,sender:connection.userid},callback||function(){})})},mPeer.onUserLeft=onUserLeft,mPeer.disconnectWith=function(remoteUserId,callback){socket&&socket.emit("disconnect-with",remoteUserId,callback||function(){}),connection.deletePeer(remoteUserId)},connection.broadcasters=[],connection.socketOptions={transport:"polling"};var socket;connection.openOrJoin=function(localUserid,password){connection.checkPresence(localUserid,function(isRoomExists,roomid){if("function"==typeof password&&(password(isRoomExists,roomid),password=null),isRoomExists){connection.sessionid=roomid;var localPeerSdpConstraints=!1,remotePeerSdpConstraints=!1,isOneWay=!!connection.session.oneway,isDataOnly=isData(connection.session);remotePeerSdpConstraints={OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},localPeerSdpConstraints={OfferToReceiveAudio:isOneWay?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:isOneWay?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo};var connectionDescription={remoteUserId:connection.sessionid,message:{newParticipationRequest:!0,isOneWay:isOneWay,isDataOnly:isDataOnly,localPeerSdpConstraints:localPeerSdpConstraints,remotePeerSdpConstraints:remotePeerSdpConstraints},sender:connection.userid,password:password||!1};return void mPeer.onNegotiationNeeded(connectionDescription)}connection.userid;connection.userid=connection.sessionid=localUserid||connection.sessionid,connection.userid+="",socket.emit("changed-uuid",connection.userid),password&&socket.emit("set-password",password),connection.isInitiator=!0,isData(connection.session)||connection.captureUserMedia()})},connection.open=function(localUserid,isPublicModerator){connection.userid;return connection.userid=connection.sessionid=localUserid||connection.sessionid,connection.userid+="",connection.isInitiator=!0,connectSocket(function(){socket.emit("changed-uuid",connection.userid),1==isPublicModerator&&connection.becomePublicModerator()}),isData(connection.session)?void("function"==typeof isPublicModerator&&isPublicModerator()):void connection.captureUserMedia("function"==typeof isPublicModerator?isPublicModerator:null)},connection.becomePublicModerator=function(){connection.isInitiator&&socket.emit("become-a-public-moderator")},connection.dontMakeMeModerator=function(){socket.emit("dont-make-me-moderator")},connection.deletePeer=function(remoteUserId){if(remoteUserId){if(connection.onleave({userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{}}),connection.peers[remoteUserId]){connection.peers[remoteUserId].streams.forEach(function(stream){stream.stop()});var peer=connection.peers[remoteUserId].peer;if(peer&&"closed"!==peer.iceConnectionState)try{peer.close()}catch(e){}connection.peers[remoteUserId]&&(connection.peers[remoteUserId].peer=null,delete connection.peers[remoteUserId])}if(-1!==connection.broadcasters.indexOf(remoteUserId)){var newArray=[];connection.broadcasters.forEach(function(broadcaster){broadcaster!==remoteUserId&&newArray.push(broadcaster)}),connection.broadcasters=newArray,keepNextBroadcasterOnServer()}}},connection.rejoin=function(connectionDescription){if(!connection.isInitiator&&connectionDescription&&Object.keys(connectionDescription).length){var extra={};connection.peers[connectionDescription.remoteUserId]&&(extra=connection.peers[connectionDescription.remoteUserId].extra,connection.deletePeer(connectionDescription.remoteUserId)),connectionDescription&&connectionDescription.remoteUserId&&(connection.join(connectionDescription.remoteUserId),connection.onReConnecting({userid:connectionDescription.remoteUserId,extra:extra}))}},connection.join=connection.connect=function(remoteUserId,options){connection.sessionid=(remoteUserId?remoteUserId.sessionid||remoteUserId.remoteUserId||remoteUserId:!1)||connection.sessionid,connection.sessionid+="";var localPeerSdpConstraints=!1,remotePeerSdpConstraints=!1,isOneWay=!1,isDataOnly=!1;if(remoteUserId&&remoteUserId.session||!remoteUserId||"string"==typeof remoteUserId){var session=remoteUserId?remoteUserId.session||connection.session:connection.session;isOneWay=!!session.oneway,isDataOnly=isData(session),remotePeerSdpConstraints={OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},localPeerSdpConstraints={OfferToReceiveAudio:isOneWay?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:isOneWay?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo}}options=options||{},"undefined"!=typeof options.localPeerSdpConstraints&&(localPeerSdpConstraints=options.localPeerSdpConstraints),"undefined"!=typeof options.remotePeerSdpConstraints&&(remotePeerSdpConstraints=options.remotePeerSdpConstraints),"undefined"!=typeof options.isOneWay&&(isOneWay=options.isOneWay),"undefined"!=typeof options.isDataOnly&&(isDataOnly=options.isDataOnly);var connectionDescription={remoteUserId:connection.sessionid,message:{newParticipationRequest:!0,isOneWay:isOneWay,isDataOnly:isDataOnly,localPeerSdpConstraints:localPeerSdpConstraints,remotePeerSdpConstraints:remotePeerSdpConstraints},sender:connection.userid,password:!1};return connectSocket(function(){connection.peers[connection.sessionid]||mPeer.onNegotiationNeeded(connectionDescription)}),connectionDescription},connection.connectWithAllParticipants=function(remoteUserId){mPeer.onNegotiationNeeded("connectWithAllParticipants",remoteUserId||connection.sessionid)},connection.removeFromBroadcastersList=function(remoteUserId){mPeer.onNegotiationNeeded("removeFromBroadcastersList",remoteUserId||connection.sessionid),connection.peers.getAllParticipants(remoteUserId||connection.sessionid).forEach(function(participant){mPeer.onNegotiationNeeded("dropPeerConnection",participant),connection.deletePeer(participant)}),connection.attachStreams.forEach(function(stream){stream.stop()})},connection.getUserMedia=connection.captureUserMedia=function(callback,session){function invokeGetUserMedia(localMediaConstraints,getUserMedia_callback){var isScreen=!1;localMediaConstraints&&(isScreen=localMediaConstraints.isScreen,delete localMediaConstraints.isScreen),getUserMediaHandler({onGettingLocalMedia:function(stream){return stream.isAudio=stream.isVideo=stream.isScreen=!1,isScreen?stream.isScreen=!0:session.audio&&session.video?stream.isVideo=!0:session.audio&&(stream.isAudio=!0),mPeer.onGettingLocalMedia(stream),getUserMedia_callback?getUserMedia_callback():void(callback&&callback(stream))},onLocalMediaError:function(error,constraints){return mPeer.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints,getUserMedia_callback)):void(callback&&callback())},localMediaConstraints:localMediaConstraints||{audio:session.audio?connection.mediaConstraints.audio:!1,video:session.video?connection.mediaConstraints.video:!1}})}return connection.observers.all(),session=session||connection.session,connection.dontCaptureUserMedia||isData(session)?void(callback&&callback()):void((session.audio||session.video||session.screen)&&(session.screen?connection.getScreenConstraints(function(error,screen_constraints){if(error)throw error;invokeGetUserMedia({video:screen_constraints,isScreen:!0},session.audio||session.video?invokeGetUserMedia:!1)}):(session.audio||session.video)&&invokeGetUserMedia()))},connection.closeBeforeUnload=!0,window.addEventListener("beforeunload",beforeUnload,!1),connection.userid=getRandomString(),connection.changeUserId=function(newUserId){connection.userid=newUserId||getRandomString(),socket.emit("changed-uuid",connection.userid)},connection.observers={extra:function(){observeObject(connection.extra,function(changes){socket.emit("extra-data-updated",connection.extra)})},attachStreams:function(){observeObject(connection.attachStreams,function(changes){changes.forEach(function(change){"add"===change.type&&setStreamEndHandler(change.object[change.name]),("remove"===change.type||"delete"===change.type)&&-1===connection.removeStreams.indexOf(change.object[change.name])&&connection.removeStreams.push(change.object[change.name]),connection.attachStreams=removeNullEntries(connection.attachStreams),connection.removeStreams=removeNullEntries(connection.removeStreams),connection.observers.all()})})},all:function(){this.extra(),this.attachStreams(),socket&&socket.emit("extra-data-updated",connection.extra)}},connection.extra={},connection.attachStreams=[],connection.removeStreams=[],connection.observers.all(),connection.session={audio:!0,video:!0},connection.enableFileSharing=!1,connection.bandwidth={screen:512,audio:128,video:512},connection.codecs={audio:"opus",video:"VP9"},connection.processSdp=function(sdp){return isMobileDevice||isFirefox?sdp:(sdp=CodecsHandler.setApplicationSpecificBandwidth(sdp,connection.bandwidth,!!connection.session.screen),sdp=CodecsHandler.setVideoBitrates(sdp,{min:8*connection.bandwidth.video*1024,max:8*connection.bandwidth.video*1024}),sdp=CodecsHandler.setOpusAttributes(sdp,{maxaveragebitrate:8*connection.bandwidth.audio*1024,maxplaybackrate:8*connection.bandwidth.audio*1024,stereo:1,maxptime:3}),"VP9"===connection.codecs.video&&(sdp=CodecsHandler.preferVP9(sdp)),"H264"===connection.codecs.video&&(sdp=CodecsHandler.removeVPX(sdp)),"G722"===connection.codecs.audio&&(sdp=CodecsHandler.removeNonG722(sdp)),sdp)},"undefined"!=typeof CodecsHandler&&(connection.BandwidthHandler=connection.CodecsHandler=CodecsHandler),connection.mediaConstraints={audio:{mandatory:{},optional:[{bandwidth:8*connection.bandwidth.audio*1024||1048576}]},video:{mandatory:{},optional:[{bandwidth:8*connection.bandwidth.audio*1024||1048576},{googLeakyBucket:!0},{facingMode:"user"}]}},isFirefox&&(connection.mediaConstraints={audio:!0,video:!0}),forceOptions.useDefaultDevices||isMobileDevice||DetectRTC.load(function(){var lastAudioDevice,lastVideoDevice;if(DetectRTC.MediaDevices.forEach(function(device){"audioinput"===device.kind&&connection.mediaConstraints.audio!==!1&&(lastAudioDevice=device),"videoinput"===device.kind&&connection.mediaConstraints.video!==!1&&(lastVideoDevice=device)}),lastAudioDevice){if(isFirefox)return void(connection.mediaConstraints.audio!==!0?connection.mediaConstraints.audio.deviceId=lastAudioDevice.id:connection.mediaConstraints.audio={deviceId:lastAudioDevice.id});1==connection.mediaConstraints.audio&&(connection.mediaConstraints.audio={mandatory:{},optional:[]}),connection.mediaConstraints.audio.optional||(connection.mediaConstraints.audio.optional=[]);var optional=[{sourceId:lastAudioDevice.id}];connection.mediaConstraints.audio.optional=optional.concat(connection.mediaConstraints.audio.optional)}if(lastVideoDevice){if(isFirefox)return void(connection.mediaConstraints.video!==!0?connection.mediaConstraints.video.deviceId=lastVideoDevice.id:connection.mediaConstraints.video={deviceId:lastVideoDevice.id});1==connection.mediaConstraints.video&&(connection.mediaConstraints.video={mandatory:{},optional:[]}),connection.mediaConstraints.video.optional||(connection.mediaConstraints.video.optional=[]);var optional=[{sourceId:lastVideoDevice.id}];connection.mediaConstraints.video.optional=optional.concat(connection.mediaConstraints.video.optional)}}),connection.sdpConstraints={mandatory:{OfferToReceiveAudio:!0,OfferToReceiveVideo:!0},optional:[{VoiceActivityDetection:!1}]},connection.optionalArgument={optional:[{DtlsSrtpKeyAgreement:!0},{googImprovedWifiBwe:!0},{googScreencastMinBitrate:300},{googIPv6:!0},{googDscp:!0},{googCpuUnderuseThreshold:55},{googCpuOveruseThreshold:85},{googSuspendBelowMinBitrate:!0},{googCpuOveruseDetection:!0}],mandatory:{}},connection.iceServers=IceServersHandler.getIceServers(connection),connection.candidates={host:!0,stun:!0,turn:!0},connection.iceProtocols={tcp:!0,udp:!0},connection.onopen=function(event){connection.enableLogs&&console.info("Data connection has been opened between you & ",event.userid)},connection.onclose=function(event){connection.enableLogs&&console.warn("Data connection has been closed between you & ",event.userid)},connection.onerror=function(error){connection.enableLogs&&console.error(error.userid,"data-error",error)},connection.onmessage=function(event){connection.enableLogs&&console.debug("data-message",event.userid,event.data)},connection.send=function(data,remoteUserId){connection.peers.send(data,remoteUserId)},connection.close=connection.disconnect=connection.leave=function(){beforeUnload(!1,!0)},connection.onstream=function(e){var parentNode=connection.videosContainer;parentNode.insertBefore(e.mediaElement,parentNode.firstChild),isMobileDevice&&setTimeout(function(){e.mediaElement.play()},5e3)},connection.onstreamended=function(e){e.mediaElement||(e.mediaElement=document.getElementById(e.streamid)),e.mediaElement&&e.mediaElement.parentNode&&e.mediaElement.parentNode.removeChild(e.mediaElement)},connection.direction="many-to-many",connection.removeStream=function(streamid){var stream;return connection.attachStreams.forEach(function(localStream){localStream.id===streamid&&(stream=localStream)}),stream?void(-1===connection.removeStreams.indexOf(stream)&&(connection.removeStreams.push(stream),connection.peers.getAllParticipants().forEach(function(participant){mPeer.renegotiatePeer(participant)}))):void console.warn("No such stream exists.",streamid)},connection.addStream=function(session){function invokeGetUserMedia(localMediaConstraints,callback){getUserMediaHandler({onGettingLocalMedia:function(stream){var videoConstraints=localMediaConstraints?localMediaConstraints.video:connection.mediaConstraints;return videoConstraints&&(videoConstraints.mediaSource||videoConstraints.mozMediaSource?stream.isScreen=!0:videoConstraints.mandatory&&videoConstraints.mandatory.chromeMediaSource&&(stream.isScreen=!0)),stream.isScreen||(stream.isVideo=stream.getVideoTracks().length,stream.isAudio=!stream.isVideo&&stream.getAudioTracks().length),mPeer.onGettingLocalMedia(stream),session.streamCallback&&session.streamCallback(stream),callback?callback():void connection.renegotiate()},onLocalMediaError:function(error,constraints){return mPeer.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints,callback)):callback?callback():void connection.renegotiate()},localMediaConstraints:localMediaConstraints||{audio:session.audio?connection.mediaConstraints.audio:!1,video:session.video?connection.mediaConstraints.video:!1}})}return isData(session)?void connection.renegotiate():void((!session.audio||session.video||session.screen)&&(session.screen?connection.getScreenConstraints(function(error,screen_constraints){return error?alert(error):void invokeGetUserMedia({video:screen_constraints},session.audio||session.video?invokeGetUserMedia:!1)}):(session.audio||session.video)&&invokeGetUserMedia()))},connection.applyConstraints=function(mediaConstraints,streamid){if(!MediaStreamTrack||!MediaStreamTrack.prototype.applyConstraints)return void alert("track.applyConstraints is NOT supported in your browser.");if(streamid){return connection.streamEvents[streamid]&&(stream=connection.streamEvents[streamid].stream),void applyConstraints(stream,mediaConstraints)}connection.attachStreams.forEach(function(stream){applyConstraints(stream,mediaConstraints)})},connection.replaceTrack=function(session,remoteUserId,isVideoTrack){function invokeGetUserMedia(localMediaConstraints,callback){getUserMediaHandler({onGettingLocalMedia:function(stream){return mPeer.onGettingLocalMedia(stream),callback?callback():void connection.replaceTrack(stream,remoteUserId,isVideoTrack||session.video||session.screen)},onLocalMediaError:function(error,constraints){return mPeer.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints,callback)):void(callback&&callback())},localMediaConstraints:localMediaConstraints||{audio:session.audio?connection.mediaConstraints.audio:!1,video:session.video?connection.mediaConstraints.video:!1}})}if(session=session||{},!RTCPeerConnection.prototype.getSenders)return void connection.addStream(session);if(session instanceof MediaStreamTrack)return void replaceTrack(session,remoteUserId,isVideoTrack);if(session instanceof MediaStream)return session.getVideoTracks().length&&replaceTrack(session.getVideoTracks()[0],remoteUserId,!0),void(session.getAudioTracks().length&&replaceTrack(session.getAudioTracks()[0],remoteUserId,!1));if(isData(session))throw"connection.replaceTrack requires audio and/or video and/or screen.";(!session.audio||session.video||session.screen)&&(session.screen?connection.getScreenConstraints(function(error,screen_constraints){return error?alert(error):void invokeGetUserMedia({video:screen_constraints},session.audio||session.video?invokeGetUserMedia:!1)}):(session.audio||session.video)&&invokeGetUserMedia())},connection.resetTrack=function(remoteUsersIds,isVideoTrack){remoteUsersIds||(remoteUsersIds=connection.getAllParticipants()),"string"==typeof remoteUsersIds&&(remoteUsersIds=[remoteUsersIds]),remoteUsersIds.forEach(function(participant){var peer=connection.peers[participant].peer;"undefined"!=typeof isVideoTrack&&isVideoTrack!==!0||!peer.lastVideoTrack||connection.replaceTrack(peer.lastVideoTrack,participant,!0),"undefined"!=typeof isVideoTrack&&isVideoTrack!==!1||!peer.lastAudioTrack||connection.replaceTrack(peer.lastAudioTrack,participant,!1)})},connection.renegotiate=function(remoteUserId){return remoteUserId?void mPeer.renegotiatePeer(remoteUserId):void connection.peers.getAllParticipants().forEach(function(participant){mPeer.renegotiatePeer(participant)})},connection.onMediaError=function(error,constraints){connection.enableLogs&&console.error(error,constraints)},connection.addNewBroadcaster=function(broadcasterId,userPreferences){connection.broadcasters.length&&setTimeout(function(){mPeer.connectNewParticipantWithAllBroadcasters(broadcasterId,userPreferences,connection.broadcasters.join("|-,-|"))},1e4),connection.session.oneway||connection.session.broadcast||"many-to-many"!==connection.direction||-1!==connection.broadcasters.indexOf(broadcasterId)||(connection.broadcasters.push(broadcasterId),keepNextBroadcasterOnServer())},connection.autoCloseEntireSession=!1,connection.filesContainer=connection.videosContainer=document.body||document.documentElement,connection.isInitiator=!1,connection.shareFile=mPeer.shareFile,"undefined"!=typeof FileProgressBarHandler&&FileProgressBarHandler.handle(connection),"undefined"!=typeof TranslationHandler&&TranslationHandler.handle(connection),connection.token=getRandomString,connection.onNewParticipant=function(participantId,userPreferences){connection.acceptParticipationRequest(participantId,userPreferences)},connection.acceptParticipationRequest=function(participantId,userPreferences){userPreferences.successCallback&&(userPreferences.successCallback(),delete userPreferences.successCallback),mPeer.createNewPeer(participantId,userPreferences)},connection.onShiftedModerationControl=function(sender,existingBroadcasters){connection.acceptModerationControl(sender,existingBroadcasters)},connection.acceptModerationControl=function(sender,existingBroadcasters){connection.isInitiator=!0,connection.broadcasters=existingBroadcasters,connection.peers.getAllParticipants().forEach(function(participant){mPeer.onNegotiationNeeded({changedUUID:sender,oldUUID:connection.userid,newUUID:sender},participant)}),connection.userid=sender,socket.emit("changed-uuid",connection.userid)},connection.shiftModerationControl=function(remoteUserId,existingBroadcasters,firedOnLeave){mPeer.onNegotiationNeeded({shiftedModerationControl:!0,broadcasters:existingBroadcasters,firedOnLeave:!!firedOnLeave},remoteUserId)},"undefined"!=typeof StreamsHandler&&(connection.StreamsHandler=StreamsHandler),connection.onleave=function(userid){},connection.invokeSelectFileDialog=function(callback){var selector=new FileSelector;selector.selectSingleFile(callback)},connection.getPublicModerators=function(userIdStartsWith,callback){"function"==typeof userIdStartsWith&&(callback=userIdStartsWith),connectSocket(function(socket){socket.emit("get-public-moderators","string"==typeof userIdStartsWith?userIdStartsWith:"",callback)})},connection.onmute=function(e){e.mediaElement&&("both"===e.muteType||"video"===e.muteType?(e.mediaElement.src=null,e.mediaElement.pause(),e.mediaElement.poster=e.snapshot||"https://cdn.webrtc-experiment.com/images/muted.png"):"audio"===e.muteType&&(e.mediaElement.muted=!0))},connection.onunmute=function(e){e.mediaElement&&("both"===e.unmuteType||"video"===e.unmuteType?(e.mediaElement.poster=null,e.mediaElement.src=URL.createObjectURL(e.stream),e.mediaElement.play()):"audio"===e.unmuteType&&(e.mediaElement.muted=!1))},connection.onExtraDataUpdated=function(event){event.status="online",connection.onUserStatusChanged(event,!0)},connection.onJoinWithPassword=function(remoteUserId){console.warn(remoteUserId,"is password protected. Please join with password.")},connection.onInvalidPassword=function(remoteUserId,oldPassword){console.warn(remoteUserId,"is password protected. Please join with valid password. Your old password",oldPassword,"is wrong.")},connection.onPasswordMaxTriesOver=function(remoteUserId){console.warn(remoteUserId,"is password protected. Your max password tries exceeded the limit.")},connection.getAllParticipants=function(sender){return connection.peers.getAllParticipants(sender)},"undefined"!=typeof StreamsHandler&&(StreamsHandler.onSyncNeeded=function(streamid,action,type){connection.peers.getAllParticipants().forEach(function(participant){mPeer.onNegotiationNeeded({streamid:streamid,action:action,streamSyncNeeded:!0,type:type||"both"},participant)})}),connection.connectSocket=function(callback){connectSocket(callback)},connection.closeSocket=function(){socket&&("undefined"!=typeof socket.disconnect&&socket.disconnect(),socket=null)},connection.getSocket=function(callback){return socket?callback&&callback(socket):connectSocket(callback),socket},connection.getRemoteStreams=mPeer.getRemoteStreams;var skipStreams=["selectFirst","selectAll","forEach"];if(connection.streamEvents={selectFirst:function(options){if(!options){var firstStream;for(var str in connection.streamEvents)-1!==skipStreams.indexOf(str)||firstStream||(firstStream=connection.streamEvents[str]);return firstStream}},selectAll:function(){}},connection.socketURL="/",connection.socketMessageEvent="RTCMultiConnection-Message",connection.socketCustomEvent="RTCMultiConnection-Custom-Message",connection.DetectRTC=DetectRTC,connection.onUserStatusChanged=function(event,dontWriteLogs){connection.enableLogs&&!dontWriteLogs&&console.info(event.userid,event.status)},connection.getUserMediaHandler=getUserMediaHandler,connection.multiPeersHandler=mPeer,connection.enableLogs=!0,connection.setCustomSocketHandler=function(customSocketHandler){"undefined"!=typeof SocketConnection&&(SocketConnection=customSocketHandler)},connection.chunkSize=65e3,connection.maxParticipantsAllowed=1e3,connection.disconnectWith=mPeer.disconnectWith,connection.checkPresence=function(remoteUserId,callback){mPeer.onNegotiationNeeded({detectPresence:!0,userid:(remoteUserId||connection.sessionid)+""},"system",callback)},connection.onReadyForOffer=function(remoteUserId,userPreferences){connection.multiPeersHandler.createNewPeer(remoteUserId,userPreferences)},connection.setUserPreferences=function(userPreferences){return connection.dontAttachStream&&(userPreferences.dontAttachLocalStream=!0),connection.dontGetRemoteStream&&(userPreferences.dontGetRemoteStream=!0),userPreferences},connection.updateExtraData=function(){socket.emit("extra-data-updated",connection.extra)},connection.enableScalableBroadcast=!1,connection.maxRelayLimitPerUser=3,connection.dontCaptureUserMedia=!1,connection.dontAttachStream=!1,connection.dontGetRemoteStream=!1,connection.onReConnecting=function(event){connection.enableLogs&&console.info("ReConnecting with",event.userid,"...")},connection.beforeAddingStream=function(stream){return stream},connection.beforeRemovingStream=function(stream){return stream},"undefined"!=typeof isChromeExtensionAvailable&&(connection.checkIfChromeExtensionAvailable=isChromeExtensionAvailable),"undefined"!=typeof isFirefoxExtensionAvailable&&(connection.checkIfChromeExtensionAvailable=isFirefoxExtensionAvailable),"undefined"!=typeof getChromeExtensionStatus&&(connection.getChromeExtensionStatus=getChromeExtensionStatus),connection.getScreenConstraints=function(callback){getScreenConstraints(function(error,screen_constraints){error||(screen_constraints=connection.modifyScreenConstraints(screen_constraints),callback(error,screen_constraints))})},connection.modifyScreenConstraints=function(screen_constraints){return screen_constraints},connection.onPeerStateChanged=function(state){connection.enableLogs&&-1!==state.iceConnectionState.search(/closed|failed/gi)&&console.error("Peer connection is closed between you & ",state.userid,state.extra,"state:",state.iceConnectionState)},connection.isOnline=!0,listenEventHandler("online",function(){connection.isOnline=!0}),listenEventHandler("offline",function(){connection.isOnline=!1}),connection.isLowBandwidth=!1,navigator&&navigator.connection&&navigator.connection.type&&(connection.isLowBandwidth=-1!==navigator.connection.type.toString().toLowerCase().search(/wifi|cell/g),connection.isLowBandwidth)){if(connection.bandwidth={audio:30,video:30,screen:30},connection.mediaConstraints.audio&&connection.mediaConstraints.audio.optional.length){var newArray=[];connection.mediaConstraints.audio.optional.forEach(function(opt){"undefined"==typeof opt.bandwidth&&newArray.push(opt); -}),connection.mediaConstraints.audio.optional=newArray}if(connection.mediaConstraints.video&&connection.mediaConstraints.video.optional.length){var newArray=[];connection.mediaConstraints.video.optional.forEach(function(opt){"undefined"==typeof opt.bandwidth&&newArray.push(opt)}),connection.mediaConstraints.video.optional=newArray}}connection.getExtraData=function(remoteUserId){if(!remoteUserId)throw"remoteUserId is required.";return connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{}},forceOptions.autoOpenOrJoin&&connection.openOrJoin(connection.sessionid)}function SocketConnection(connection,connectCallback){var parameters="";parameters+="?userid="+connection.userid,parameters+="&msgEvent="+connection.socketMessageEvent,parameters+="&socketCustomEvent="+connection.socketCustomEvent,connection.enableScalableBroadcast&&(parameters+="&enableScalableBroadcast=true",parameters+="&maxRelayLimitPerUser="+(connection.maxRelayLimitPerUser||2));var socket=io.connect((connection.socketURL||"/")+parameters,connection.socketOptions),mPeer=connection.multiPeersHandler;return socket.on("extra-data-updated",function(remoteUserId,extra){connection.peers[remoteUserId]&&(connection.peers[remoteUserId].extra=extra,connection.onExtraDataUpdated({userid:remoteUserId,extra:extra}))}),socket.on(connection.socketMessageEvent,function(message){if(message.remoteUserId==connection.userid){if(connection.peers[message.sender]&&connection.peers[message.sender].extra!=message.extra&&(connection.peers[message.sender].extra=message.extra,connection.onExtraDataUpdated({userid:message.sender,extra:message.extra})),message.message.streamSyncNeeded&&connection.peers[message.sender]){var stream=connection.streamEvents[message.message.streamid];if(!stream||!stream.stream)return;var action=message.message.action;if("ended"===action||"stream-removed"===action)return void connection.onstreamended(stream);var type="both"!=message.message.type?message.message.type:null;return void stream.stream[action](type)}if("connectWithAllParticipants"===message.message)return-1===connection.broadcasters.indexOf(message.sender)&&connection.broadcasters.push(message.sender),void mPeer.onNegotiationNeeded({allParticipants:connection.getAllParticipants(message.sender)},message.sender);if("removeFromBroadcastersList"===message.message)return void(-1!==connection.broadcasters.indexOf(message.sender)&&(delete connection.broadcasters[connection.broadcasters.indexOf(message.sender)],connection.broadcasters=removeNullEntries(connection.broadcasters)));if("dropPeerConnection"===message.message)return void connection.deletePeer(message.sender);if(message.message.allParticipants)return-1===message.message.allParticipants.indexOf(message.sender)&&message.message.allParticipants.push(message.sender),void message.message.allParticipants.forEach(function(participant){mPeer[connection.peers[participant]?"renegotiatePeer":"createNewPeer"](participant,{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})});if(message.message.newParticipant){if(message.message.newParticipant==connection.userid)return;if(connection.peers[message.message.newParticipant])return;return void mPeer.createNewPeer(message.message.newParticipant,message.message.userPreferences||{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})}if((message.message.readyForOffer||message.message.addMeAsBroadcaster)&&connection.addNewBroadcaster(message.sender),message.message.newParticipationRequest&&message.sender!==connection.userid){connection.peers[message.sender]&&connection.deletePeer(message.sender);var userPreferences={extra:message.extra||{},localPeerSdpConstraints:message.message.remotePeerSdpConstraints||{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:message.message.localPeerSdpConstraints||{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:"undefined"!=typeof message.message.isDataOnly?message.message.isDataOnly:isData(connection.session),dontGetRemoteStream:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,dontAttachLocalStream:!!message.message.dontGetRemoteStream,connectionDescription:message,successCallback:function(){("undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction)&&connection.addNewBroadcaster(message.sender,userPreferences),(connection.session.oneway||"one-way"===connection.direction||isData(connection.session))&&connection.addNewBroadcaster(message.sender,userPreferences)}};return void connection.onNewParticipant(message.sender,userPreferences)}return message.message.shiftedModerationControl?void connection.onShiftedModerationControl(message.sender,message.message.broadcasters):(message.message.changedUUID&&connection.peers[message.message.oldUUID]&&(connection.peers[message.message.newUUID]=connection.peers[message.message.oldUUID],delete connection.peers[message.message.oldUUID]),message.message.userLeft?(mPeer.onUserLeft(message.sender),void(message.message.autoCloseEntireSession&&connection.leave())):void mPeer.addNegotiatedMessage(message.message,message.sender))}}),socket.on("user-left",function(userid){onUserLeft(userid),connection.onUserStatusChanged({userid:userid,status:"offline",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}}),connection.onleave({userid:userid,extra:{}})}),socket.on("connect",function(){connection.enableLogs&&console.info("socket.io connection is opened."),socket.emit("extra-data-updated",connection.extra),connectCallback&&connectCallback(socket)}),socket.on("disconnect",function(){connection.enableLogs&&(console.info("socket.io connection is closed"),console.warn("socket.io reconnecting"))}),socket.on("join-with-password",function(remoteUserId){connection.onJoinWithPassword(remoteUserId)}),socket.on("invalid-password",function(remoteUserId,oldPassword){connection.onInvalidPassword(remoteUserId,oldPassword)}),socket.on("password-max-tries-over",function(remoteUserId){connection.onPasswordMaxTriesOver(remoteUserId)}),socket.on("user-disconnected",function(remoteUserId){remoteUserId!==connection.userid&&(connection.onUserStatusChanged({userid:remoteUserId,status:"offline",extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra||{}:{}}),connection.deletePeer(remoteUserId))}),socket.on("user-connected",function(userid){userid!==connection.userid&&connection.onUserStatusChanged({userid:userid,status:"online",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}})}),socket.on("logs",function(log){connection.enableLogs&&console.debug("server-logs",log)}),socket}function MultiPeers(connection){function initFileBufferReader(){fbr=new FileBufferReader,fbr.onProgress=function(chunk){connection.onFileProgress(chunk)},fbr.onBegin=function(file){connection.onFileStart(file)},fbr.onEnd=function(file){connection.onFileEnd(file)}}var self=this,skipPeers=["getAllParticipants","getLength","selectFirst","streams","send","forEach"];connection.peers={getLength:function(){var numberOfPeers=0;for(var peer in this)-1==skipPeers.indexOf(peer)&&numberOfPeers++;return numberOfPeers},selectFirst:function(){var firstPeer;for(var peer in this)-1==skipPeers.indexOf(peer)&&(firstPeer=this[peer]);return firstPeer},getAllParticipants:function(sender){var allPeers=[];for(var peer in this)-1==skipPeers.indexOf(peer)&&peer!=sender&&allPeers.push(peer);return allPeers},forEach:function(callbcak){this.getAllParticipants().forEach(function(participant){callbcak(connection.peers[participant])})},send:function(data,remoteUserId){var that=this;if(!isNull(data.size)&&!isNull(data.type))return void self.shareFile(data,remoteUserId);if(!("text"===data.type||data instanceof ArrayBuffer||data instanceof DataView))return void TextSender.send({text:data,channel:this,connection:connection,remoteUserId:remoteUserId});if("text"===data.type&&(data=JSON.stringify(data)),remoteUserId){var remoteUser=connection.peers[remoteUserId];if(remoteUser)return void remoteUser.channels.forEach(function(channel){channel.send(data)})}this.getAllParticipants().forEach(function(participant){that[participant].channels.forEach(function(channel){channel.send(data)})})}},this.uuid=connection.userid,this.getLocalConfig=function(remoteSdp,remoteUserId,userPreferences){return userPreferences||(userPreferences={}),{streamsToShare:userPreferences.streamsToShare||{},rtcMultiConnection:connection,connectionDescription:userPreferences.connectionDescription,remoteUserId:remoteUserId,localPeerSdpConstraints:userPreferences.localPeerSdpConstraints,remotePeerSdpConstraints:userPreferences.remotePeerSdpConstraints,dontGetRemoteStream:!!userPreferences.dontGetRemoteStream,dontAttachLocalStream:!!userPreferences.dontAttachLocalStream,renegotiatingPeer:!!userPreferences.renegotiatingPeer,peerRef:userPreferences.peerRef,onLocalSdp:function(localSdp){self.onNegotiationNeeded(localSdp,remoteUserId)},onLocalCandidate:function(localCandidate){localCandidate=OnIceCandidateHandler.processCandidates(connection,localCandidate),localCandidate&&self.onNegotiationNeeded(localCandidate,remoteUserId)},remoteSdp:remoteSdp,onDataChannelMessage:function(message){if(!fbr&&connection.enableFileSharing&&initFileBufferReader(),"string"==typeof message||!connection.enableFileSharing)return void self.onDataChannelMessage(message,remoteUserId);var that=this;return message instanceof ArrayBuffer||message instanceof DataView?void fbr.convertToObject(message,function(object){that.onDataChannelMessage(object)}):message.readyForNextChunk?void fbr.getNextChunk(message.uuid,function(nextChunk,isLastChunk){connection.peers[remoteUserId].channels.forEach(function(channel){channel.send(nextChunk)})},remoteUserId):void fbr.addChunk(message,function(promptNextChunk){connection.peers[remoteUserId].peer.channel.send(promptNextChunk)})},onDataChannelError:function(error){self.onDataChannelError(error,remoteUserId)},onDataChannelOpened:function(channel){self.onDataChannelOpened(channel,remoteUserId)},onDataChannelClosed:function(event){self.onDataChannelClosed(event,remoteUserId)},onRemoteStream:function(stream){if(connection.peers[remoteUserId].streams.push(stream),isPluginRTC){var mediaElement=document.createElement("video"),body=connection.videosContainer;return body.insertBefore(mediaElement,body.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),self.onGettingRemoteMedia(mediaElement,remoteUserId)},3e3)}self.onGettingRemoteMedia(stream,remoteUserId)},onRemoteStreamRemoved:function(stream){self.onRemovingRemoteMedia(stream,remoteUserId)},onPeerStateChanged:function(states){self.onPeerStateChanged(states),"new"===states.iceConnectionState&&self.onNegotiationStarted(remoteUserId,states),"connected"===states.iceConnectionState&&self.onNegotiationCompleted(remoteUserId,states),-1!==states.iceConnectionState.search(/closed|failed/gi)&&(self.onUserLeft(remoteUserId),self.disconnectWith(remoteUserId))}}},this.createNewPeer=function(remoteUserId,userPreferences){if(!(connection.maxParticipantsAllowed<=connection.getAllParticipants().length)){if(userPreferences=userPreferences||{},!userPreferences.isOneWay&&!userPreferences.isDataOnly)return userPreferences.isOneWay=!0,void this.onNegotiationNeeded({enableMedia:!0,userPreferences:userPreferences},remoteUserId);userPreferences=connection.setUserPreferences(userPreferences,remoteUserId);var localConfig=this.getLocalConfig(null,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)}},this.createAnsweringPeer=function(remoteSdp,remoteUserId,userPreferences){userPreferences=connection.setUserPreferences(userPreferences||{},remoteUserId);var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.renegotiatePeer=function(remoteUserId,userPreferences,remoteSdp){if(!connection.peers[remoteUserId])return void(connection.enableLogs&&console.error("This peer ("+remoteUserId+") does not exists. Renegotiation skipped."));userPreferences||(userPreferences={}),userPreferences.renegotiatingPeer=!0,userPreferences.peerRef=connection.peers[remoteUserId].peer;var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.replaceTrack=function(track,remoteUserId,isVideoTrack){if(!connection.peers[remoteUserId])throw"This peer ("+remoteUserId+") does not exists.";var peer=connection.peers[remoteUserId].peer;return peer.getSenders&&"function"==typeof peer.getSenders&&peer.getSenders().length?void peer.getSenders().forEach(function(rtpSender){isVideoTrack&&rtpSender.track instanceof VideoStreamTrack&&(connection.peers[remoteUserId].peer.lastVideoTrack=rtpSender.track,rtpSender.replaceTrack(track)),!isVideoTrack&&rtpSender.track instanceof AudioStreamTrack&&(connection.peers[remoteUserId].peer.lastAudioTrack=rtpSender.track,rtpSender.replaceTrack(track))}):(console.warn("RTPSender.replaceTrack is NOT supported."),void this.renegotiatePeer(remoteUserId))},this.onNegotiationNeeded=function(message,remoteUserId){},this.addNegotiatedMessage=function(message,remoteUserId){function invokeGetUserMedia(mediaConstraints){getUserMediaHandler({onGettingLocalMedia:function(localStream){self.onGettingLocalMedia(localStream);var streamsToShare={};connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},onLocalMediaError:function(error,constraints){return self.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints)):void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},localMediaConstraints:mediaConstraints})}if(message.type&&message.sdp)return"answer"==message.type&&connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteSdp(message),"offer"==message.type&&(message.renegotiatingPeer?this.renegotiatePeer(remoteUserId,null,message):this.createAnsweringPeer(message,remoteUserId)),void(connection.enableLogs&&console.log("Remote peer's sdp:",message.sdp));if(message.candidate)return connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteCandidate(message),void(connection.enableLogs&&console.log("Remote peer's candidate pairs:",message.candidate));if(message.enableMedia){if(connection.attachStreams.length){var streamsToShare={};return connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)}var localMediaConstraints={},userPreferences=message.userPreferences;userPreferences.localPeerSdpConstraints.OfferToReceiveAudio&&(localMediaConstraints.audio=connection.mediaConstraints.audio),userPreferences.localPeerSdpConstraints.OfferToReceiveVideo&&(localMediaConstraints.video=connection.mediaConstraints.video),invokeGetUserMedia(localMediaConstraints)}message.readyForOffer&&connection.onReadyForOffer(remoteUserId,message.userPreferences)},this.connectNewParticipantWithAllBroadcasters=function(newParticipantId,userPreferences,broadcastersList){if(broadcastersList=broadcastersList.split("|-,-|"),broadcastersList.length){var firstBroadcaster=broadcastersList[0];self.onNegotiationNeeded({newParticipant:newParticipantId,userPreferences:userPreferences||!1},firstBroadcaster),delete broadcastersList[0];var array=[];broadcastersList.forEach(function(broadcaster){broadcaster&&array.push(broadcaster)}),setTimeout(function(){self.connectNewParticipantWithAllBroadcasters(newParticipantId,userPreferences,array.join("|-,-|"))},1e4)}},this.onGettingRemoteMedia=function(stream,remoteUserId){},this.onRemovingRemoteMedia=function(stream,remoteUserId){},this.onGettingLocalMedia=function(localStream){},this.onLocalMediaError=function(error,constraints){connection.onMediaError(error,constraints)};var fbr;this.shareFile=function(file,remoteUserId){if(!connection.enableFileSharing)throw'"connection.enableFileSharing" is false.';initFileBufferReader(),fbr.readAsArrayBuffer(file,function(uuid){var arrayOfUsers=connection.getAllParticipants();remoteUserId&&(arrayOfUsers=[remoteUserId]),arrayOfUsers.forEach(function(participant){fbr.getNextChunk(uuid,function(nextChunk){connection.peers[participant].channels.forEach(function(channel){channel.send(nextChunk)})},participant)})},{userid:connection.userid,chunkSize:isFirefox?15e3:connection.chunkSize||0})};var textReceiver=new TextReceiver(connection);this.onDataChannelMessage=function(message,remoteUserId){textReceiver.receive(JSON.parse(message),remoteUserId,connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{})},this.onDataChannelClosed=function(event,remoteUserId){event.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onclose(event)},this.onDataChannelError=function(error,remoteUserId){error.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onerror(error)},this.onDataChannelOpened=function(channel,remoteUserId){connection.peers[remoteUserId].channels.length||(connection.peers[remoteUserId].channels.push(channel),connection.onopen({userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},channel:channel}),connection.observers.all())},this.onPeerStateChanged=function(state){connection.onPeerStateChanged(state)},this.onNegotiationStarted=function(remoteUserId,states){},this.onNegotiationCompleted=function(remoteUserId,states){},this.getRemoteStreams=function(remoteUserId){return remoteUserId=remoteUserId||connection.peers.getAllParticipants()[0],connection.peers[remoteUserId]?connection.peers[remoteUserId].streams:[]},this.isPluginRTC=connection.isPluginRTC=isPluginRTC}function fireEvent(obj,eventName,args){if("undefined"!=typeof CustomEvent){var eventDetail={arguments:args,__exposedProps__:args},event=new CustomEvent(eventName,eventDetail);obj.dispatchEvent(event)}}function setHarkEvents(connection,streamEvent){if(!connection||!streamEvent)throw"Both arguments are required.";if(connection.onspeaking&&connection.onsilence){if("undefined"==typeof hark)throw"hark.js not found.";hark(streamEvent.stream,{onspeaking:function(){connection.onspeaking(streamEvent)},onsilence:function(){connection.onsilence(streamEvent)},onvolumechange:function(volume,threshold){connection.onvolumechange&&connection.onvolumechange(merge({volume:volume,threshold:threshold},streamEvent))}})}}function setMuteHandlers(connection,streamEvent){streamEvent.stream.addEventListener("mute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.muteType,video:"video"===event.muteType},connection.onmute(event)},!1),streamEvent.stream.addEventListener("unmute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.unmuteType,video:"video"===event.unmuteType},connection.onunmute(event)},!1)}function getRandomString(){if(window.crypto&&window.crypto.getRandomValues&&-1===navigator.userAgent.indexOf("Safari")){for(var a=window.crypto.getRandomValues(new Uint32Array(3)),token="",i=0,l=a.length;l>i;i++)token+=a[i].toString(36);return token}return(Math.random()*(new Date).getTime()).toString(36).replace(/\./g,"")}function getRMCMediaElement(stream,callback,connection){var isAudioOnly=!1;stream.getVideoTracks().length||(isAudioOnly=!0);var mediaElement=document.createElement(isAudioOnly?"audio":"video");return isPluginRTC?(connection.videosContainer.insertBefore(mediaElement,connection.videosContainer.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),callback(mediaElement)},1e3)):(mediaElement[isFirefox?"mozSrcObject":"src"]=isFirefox?stream:window.URL.createObjectURL(stream),mediaElement.controls=!0,isFirefox&&mediaElement.addEventListener("ended",function(){if(currentUserMediaRequest.remove(stream.idInstance),"local"===stream.type){StreamsHandler.onSyncNeeded(stream.streamid,"ended"),connection.attachStreams.forEach(function(aStream,idx){stream.streamid===aStream.streamid&&delete connection.attachStreams[idx]});var newStreamsArray=[];connection.attachStreams.forEach(function(aStream){aStream&&newStreamsArray.push(aStream)}),connection.attachStreams=newStreamsArray,connection.observers.all();var streamEvent=connection.streamEvents[stream.streamid];if(streamEvent)return void connection.onstreamended(streamEvent);this.parentNode&&this.parentNode.removeChild(this)}},!1),mediaElement.play(),void callback(mediaElement))}function listenEventHandler(eventName,eventHandler){window.removeEventListener(eventName,eventHandler),window.addEventListener(eventName,eventHandler,!1)}function removeNullEntries(array){var newArray=[];return array.forEach(function(item){item&&newArray.push(item)}),newArray}function isData(session){return!session.audio&&!session.video&&!session.screen&&session.data}function isNull(obj){return"undefined"==typeof obj}function isString(obj){return"string"==typeof obj}function observeObject(obj,callback){Object.observe&&(isMobileDevice||Object.observe(obj,function(changes){var jsonStringified=JSON.stringify(changes);lastChanges!=jsonStringified&&(lastChanges=jsonStringified,callback(changes))}))}function setSdpConstraints(config){var sdpConstraints,sdpConstraints_mandatory={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo};return sdpConstraints={mandatory:sdpConstraints_mandatory,optional:[{VoiceActivityDetection:!1}]},navigator.mozGetUserMedia&&firefoxVersion>34&&(sdpConstraints={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo}),sdpConstraints}function onPluginRTCInitialized(pluginRTCObject){Plugin=pluginRTCObject,MediaStreamTrack=Plugin.MediaStreamTrack,RTCPeerConnection=Plugin.RTCPeerConnection,RTCIceCandidate=Plugin.RTCIceCandidate,RTCSessionDescription=Plugin.RTCSessionDescription}function PeerInitiator(config){function createDataChannel(){if(!isOfferer)return void(peer.ondatachannel=function(event){var channel=event.channel;setChannelEvents(channel)});var channel=peer.createDataChannel("RTCDataChannel",{});setChannelEvents(channel)}function setChannelEvents(channel){channel.binaryType="arraybuffer",channel.onmessage=function(event){config.onDataChannelMessage(event.data)},channel.onopen=function(){config.onDataChannelOpened(channel)},channel.onerror=function(error){config.onDataChannelError(error)},channel.onclose=function(event){config.onDataChannelClosed(event)},channel.internalSend=channel.send,channel.send=function(data){"open"===channel.readyState&&channel.internalSend(data)},peer.channel=channel}var connection=config.rtcMultiConnection;this.extra=config.remoteSdp?config.remoteSdp.extra:connection.extra,this.remoteUserId=config.remoteUserId,this.streams=[],this.channels=[],this.connectionDescription=config.connectionDescription;var that=this;config.remoteSdp&&(this.connectionDescription=config.remoteSdp.connectionDescription);var allRemoteStreams={};if(Object.observe){var that=this;Object.observe(this.channels,function(changes){changes.forEach(function(change){"add"===change.type&&change.object[change.name].addEventListener("close",function(){delete that.channels[that.channels.indexOf(change.object[change.name])],that.channels=removeNullEntries(that.channels)},!1),("remove"===change.type||"delete"===change.type)&&-1!==that.channels.indexOf(change.object[change.name])&&delete that.channels.indexOf(change.object[change.name]),that.channels=removeNullEntries(that.channels)})})}defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:!0,OfferToReceiveVideo:!0});var peer,renegotiatingPeer=!!config.renegotiatingPeer;config.remoteSdp&&(renegotiatingPeer=!!config.remoteSdp.renegotiatingPeer);var localStreams=[];connection.attachStreams.forEach(function(stream){stream&&localStreams.push(stream)}),renegotiatingPeer?(peer=config.peerRef,peer.getLocalStreams().forEach(function(stream){localStreams.forEach(function(localStream,index){stream==localStream&&delete localStreams[index]}),connection.removeStreams.forEach(function(streamToRemove,index){stream===streamToRemove&&(stream=connection.beforeRemovingStream(stream),stream&&peer.removeStream&&peer.removeStream(stream),localStreams.forEach(function(localStream,index){streamToRemove==localStream&&delete localStreams[index]}))})})):peer=new RTCPeerConnection(navigator.onLine?{iceServers:connection.iceServers,iceTransports:"all"}:null,connection.optionalArgument),"Firefox"===connection.DetectRTC.browser.name&&(peer.removeStream=function(stream){stream.mute(),connection.StreamsHandler.onSyncNeeded(stream.streamid,"stream-removed")}),peer.onicecandidate=function(event){event.candidate&&config.onLocalCandidate({candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex})};var isFirefoxOffered=!isFirefox;config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered&&(isFirefoxOffered=!0),localStreams.forEach(function(localStream){config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream||config.dontAttachLocalStream||(localStream=connection.beforeAddingStream(localStream),localStream&&peer.addStream(localStream))}),peer.oniceconnectionstatechange=peer.onsignalingstatechange=function(){var extra=that.extra;connection.peers[that.remoteUserId]&&(extra=connection.peers[that.remoteUserId].extra||extra),peer&&config.onPeerStateChanged({iceConnectionState:peer.iceConnectionState,iceGatheringState:peer.iceGatheringState,signalingState:peer.signalingState,extra:extra,userid:that.remoteUserId})};var sdpConstraints={OfferToReceiveAudio:!!localStreams.length,OfferToReceiveVideo:!!localStreams.length};config.localPeerSdpConstraints&&(sdpConstraints=config.localPeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),peer.onaddstream=function(event){var streamsToShare={};config.remoteSdp&&config.remoteSdp.streamsToShare?streamsToShare=config.remoteSdp.streamsToShare:config.streamsToShare&&(streamsToShare=config.streamsToShare);var streamToShare=streamsToShare[event.stream.id];streamToShare&&(event.stream.isAudio=streamToShare.isAudio,event.stream.isVideo=streamToShare.isVideo,event.stream.isScreen=streamToShare.isScreen),event.stream.streamid=event.stream.id,event.stream.stop||(event.stream.stop=function(){isFirefox&&fireEvent(this,"ended")}),allRemoteStreams[event.stream.id]=event.stream,config.onRemoteStream(event.stream)},peer.onremovestream=function(event){event.stream.streamid=event.stream.id,allRemoteStreams[event.stream.id]&&delete allRemoteStreams[event.stream.id],config.onRemoteStreamRemoved(event.stream)},this.addRemoteCandidate=function(remoteCandidate){peer.addIceCandidate(new RTCIceCandidate(remoteCandidate))},this.addRemoteSdp=function(remoteSdp){remoteSdp.sdp=connection.processSdp(remoteSdp.sdp),peer.setRemoteDescription(new RTCSessionDescription(remoteSdp),function(){},function(error){connection.enableLogs&&console.error(JSON.stringify(error,null," "))})};var isOfferer=!0;config.remoteSdp&&(isOfferer=!1),connection.session.data===!0&&createDataChannel(),config.remoteSdp&&(config.remoteSdp.remotePeerSdpConstraints&&(sdpConstraints=config.remoteSdp.remotePeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),this.addRemoteSdp(config.remoteSdp)),("two-way"==connection.session.audio||"two-way"==connection.session.video||"two-way"==connection.session.screen)&&(defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:"two-way"==connection.session.audio||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio,OfferToReceiveVideo:"two-way"==connection.session.video||"two-way"==connection.session.screen||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio}));var streamsToShare={};peer.getLocalStreams().forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),peer[isOfferer?"createOffer":"createAnswer"](function(localSdp){localSdp.sdp=connection.processSdp(localSdp.sdp),peer.setLocalDescription(localSdp),config.onLocalSdp({type:localSdp.type,sdp:localSdp.sdp,remotePeerSdpConstraints:config.remotePeerSdpConstraints||!1,renegotiatingPeer:!!config.renegotiatingPeer||!1,connectionDescription:that.connectionDescription,dontGetRemoteStream:!!config.dontGetRemoteStream,extra:connection?connection.extra:{},streamsToShare:streamsToShare,isFirefoxOffered:isFirefox})},function(error){connection.enableLogs&&console.error("sdp-error",error)},defaults.sdpConstraints),peer.nativeClose=peer.close,peer.close=function(){if(peer){try{-1===peer.iceConnectionState.search(/closed|failed/gi)&&peer.getRemoteStreams().forEach(function(stream){stream.stop()}),peer.nativeClose()}catch(e){}peer=null,that.peer=null}},this.peer=peer}function setCordovaAPIs(){if("undefined"!=typeof cordova&&"undefined"!=typeof cordova.plugins&&"undefined"!=typeof cordova.plugins.iosrtc&&window.device&&"iOS"===window.device.platform){var iosrtc=cordova.plugins.iosrtc;RTCPeerConnection=iosrtc.RTCPeerConnection,RTCSessionDescription=iosrtc.RTCSessionDescription,RTCIceCandidate=iosrtc.RTCIceCandidate,MediaStream=iosrtc.MediaStream,MediaStreamTrack=iosrtc.MediaStreamTrack,iosrtc.debug.enable("iosrtc*"),iosrtc.registerGlobals()}}function loadIceFrame(callback,skip){if(!loadedIceFrame){if(!skip)return loadIceFrame(callback,!0);loadedIceFrame=!0;var iframe=document.createElement("iframe");iframe.onload=function(){function iFrameLoaderCallback(event){event.data&&event.data.iceServers&&(callback(event.data.iceServers), -window.removeEventListener("message",iFrameLoaderCallback))}iframe.isLoaded=!0,listenEventHandler("message",iFrameLoaderCallback),iframe.contentWindow.postMessage("get-ice-servers","*")},iframe.src="https://cdn.webrtc-experiment.com/getIceServers/",iframe.style.display="none",(document.body||document.documentElement).appendChild(iframe)}}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}function setStreamType(constraints,stream){constraints.mandatory&&constraints.mandatory.chromeMediaSource?stream.isScreen=!0:constraints.mozMediaSource||constraints.mediaSource?stream.isScreen=!0:constraints.video?stream.isVideo=!0:constraints.audio&&(stream.isAudio=!0)}function getUserMediaHandler(options){function streaming(stream,returnBack){setStreamType(options.localMediaConstraints,stream),options.onGettingLocalMedia(stream,returnBack),stream.addEventListener("ended",function(){delete currentUserMediaRequest.streams[idInstance],currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.indexOf(options)&&(delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)],currentUserMediaRequest.queueRequests=removeNullEntries(currentUserMediaRequest.queueRequests))},!1),currentUserMediaRequest.streams[idInstance]={stream:stream},currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.length&&getUserMediaHandler(currentUserMediaRequest.queueRequests.shift())}if(currentUserMediaRequest.mutex===!0)return void currentUserMediaRequest.queueRequests.push(options);currentUserMediaRequest.mutex=!0;var idInstance=JSON.stringify(options.localMediaConstraints);if(currentUserMediaRequest.streams[idInstance])streaming(currentUserMediaRequest.streams[idInstance].stream,!0);else{if(isPluginRTC){document.createElement("video");return void Plugin.getUserMedia({audio:!0,video:!0},function(stream){stream.streamid=stream.id||getRandomString(),streaming(stream)},function(error){})}"undefined"!=typeof DetectRTC&&(DetectRTC.hasMicrophone||(options.localMediaConstraints.audio=!1),DetectRTC.hasWebcam||(options.localMediaConstraints.video=!1)),navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream){stream.streamid=stream.streamid||stream.id||getRandomString(),stream.idInstance=idInstance,streaming(stream)})["catch"](function(error){options.onLocalMediaError(error,options.localMediaConstraints)})}}function TextReceiver(connection){function receive(data,userid,extra){var uuid=data.uuid;if(content[uuid]||(content[uuid]=[]),content[uuid].push(data.message),data.last){var message=content[uuid].join("");data.isobject&&(message=JSON.parse(message));var receivingTime=(new Date).getTime(),latency=receivingTime-data.sendingTime,e={data:message,userid:userid,extra:extra,latency:latency};connection.autoTranslateText?(e.original=e.data,connection.Translator.TranslateText(e.data,function(translatedText){e.data=translatedText,connection.onmessage(e)})):connection.onmessage(e),delete content[uuid]}}var content={};return{receive:receive}}var isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode,isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);"undefined"!=typeof cordova&&(isMobileDevice=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0);var isPluginRTC=!isMobileDevice&&(isSafari||isIE),chromeVersion=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),50),matchArray=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);isChrome&&matchArray&&matchArray[2]&&(chromeVersion=parseInt(matchArray[2],10));var firefoxVersion=50;matchArray=navigator.userAgent.match(/Firefox\/(.*)/),isFirefox&&matchArray&&matchArray[1]&&(firefoxVersion=parseInt(matchArray[1],10)),window.addEventListener||(window.addEventListener=function(el,eventName,eventHandler){el.attachEvent&&el.attachEvent("on"+eventName,eventHandler)}),window.attachEventListener=function(video,type,listener,useCapture){video.addEventListener(type,listener,useCapture)};var MediaStream=window.MediaStream;"undefined"==typeof MediaStream&&"undefined"!=typeof webkitMediaStream&&(MediaStream=webkitMediaStream),"undefined"==typeof MediaStream||"stop"in MediaStream.prototype||(MediaStream.prototype.stop=function(){!this.getAudioTracks&&this.getTracks&&(this.getAudioTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("audio")&&array.push(track)}),array}),!this.getVideoTracks&&this.getTracks&&(this.getVideoTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("video")&&array.push(track)}),array}),this.getAudioTracks().forEach(function(track){track.stop&&track.stop()}),this.getVideoTracks().forEach(function(track){track.stop&&track.stop()}),isFirefox&&fireEvent(this,"ended")}),"undefined"!=typeof MediaStream&&("getAudioTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getAudioTracks||(MediaStream.prototype.getAudioTracks=function(){}),"getVideoTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getVideoTracks||(MediaStream.prototype.getVideoTracks=function(){}));var lastChanges="";!function(){function LoadPluginRTC(){function getPlugin(){return document.getElementById("WebrtcEverywherePluginId")}window.PluginRTC={};var extractPluginObj=function(elt){return elt.isWebRtcPlugin?elt:elt.pluginObj},attachEventListener=function(elt,type,listener,useCapture){var _pluginObj=extractPluginObj(elt);_pluginObj?_pluginObj.bindEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEventListener?elt.addEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEvent&&elt.addEventListener("on"+type,listener,useCapture)},installPlugin=function(){if(!document.getElementById("WebrtcEverywherePluginId")){var pluginObj=document.createElement("object");isIE?pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):pluginObj.setAttribute("type","application/webrtc-everywhere"),pluginObj.setAttribute("id","WebrtcEverywherePluginId"),(document.body||document.documentElement).appendChild(pluginObj),pluginObj.setAttribute("width","0"),pluginObj.setAttribute("height","0")}};document.body?installPlugin():(attachEventListener(window,"load",function(){installPlugin()}),attachEventListener(document,"readystatechange",function(){"complete"==document.readyState&&installPlugin()}));var getUserMediaDelayed;window.PluginRTC.getUserMedia=navigator.getUserMedia=function(constraints,successCallback,errorCallback){"complete"!==document.readyState?getUserMediaDelayed||(getUserMediaDelayed=!0,attachEventListener(document,"readystatechange",function(){getUserMediaDelayed&&"complete"==document.readyState&&(getUserMediaDelayed=!1,getPlugin().getUserMedia(constraints,successCallback,errorCallback))})):getPlugin().getUserMedia(constraints,successCallback,errorCallback)},window.PluginRTC.attachMediaStream=function(element,stream){if(element.isWebRtcPlugin)return element.src=stream,element;if("video"===element.nodeName.toLowerCase()){if(!element.pluginObj&&stream){var _pluginObj=document.createElement("object"),_isIE=Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window;_isIE?_pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):_pluginObj.setAttribute("type","application/webrtc-everywhere"),element.pluginObj=_pluginObj,_pluginObj.setAttribute("className",element.className),_pluginObj.setAttribute("innerHTML",element.innerHTML);var width=element.getAttribute("width"),height=element.getAttribute("height"),bounds=element.getBoundingClientRect();if(width||(width=bounds.right-bounds.left),height||(height=bounds.bottom-bounds.top),"getComputedStyle"in window){var computedStyle=window.getComputedStyle(element,null);width||"auto"==computedStyle.width||"0px"==computedStyle.width||(width=computedStyle.width),height||"auto"==computedStyle.height||"0px"==computedStyle.height||(height=computedStyle.height)}width?_pluginObj.setAttribute("width",width):_pluginObj.setAttribute("autowidth",!0),height?_pluginObj.setAttribute("height",height):_pluginObj.setAttribute("autoheight",!0),(document.body||document.documentElement).appendChild(_pluginObj),element.parentNode&&(element.parentNode.replaceChild(_pluginObj,element),document.body.appendChild(element),element.style.visibility="hidden")}return element.pluginObj&&(element.pluginObj.bindEventListener("play",function(objvid){element.pluginObj&&(element.pluginObj.getAttribute("autowidth")&&objvid.videoWidth&&element.pluginObj.setAttribute("width",objvid.videoWidth),element.pluginObj.getAttribute("autoheight")&&objvid.videoHeight&&element.pluginObj.setAttribute("height",objvid.videoHeight))}),element.pluginObj.src=stream),element.pluginObj}return"audio"===element.nodeName.toLowerCase()?element:void 0},window.PluginRTC.MediaStreamTrack={};var getSourcesDelayed;window.PluginRTC.MediaStreamTrack.getSources=function(gotSources){"complete"!==document.readyState?getSourcesDelayed||(getSourcesDelayed=!0,attachEventListener(document,"readystatechange",function(){getSourcesDelayed&&"complete"==document.readyState&&(getSourcesDelayed=!1,getPlugin().getSources(gotSources))})):getPlugin().getSources(gotSources)},window.PluginRTC.RTCPeerConnection=function(configuration,constraints){return getPlugin().createPeerConnection(configuration,constraints)},window.PluginRTC.RTCIceCandidate=function(RTCIceCandidateInit){return getPlugin().createIceCandidate(RTCIceCandidateInit)},window.PluginRTC.RTCSessionDescription=function(RTCSessionDescriptionInit){return getPlugin().createSessionDescription(RTCSessionDescriptionInit)},window.onPluginRTCInitialized&&window.onPluginRTCInitialized(window.PluginRTC)}var ua=navigator.userAgent.toLowerCase(),isSafari=-1!=ua.indexOf("safari")&&-1==ua.indexOf("chrome"),isIE=!!(Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window);(isSafari||isIE)&&window.addEventListener("load",LoadPluginRTC,!1)}();var RTCPeerConnection,defaults={};"undefined"!=typeof mozRTCPeerConnection?RTCPeerConnection=mozRTCPeerConnection:"undefined"!=typeof webkitRTCPeerConnection?RTCPeerConnection=webkitRTCPeerConnection:"undefined"!=typeof window.RTCPeerConnection?RTCPeerConnection=window.RTCPeerConnection:(console.error("WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser."),RTCPeerConnection=window.RTCSessionDescription=window.RTCIceCandidate=function(){});var RTCSessionDescription=window.RTCSessionDescription||window.mozRTCSessionDescription,RTCIceCandidate=window.RTCIceCandidate||window.mozRTCIceCandidate,MediaStreamTrack=window.MediaStreamTrack,Plugin={};"undefined"!=typeof PluginRTC&&onPluginRTCInitialized(PluginRTC);var isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isIE=!!document.documentMode,isPluginRTC=isSafari||isIE;document.addEventListener("deviceready",setCordovaAPIs,!1),setCordovaAPIs();var CodecsHandler=function(){function removeVPX(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtpmap:100 VP8/90000\r\n",""),sdp=sdp.replace("a=rtpmap:101 VP9/90000\r\n",""),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace("a=rtcp-fb:120 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 ccm fir\r\n","")}function disableNACK(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtcp-fb:126 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:126 nack pli\r\n","a=rtcp-fb:126 pli\r\n"),sdp=sdp.replace("a=rtcp-fb:97 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:97 nack pli\r\n","a=rtcp-fb:97 pli\r\n")}function prioritize(codecMimeType,peer){if(peer&&peer.getSenders&&peer.getSenders().length){if(!codecMimeType||"string"!=typeof codecMimeType)throw"Invalid arguments.";peer.getSenders().forEach(function(sender){for(var params=sender.getParameters(),i=0;ii;++i)if(0===sdpLines[i].indexOf(prefix)&&(!substr||-1!==sdpLines[i].toLowerCase().indexOf(substr.toLowerCase())))return i;return null}function getCodecPayloadType(sdpLine){var pattern=new RegExp("a=rtpmap:(\\d+) \\w+\\/\\d+"),result=sdpLine.match(pattern);return result&&2===result.length?result[1]:null}function setVideoBitrates(sdp,params){if(isMobileDevice)return sdp;params=params||{};var vp8Payload,xgoogle_min_bitrate=params.min,xgoogle_max_bitrate=params.max,sdpLines=sdp.split("\r\n"),vp8Index=findLine(sdpLines,"a=rtpmap","VP8/90000");if(vp8Index&&(vp8Payload=getCodecPayloadType(sdpLines[vp8Index])),!vp8Payload)return sdp;var rtxPayload,rtxIndex=findLine(sdpLines,"a=rtpmap","rtx/90000");if(rtxIndex&&(rtxPayload=getCodecPayloadType(sdpLines[rtxIndex])),!rtxIndex)return sdp;var rtxFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+rtxPayload.toString());if(null!==rtxFmtpLineIndex){var appendrtxNext="\r\n";appendrtxNext+="a=fmtp:"+vp8Payload+" x-google-min-bitrate="+(xgoogle_min_bitrate||"228")+"; x-google-max-bitrate="+(xgoogle_max_bitrate||"228"),sdpLines[rtxFmtpLineIndex]=sdpLines[rtxFmtpLineIndex].concat(appendrtxNext),sdp=sdpLines.join("\r\n")}return sdp}function setOpusAttributes(sdp,params){if(isMobileDevice)return sdp;params=params||{};var opusPayload,sdpLines=sdp.split("\r\n"),opusIndex=findLine(sdpLines,"a=rtpmap","opus/48000");if(opusIndex&&(opusPayload=getCodecPayloadType(sdpLines[opusIndex])),!opusPayload)return sdp;var opusFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+opusPayload.toString());if(null===opusFmtpLineIndex)return sdp;var appendOpusNext="";return appendOpusNext+="; stereo="+("undefined"!=typeof params.stereo?params.stereo:"1"),appendOpusNext+="; sprop-stereo="+("undefined"!=typeof params["sprop-stereo"]?params["sprop-stereo"]:"1"),"undefined"!=typeof params.maxaveragebitrate&&(appendOpusNext+="; maxaveragebitrate="+(params.maxaveragebitrate||1048576)),"undefined"!=typeof params.maxplaybackrate&&(appendOpusNext+="; maxplaybackrate="+(params.maxplaybackrate||1048576)),"undefined"!=typeof params.cbr&&(appendOpusNext+="; cbr="+("undefined"!=typeof params.cbr?params.cbr:"1")),"undefined"!=typeof params.useinbandfec&&(appendOpusNext+="; useinbandfec="+params.useinbandfec),"undefined"!=typeof params.usedtx&&(appendOpusNext+="; usedtx="+params.usedtx),"undefined"!=typeof params.maxptime&&(appendOpusNext+="\r\na=maxptime:"+params.maxptime),sdpLines[opusFmtpLineIndex]=sdpLines[opusFmtpLineIndex].concat(appendOpusNext),sdp=sdpLines.join("\r\n")}function preferVP9(sdp){return-1===sdp.indexOf("SAVPF 100 101")||-1===sdp.indexOf("VP9/90000")?sdp:sdp.replace("SAVPF 100 101","SAVPF 101 100")}var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);return"undefined"!=typeof cordova&&(isMobileDevice=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0),{removeVPX:removeVPX,disableNACK:disableNACK,prioritize:prioritize,removeNonG722:removeNonG722,setApplicationSpecificBandwidth:function(sdp,bandwidth,isScreen){return setBAS(sdp,bandwidth,isScreen)},setVideoBitrates:function(sdp,params){return setVideoBitrates(sdp,params)},setOpusAttributes:function(sdp,params){return setOpusAttributes(sdp,params)},preferVP9:preferVP9}}();window.BandwidthHandler=CodecsHandler;var loadedIceFrame,OnIceCandidateHandler=function(){function processCandidates(connection,icePair){var candidate=icePair.candidate,iceRestrictions=connection.candidates,stun=iceRestrictions.stun,turn=iceRestrictions.turn;if(isNull(iceRestrictions.reflexive)||(stun=iceRestrictions.reflexive),isNull(iceRestrictions.relay)||(turn=iceRestrictions.relay),(iceRestrictions.host||!candidate.match(/typ host/g))&&(turn||!candidate.match(/typ relay/g))&&(stun||!candidate.match(/typ srflx/g))){var protocol=connection.iceProtocols;if((protocol.udp||!candidate.match(/ udp /g))&&(protocol.tcp||!candidate.match(/ tcp /g)))return connection.enableLogs&&console.debug("Your candidate pairs:",candidate),{candidate:candidate,sdpMid:icePair.sdpMid,sdpMLineIndex:icePair.sdpMLineIndex}}}return{processCandidates:processCandidates}}();"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&loadIceFrame(function(externalIceServers){externalIceServers&&externalIceServers.length&&(window.RMCExternalIceServers=externalIceServers,window.iceServersLoadCallback&&"function"==typeof window.iceServersLoadCallback&&window.iceServersLoadCallback(externalIceServers))});var IceServersHandler=function(){function getIceServers(connection){var iceServers=[];return iceServers.push({urls:"stun:stun.l.google.com:19302"},{urls:"stun:mmt-stun.verkstad.net"},{urls:"stun:stun.anyfirewall.com:3478"}),iceServers.push({urls:"turn:turn.bistri.com:80",credential:"homeo",username:"homeo"}),iceServers.push({urls:"turn:turn.anyfirewall.com:443",credential:"webrtc",username:"webrtc"}),iceServers.push({urls:"turn:mmt-turn.verkstad.net",username:"webrtc",credential:"secret"}),window.RMCExternalIceServers?(iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers):"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&(window.iceServersLoadCallback=function(){iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers}),iceServers}return{getIceServers:getIceServers}}(),getUserMedia=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils=window.webrtcUtils||{};if(webrtcUtils.enableLogs||(webrtcUtils.enableLogs=!0),webrtcUtils.log||(webrtcUtils.log=function(){webrtcUtils.enableLogs&&("undefined"!=typeof module||"function"==typeof require&&"function"==typeof define||console.log.apply(console,arguments))}),webrtcUtils.extractVersion||(webrtcUtils.extractVersion=function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos],10)}),"object"==typeof window&&(!window.HTMLMediaElement||"srcObject"in window.HTMLMediaElement.prototype||Object.defineProperty(window.HTMLMediaElement.prototype,"srcObject",{get:function(){return"mozSrcObject"in this?this.mozSrcObject:this._srcObject},set:function(stream){"mozSrcObject"in this?this.mozSrcObject=stream:(this._srcObject=stream,this.src=stream?URL.createObjectURL(stream):null)}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia&&window.mozRTCPeerConnection){if(webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,getUserMedia=function(constraints,onSuccess,onError){var constraintsToFF37=function(c){if("object"!=typeof c||c.require)return c;var require=[];return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r=c[key]="object"==typeof c[key]?c[key]:{ideal:c[key]};if((void 0!==r.min||void 0!==r.max||void 0!==r.exact)&&require.push(key),void 0!==r.exact&&("number"==typeof r.exact?r.min=r.max=r.exact:c[key]=r.exact,delete r.exact),void 0!==r.ideal){c.advanced=c.advanced||[];var oc={};"number"==typeof r.ideal?oc[key]={min:r.ideal,max:r.ideal}:oc[key]=r.ideal,c.advanced.push(oc),delete r.ideal,Object.keys(r).length||delete c[key]}}}),require.length&&(c.require=require),c};return 38>webrtcDetectedVersion&&(webrtcUtils.log("spec: "+JSON.stringify(constraints)),constraints.audio&&(constraints.audio=constraintsToFF37(constraints.audio)),constraints.video&&(constraints.video=constraintsToFF37(constraints.video)),webrtcUtils.log("ff37: "+JSON.stringify(constraints))),navigator.mozGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia,addEventListener:function(){},removeEventListener:function(){}}),navigator.mediaDevices.enumerateDevices=navigator.mediaDevices.enumerateDevices||function(){return new Promise(function(resolve){var infos=[{kind:"audioinput",deviceId:"default",label:"",groupId:""},{kind:"videoinput",deviceId:"default",label:"",groupId:""}];resolve(infos)})},41>webrtcDetectedVersion){var orgEnumerateDevices=navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);navigator.mediaDevices.enumerateDevices=function(){return orgEnumerateDevices().then(void 0,function(e){if("NotFoundError"===e.name)return[];throw e})}}}else if(navigator.webkitGetUserMedia&&window.webkitRTCPeerConnection){webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38;var constraintsToChrome=function(c){if("object"!=typeof c||c.mandatory||c.optional)return c;var cc={};return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r="object"==typeof c[key]?c[key]:{ideal:c[key]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);var oldname=function(prefix,name){return prefix?prefix+name.charAt(0).toUpperCase()+name.slice(1):"deviceId"===name?"sourceId":name};if(void 0!==r.ideal){cc.optional=cc.optional||[];var oc={};"number"==typeof r.ideal?(oc[oldname("min",key)]=r.ideal,cc.optional.push(oc),oc={},oc[oldname("max",key)]=r.ideal,cc.optional.push(oc)):(oc[oldname("",key)]=r.ideal,cc.optional.push(oc))}void 0!==r.exact&&"number"!=typeof r.exact?(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname("",key)]=r.exact):["min","max"].forEach(function(mix){void 0!==r[mix]&&(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname(mix,key)]=r[mix])})}}),c.advanced&&(cc.optional=(cc.optional||[]).concat(c.advanced)),cc};if(getUserMedia=function(constraints,onSuccess,onError){return constraints.audio&&(constraints.audio=constraintsToChrome(constraints.audio)),constraints.video&&(constraints.video=constraintsToChrome(constraints.video)),webrtcUtils.log("chrome: "+JSON.stringify(constraints)),navigator.webkitGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia}),navigator.mediaDevices.getUserMedia){var origGetUserMedia=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=function(c){return webrtcUtils.log("spec: "+JSON.stringify(c)),c.audio=constraintsToChrome(c.audio),c.video=constraintsToChrome(c.video),webrtcUtils.log("chrome: "+JSON.stringify(c)),origGetUserMedia(c)}}else navigator.mediaDevices.getUserMedia=function(constraints){return requestUserMedia(constraints)};"undefined"==typeof navigator.mediaDevices.addEventListener&&(navigator.mediaDevices.addEventListener=function(){webrtcUtils.log("Dummy mediaDevices.addEventListener called.")}),"undefined"==typeof navigator.mediaDevices.removeEventListener&&(navigator.mediaDevices.removeEventListener=function(){webrtcUtils.log("Dummy mediaDevices.removeEventListener called.")})}else navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?(webrtcUtils.log("This appears to be Edge"),webrtcDetectedBrowser="edge",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),webrtcMinimumVersion=12):webrtcUtils.log("Browser does not appear to be WebRTC-capable");else webrtcDetectedBrowser="not a browser";"undefined"!=typeof module?module.exports={getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}:"function"==typeof require&&"function"==typeof define&&define([],function(){return{getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}}),"undefined"!=typeof webrtcUtils&&(webrtcUtils.enableLogs=!1);var currentUserMediaRequest={streams:[],mutex:!1,queueRequests:[],remove:function(idInstance){this.mutex=!1;var stream=this.streams[idInstance];if(stream){stream=stream.stream;var options=stream.currentUserMediaRequestOptions;this.queueRequests.indexOf(options)&&(delete this.queueRequests[this.queueRequests.indexOf(options)],this.queueRequests=removeNullEntries(this.queueRequests)),this.streams[idInstance].stream=null,delete this.streams[idInstance]}}},StreamsHandler=function(){function handleType(type){return type?"string"==typeof type||"undefined"==typeof type?type:type.audio&&type.video?null:type.audio?"audio":type.video?"video":void 0:void 0}function setHandlers(stream,syncAction,connection){function graduallyIncreaseVolume(){if(connection.streamEvents[stream.streamid].mediaElement){var mediaElement=connection.streamEvents[stream.streamid].mediaElement;mediaElement.volume=0,afterEach(200,5,function(){mediaElement.volume+=.2})}}("undefined"==typeof syncAction||1==syncAction)&&stream.addEventListener("ended",function(){StreamsHandler.onSyncNeeded(this.streamid,"ended")},!1),stream.mute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!1,connection.streamEvents[stream.streamid].isAudioMuted=!0}),("undefined"==typeof type||"video"==type)&&stream.getVideoTracks().forEach(function(track){track.enabled=!1}),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"mute",type),connection.streamEvents[stream.streamid].muteType=type||"both",fireEvent(stream,"mute",type)},stream.unmute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),graduallyIncreaseVolume(),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!0,connection.streamEvents[stream.streamid].isAudioMuted=!1}),("undefined"==typeof type||"video"==type)&&(stream.getVideoTracks().forEach(function(track){track.enabled=!0}),"undefined"!=typeof type&&"video"==type&&connection.streamEvents[stream.streamid].isAudioMuted&&!function looper(times){times||(times=0),times++,100>times&&connection.streamEvents[stream.streamid].isAudioMuted&&(stream.mute("audio"),setTimeout(function(){looper(times)},50))}()),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"unmute",type),connection.streamEvents[stream.streamid].unmuteType=type||"both",fireEvent(stream,"unmute",type)}}function afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes){startedTimes=(startedTimes||0)+1,startedTimes>=numberOfTimes||setTimeout(function(){callback(),afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes)},setTimeoutInteval)}return{setHandlers:setHandlers,onSyncNeeded:function(streamid,action,type){}}}();!function(){function getBrowserInfo(){var nameOffset,verOffset,ix,nAgt=(navigator.appVersion,navigator.userAgent),browserName=navigator.appName,fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10);if(isOpera){browserName="Opera";try{fullVersion=navigator.userAgent.split("OPR/")[1].split(" ")[0],majorVersion=fullVersion.split(".")[0]}catch(e){fullVersion="0.0.0.0",majorVersion=0}}else isIE?(verOffset=nAgt.indexOf("MSIE"),browserName="IE",fullVersion=nAgt.substring(verOffset+5)):isChrome?(verOffset=nAgt.indexOf("Chrome"),browserName="Chrome",fullVersion=nAgt.substring(verOffset+7)):isSafari?(verOffset=nAgt.indexOf("Safari"),browserName="Safari",fullVersion=nAgt.substring(verOffset+7),-1!==(verOffset=nAgt.indexOf("Version"))&&(fullVersion=nAgt.substring(verOffset+8))):isFirefox?(verOffset=nAgt.indexOf("Firefox"),browserName="Firefox",fullVersion=nAgt.substring(verOffset+8)):(nameOffset=nAgt.lastIndexOf(" ")+1)<(verOffset=nAgt.lastIndexOf("/"))&&(browserName=nAgt.substring(nameOffset,verOffset),fullVersion=nAgt.substring(verOffset+1),browserName.toLowerCase()===browserName.toUpperCase()&&(browserName=navigator.appName));return isEdge&&(browserName="Edge",fullVersion=parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2],10).toString()),-1!==(ix=fullVersion.indexOf(";"))&&(fullVersion=fullVersion.substring(0,ix)),-1!==(ix=fullVersion.indexOf(" "))&&(fullVersion=fullVersion.substring(0,ix)),majorVersion=parseInt(""+fullVersion,10),isNaN(majorVersion)&&(fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10)),{fullVersion:fullVersion,version:majorVersion,name:browserName,isPrivateBrowsing:!1}}function retry(isDone,next){var currentTrial=0,maxRetry=50,isTimeout=!1,id=window.setInterval(function(){isDone()&&(window.clearInterval(id),next(isTimeout)),currentTrial++>maxRetry&&(window.clearInterval(id),isTimeout=!0,next(isTimeout))},10)}function isIE10OrLater(userAgent){var ua=userAgent.toLowerCase();if(0===ua.indexOf("msie")&&0===ua.indexOf("trident"))return!1;var match=/(?:msie|rv:)\s?([\d\.]+)/.exec(ua);return match&&parseInt(match[1],10)>=10?!0:!1}function detectPrivateMode(callback){var isPrivate;if(window.webkitRequestFileSystem)window.webkitRequestFileSystem(window.TEMPORARY,1,function(){isPrivate=!1},function(e){console.log(e),isPrivate=!0});else if(window.indexedDB&&/Firefox/.test(window.navigator.userAgent)){var db;try{db=window.indexedDB.open("test")}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&retry(function(){return"done"===db.readyState?!0:!1},function(isTimeout){isTimeout||(isPrivate=db.result?!1:!0)})}else if(isIE10OrLater(window.navigator.userAgent)){isPrivate=!1;try{window.indexedDB||(isPrivate=!0)}catch(e){isPrivate=!0}}else if(window.localStorage&&/Safari/.test(window.navigator.userAgent)){try{window.localStorage.setItem("test",1)}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&(isPrivate=!1,window.localStorage.removeItem("test")); -}retry(function(){return"undefined"!=typeof isPrivate?!0:!1},function(isTimeout){callback(isPrivate)})}function detectDesktopOS(){var unknown="-",nVer=navigator.appVersion,nAgt=navigator.userAgent,os=unknown,clientStrings=[{s:"Windows 10",r:/(Windows 10.0|Windows NT 10.0)/},{s:"Windows 8.1",r:/(Windows 8.1|Windows NT 6.3)/},{s:"Windows 8",r:/(Windows 8|Windows NT 6.2)/},{s:"Windows 7",r:/(Windows 7|Windows NT 6.1)/},{s:"Windows Vista",r:/Windows NT 6.0/},{s:"Windows Server 2003",r:/Windows NT 5.2/},{s:"Windows XP",r:/(Windows NT 5.1|Windows XP)/},{s:"Windows 2000",r:/(Windows NT 5.0|Windows 2000)/},{s:"Windows ME",r:/(Win 9x 4.90|Windows ME)/},{s:"Windows 98",r:/(Windows 98|Win98)/},{s:"Windows 95",r:/(Windows 95|Win95|Windows_95)/},{s:"Windows NT 4.0",r:/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/},{s:"Windows CE",r:/Windows CE/},{s:"Windows 3.11",r:/Win16/},{s:"Android",r:/Android/},{s:"Open BSD",r:/OpenBSD/},{s:"Sun OS",r:/SunOS/},{s:"Linux",r:/(Linux|X11)/},{s:"iOS",r:/(iPhone|iPad|iPod)/},{s:"Mac OS X",r:/Mac OS X/},{s:"Mac OS",r:/(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},{s:"QNX",r:/QNX/},{s:"UNIX",r:/UNIX/},{s:"BeOS",r:/BeOS/},{s:"OS/2",r:/OS\/2/},{s:"Search Bot",r:/(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/}];for(var id in clientStrings){var cs=clientStrings[id];if(cs.r.test(nAgt)){os=cs.s;break}}var osVersion=unknown;switch(/Windows/.test(os)&&(osVersion=/Windows (.*)/.exec(os)[1],os="Windows"),os){case"Mac OS X":osVersion=/Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1];break;case"Android":osVersion=/Android ([\.\_\d]+)/.exec(nAgt)[1];break;case"iOS":osVersion=/OS (\d+)_(\d+)_?(\d+)?/.exec(nVer),osVersion=osVersion[1]+"."+osVersion[2]+"."+(0|osVersion[3])}return{osName:os,osVersion:osVersion}}function DetectLocalIPAddress(callback){DetectRTC.isWebRTCSupported&&(DetectRTC.isORTCSupported||getIPs(function(ip){callback(ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)?"Local: "+ip:"Public: "+ip)}))}function getIPs(callback){function handleCandidate(candidate){var ipRegex=/([0-9]{1,3}(\.[0-9]{1,3}){3})/,match=ipRegex.exec(candidate);if(!match)return void console.warn("Could not match IP address in",candidate);var ipAddress=match[1];void 0===ipDuplicates[ipAddress]&&callback(ipAddress),ipDuplicates[ipAddress]=!0}var ipDuplicates={},RTCPeerConnection=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection,useWebKit=!!window.webkitRTCPeerConnection;if(!RTCPeerConnection){var iframe=document.getElementById("iframe");if(!iframe)throw"NOTE: you need to have an iframe in the page right above the script tag.";var win=iframe.contentWindow;RTCPeerConnection=win.RTCPeerConnection||win.mozRTCPeerConnection||win.webkitRTCPeerConnection,useWebKit=!!win.webkitRTCPeerConnection}if(RTCPeerConnection){var servers,mediaConstraints={optional:[{RtpDataChannels:!0}]};useWebKit&&(servers={iceServers:[{urls:"stun:stun.services.mozilla.com"}]},"undefined"!=typeof DetectRTC&&DetectRTC.browser.isFirefox&&DetectRTC.browser.version<=38&&(servers[0]={url:servers[0].urls}));var pc=new RTCPeerConnection(servers,mediaConstraints);pc.onicecandidate=function(ice){ice.candidate&&handleCandidate(ice.candidate.candidate)},pc.createDataChannel(""),pc.createOffer(function(result){pc.setLocalDescription(result,function(){},function(){})},function(){}),setTimeout(function(){var lines=pc.localDescription.sdp.split("\n");lines.forEach(function(line){0===line.indexOf("a=candidate:")&&handleCandidate(line)})},1e3)}}function checkDeviceSupport(callback){if(canEnumerate){if(!navigator.enumerateDevices&&window.MediaStreamTrack&&window.MediaStreamTrack.getSources&&(navigator.enumerateDevices=window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack)),!navigator.enumerateDevices&&navigator.enumerateDevices&&(navigator.enumerateDevices=navigator.enumerateDevices.bind(navigator)),!navigator.enumerateDevices)return void(callback&&callback());MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[],navigator.enumerateDevices(function(devices){devices.forEach(function(_device){var device={};for(var d in _device)device[d]=_device[d];"audio"===device.kind&&(device.kind="audioinput"),"video"===device.kind&&(device.kind="videoinput");var skip;MediaDevices.forEach(function(d){d.id===device.id&&d.kind===device.kind&&(skip=!0)}),skip||(device.deviceId||(device.deviceId=device.id),device.id||(device.id=device.deviceId),device.label?("videoinput"!==device.kind||isWebsiteHasWebcamPermissions||(isWebsiteHasWebcamPermissions=!0),"audioinput"!==device.kind||isWebsiteHasMicrophonePermissions||(isWebsiteHasMicrophonePermissions=!0)):(device.label="Please invoke getUserMedia once.","https:"!==location.protocol&&(device.label="HTTPs is required to get label of this "+device.kind+" device.")),"audioinput"===device.kind&&(hasMicrophone=!0,-1===audioInputDevices.indexOf(device)&&audioInputDevices.push(device)),"audiooutput"===device.kind&&(hasSpeakers=!0,-1===audioOutputDevices.indexOf(device)&&audioOutputDevices.push(device)),"videoinput"===device.kind&&(hasWebcam=!0,-1===videoInputDevices.indexOf(device)&&videoInputDevices.push(device)),-1===MediaDevices.indexOf(device)&&MediaDevices.push(device))}),"undefined"!=typeof DetectRTC&&(DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices),callback&&callback()})}}var navigator=window.navigator;"undefined"!=typeof navigator?("undefined"!=typeof navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),"undefined"!=typeof navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia)):navigator={getUserMedia:function(){},userAgent:"Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45"};var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i),isEdge=!(-1===navigator.userAgent.indexOf("Edge")||!navigator.msSaveOrOpenBlob&&!navigator.msSaveBlob),isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode&&!isEdge,isMobile={Android:function(){return navigator.userAgent.match(/Android/i)},BlackBerry:function(){return navigator.userAgent.match(/BlackBerry/i)},iOS:function(){return navigator.userAgent.match(/iPhone|iPad|iPod/i)},Opera:function(){return navigator.userAgent.match(/Opera Mini/i)},Windows:function(){return navigator.userAgent.match(/IEMobile/i)},any:function(){return isMobile.Android()||isMobile.BlackBerry()||isMobile.iOS()||isMobile.Opera()||isMobile.Windows()},getOsName:function(){var osName="Unknown OS";return isMobile.Android()&&(osName="Android"),isMobile.BlackBerry()&&(osName="BlackBerry"),isMobile.iOS()&&(osName="iOS"),isMobile.Opera()&&(osName="Opera Mini"),isMobile.Windows()&&(osName="Windows"),osName}},osName="Unknown OS",osVersion="Unknown OS Version";if(isMobile.any())osName=isMobile.getOsName();else{var osInfo=detectDesktopOS();osName=osInfo.osName,osVersion=osInfo.osVersion}var isCanvasSupportsStreamCapturing=!1,isVideoSupportsStreamCapturing=!1;["captureStream","mozCaptureStream","webkitCaptureStream"].forEach(function(item){!isCanvasSupportsStreamCapturing&&item in document.createElement("canvas")&&(isCanvasSupportsStreamCapturing=!0),!isVideoSupportsStreamCapturing&&item in document.createElement("video")&&(isVideoSupportsStreamCapturing=!0)});var MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[];navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(navigator.enumerateDevices=function(callback){navigator.mediaDevices.enumerateDevices().then(callback)});var canEnumerate=!1;"undefined"!=typeof MediaStreamTrack&&"getSources"in MediaStreamTrack?canEnumerate=!0:navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(canEnumerate=!0);var hasMicrophone=!1,hasSpeakers=!1,hasWebcam=!1,isWebsiteHasMicrophonePermissions=!1,isWebsiteHasWebcamPermissions=!1;checkDeviceSupport();var DetectRTC=window.DetectRTC||{};DetectRTC.browser=getBrowserInfo(),detectPrivateMode(function(isPrivateBrowsing){DetectRTC.browser.isPrivateBrowsing=!!isPrivateBrowsing}),DetectRTC.browser["is"+DetectRTC.browser.name]=!0;var isWebRTCSupported=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),!1);["RTCPeerConnection","webkitRTCPeerConnection","mozRTCPeerConnection","RTCIceGatherer"].forEach(function(item){isWebRTCSupported||item in window&&(isWebRTCSupported=!0)}),DetectRTC.isWebRTCSupported=isWebRTCSupported,DetectRTC.isORTCSupported="undefined"!=typeof RTCIceGatherer;var isScreenCapturingSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>=35?isScreenCapturingSupported=!0:DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=34&&(isScreenCapturingSupported=!0),"https:"!==location.protocol&&(isScreenCapturingSupported=!1),DetectRTC.isScreenCapturingSupported=isScreenCapturingSupported;var webAudio={isSupported:!1,isCreateMediaStreamSourceSupported:!1};["AudioContext","webkitAudioContext","mozAudioContext","msAudioContext"].forEach(function(item){webAudio.isSupported||item in window&&(webAudio.isSupported=!0,"createMediaStreamSource"in window[item].prototype&&(webAudio.isCreateMediaStreamSourceSupported=!0))}),DetectRTC.isAudioContextSupported=webAudio.isSupported,DetectRTC.isCreateMediaStreamSourceSupported=webAudio.isCreateMediaStreamSourceSupported;var isRtpDataChannelsSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>31&&(isRtpDataChannelsSupported=!0),DetectRTC.isRtpDataChannelsSupported=isRtpDataChannelsSupported;var isSCTPSupportd=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>28?isSCTPSupportd=!0:DetectRTC.browser.isChrome&&DetectRTC.browser.version>25?isSCTPSupportd=!0:DetectRTC.browser.isOpera&&DetectRTC.browser.version>=11&&(isSCTPSupportd=!0),DetectRTC.isSctpDataChannelsSupported=isSCTPSupportd,DetectRTC.isMobileDevice=isMobileDevice;var isGetUserMediaSupported=!1;navigator.getUserMedia?isGetUserMediaSupported=!0:navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&(isGetUserMediaSupported=!0),DetectRTC.browser.isChrome&&DetectRTC.browser.version>=46&&"https:"!==location.protocol&&(DetectRTC.isGetUserMediaSupported="Requires HTTPs"),DetectRTC.isGetUserMediaSupported=isGetUserMediaSupported,DetectRTC.osName=osName,DetectRTC.osVersion=osVersion;var displayResolution="";if(screen.width){var width=screen.width?screen.width:"",height=screen.height?screen.height:"";displayResolution+=""+width+" x "+height}DetectRTC.displayResolution=displayResolution,DetectRTC.isCanvasSupportsStreamCapturing=isCanvasSupportsStreamCapturing,DetectRTC.isVideoSupportsStreamCapturing=isVideoSupportsStreamCapturing,DetectRTC.DetectLocalIPAddress=DetectLocalIPAddress,DetectRTC.isWebSocketsSupported="WebSocket"in window&&2===window.WebSocket.CLOSING,DetectRTC.isWebSocketsBlocked=!DetectRTC.isWebSocketsSupported,DetectRTC.checkWebSocketsSupport=function(callback){callback=callback||function(){};try{var websocket=new WebSocket("wss://echo.websocket.org:443/");websocket.onopen=function(){DetectRTC.isWebSocketsBlocked=!1,callback(),websocket.close(),websocket=null},websocket.onerror=function(){DetectRTC.isWebSocketsBlocked=!0,callback()}}catch(e){DetectRTC.isWebSocketsBlocked=!0,callback()}},DetectRTC.load=function(callback){callback=callback||function(){},checkDeviceSupport(callback)},DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices;var isSetSinkIdSupported=!1;"setSinkId"in document.createElement("video")&&(isSetSinkIdSupported=!0),DetectRTC.isSetSinkIdSupported=isSetSinkIdSupported;var isRTPSenderReplaceTracksSupported=!1;DetectRTC.browser.isFirefox?"getSenders"in mozRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0):DetectRTC.browser.isChrome&&"getSenders"in webkitRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0),DetectRTC.isRTPSenderReplaceTracksSupported=isRTPSenderReplaceTracksSupported;var isRemoteStreamProcessingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>38&&(isRemoteStreamProcessingSupported=!0),DetectRTC.isRemoteStreamProcessingSupported=isRemoteStreamProcessingSupported;var isApplyConstraintsSupported=!1;"undefined"!=typeof MediaStreamTrack&&"applyConstraints"in MediaStreamTrack.prototype&&(isApplyConstraintsSupported=!0),DetectRTC.isApplyConstraintsSupported=isApplyConstraintsSupported;var isMultiMonitorScreenCapturingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=43&&(isMultiMonitorScreenCapturingSupported=!0),DetectRTC.isMultiMonitorScreenCapturingSupported=isMultiMonitorScreenCapturingSupported,window.DetectRTC=DetectRTC}(),function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}(),function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}if(-1!==document.domain.indexOf("webrtc-experiment.com")){window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}}();var TextSender={send:function(config){function sendText(textMessage,text){var data={type:"text",uuid:uuid,sendingTime:sendingTime};textMessage&&(text=textMessage,data.packets=parseInt(text.length/packetSize)),text.length>packetSize?data.message=text.slice(0,packetSize):(data.message=text,data.last=!0,data.isobject=isobject),channel.send(data,remoteUserId),textToTransfer=text.slice(data.message.length),textToTransfer.length&&setTimeout(function(){sendText(null,textToTransfer)},connection.chunkInterval||100)}var connection=config.connection,channel=config.channel,remoteUserId=config.remoteUserId,initialText=config.text,packetSize=connection.chunkSize||1e3,textToTransfer="",isobject=!1;isString(initialText)||(isobject=!0,initialText=JSON.stringify(initialText));var uuid=getRandomString(),sendingTime=(new Date).getTime();sendText(initialText)}},FileProgressBarHandler=function(){function handle(connection){function updateLabel(progress,label){if(-1!==progress.position){var position=+progress.position.toFixed(2).split(".")[1]||100;label.innerHTML=position+"%"}}var progressHelper={};connection.onFileStart=function(file){var div=document.createElement("div");return div.title=file.name,div.innerHTML=" ",file.remoteUserId&&(div.innerHTML+=" (Sharing with:"+file.remoteUserId+")"),connection.filesContainer||(connection.filesContainer=document.body||document.documentElement),connection.filesContainer.insertBefore(div,connection.filesContainer.firstChild),file.remoteUserId?(progressHelper[file.uuid]||(progressHelper[file.uuid]={}),progressHelper[file.uuid][file.remoteUserId]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid][file.remoteUserId].progress.max=file.maxChunks)):(progressHelper[file.uuid]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid].progress.max=file.maxChunks))},connection.onFileProgress=function(chunk){var helper=progressHelper[chunk.uuid];helper&&(!chunk.remoteUserId||(helper=progressHelper[chunk.uuid][chunk.remoteUserId]))&&(helper.progress.value=chunk.currentPosition||chunk.maxChunks||helper.progress.max,updateLabel(helper.progress,helper.label))},connection.onFileEnd=function(file){var helper=progressHelper[file.uuid];if(!helper)return void console.error("No such progress-helper element exists.",file);if(!file.remoteUserId||(helper=progressHelper[file.uuid][file.remoteUserId])){var div=helper.div;-1!=file.type.indexOf("image")?div.innerHTML='Download '+file.name+'
':div.innerHTML='Download '+file.name+'
'}}}return{handle:handle}}(),TranslationHandler=function(){function handle(connection){connection.autoTranslateText=!1,connection.language="en",connection.googKey="AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE",connection.Translator={TranslateText:function(text,callback){var newScript=document.createElement("script");newScript.type="text/javascript";var sourceText=encodeURIComponent(text),randomNumber="method"+connection.token();window[randomNumber]=function(response){response.data&&response.data.translations[0]&&callback&&callback(response.data.translations[0].translatedText),response.error&&"Daily Limit Exceeded"===response.error.message&&(warn('Text translation failed. Error message: "Daily Limit Exceeded."'),callback(text))};var source="https://www.googleapis.com/language/translate/v2?key="+connection.googKey+"&target="+(connection.language||"en-US")+"&callback=window."+randomNumber+"&q="+sourceText;newScript.src=source,document.getElementsByTagName("head")[0].appendChild(newScript)}}}return{handle:handle}}();window.RTCMultiConnection=RTCMultiConnection}(); \ No newline at end of file +}),connection.mediaConstraints.audio.optional=newArray}if(connection.mediaConstraints.video&&connection.mediaConstraints.video.optional.length){var newArray=[];connection.mediaConstraints.video.optional.forEach(function(opt){"undefined"==typeof opt.bandwidth&&newArray.push(opt)}),connection.mediaConstraints.video.optional=newArray}}connection.getExtraData=function(remoteUserId){if(!remoteUserId)throw"remoteUserId is required.";return connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{}},forceOptions.autoOpenOrJoin&&connection.openOrJoin(connection.sessionid)}function SocketConnection(connection,connectCallback){var parameters="";parameters+="?userid="+connection.userid,parameters+="&msgEvent="+connection.socketMessageEvent,parameters+="&socketCustomEvent="+connection.socketCustomEvent,connection.enableScalableBroadcast&&(parameters+="&enableScalableBroadcast=true",parameters+="&maxRelayLimitPerUser="+(connection.maxRelayLimitPerUser||2));var socket;try{socket=io((connection.socketURL||"/")+parameters)}catch(e){socket=io.connect((connection.socketURL||"/")+parameters,connection.socketOptions)}var mPeer=connection.multiPeersHandler;return socket.on("extra-data-updated",function(remoteUserId,extra){connection.peers[remoteUserId]&&(connection.peers[remoteUserId].extra=extra,connection.onExtraDataUpdated({userid:remoteUserId,extra:extra}))}),socket.on(connection.socketMessageEvent,function(message){if(message.remoteUserId==connection.userid){if(connection.peers[message.sender]&&connection.peers[message.sender].extra!=message.extra&&(connection.peers[message.sender].extra=message.extra,connection.onExtraDataUpdated({userid:message.sender,extra:message.extra})),message.message.streamSyncNeeded&&connection.peers[message.sender]){var stream=connection.streamEvents[message.message.streamid];if(!stream||!stream.stream)return;var action=message.message.action;if("ended"===action||"stream-removed"===action)return void connection.onstreamended(stream);var type="both"!=message.message.type?message.message.type:null;return void stream.stream[action](type)}if("connectWithAllParticipants"===message.message)return-1===connection.broadcasters.indexOf(message.sender)&&connection.broadcasters.push(message.sender),void mPeer.onNegotiationNeeded({allParticipants:connection.getAllParticipants(message.sender)},message.sender);if("removeFromBroadcastersList"===message.message)return void(-1!==connection.broadcasters.indexOf(message.sender)&&(delete connection.broadcasters[connection.broadcasters.indexOf(message.sender)],connection.broadcasters=removeNullEntries(connection.broadcasters)));if("dropPeerConnection"===message.message)return void connection.deletePeer(message.sender);if(message.message.allParticipants)return-1===message.message.allParticipants.indexOf(message.sender)&&message.message.allParticipants.push(message.sender),void message.message.allParticipants.forEach(function(participant){mPeer[connection.peers[participant]?"renegotiatePeer":"createNewPeer"](participant,{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})});if(message.message.newParticipant){if(message.message.newParticipant==connection.userid)return;if(connection.peers[message.message.newParticipant])return;return void mPeer.createNewPeer(message.message.newParticipant,message.message.userPreferences||{localPeerSdpConstraints:{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:isData(connection.session)})}if((message.message.readyForOffer||message.message.addMeAsBroadcaster)&&connection.addNewBroadcaster(message.sender),message.message.newParticipationRequest&&message.sender!==connection.userid){connection.peers[message.sender]&&connection.deletePeer(message.sender);var userPreferences={extra:message.extra||{},localPeerSdpConstraints:message.message.remotePeerSdpConstraints||{OfferToReceiveAudio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.sdpConstraints.mandatory.OfferToReceiveVideo},remotePeerSdpConstraints:message.message.localPeerSdpConstraints||{OfferToReceiveAudio:connection.session.oneway?!!connection.session.audio:connection.sdpConstraints.mandatory.OfferToReceiveAudio,OfferToReceiveVideo:connection.session.oneway?!!connection.session.video||!!connection.session.screen:connection.sdpConstraints.mandatory.OfferToReceiveVideo},isOneWay:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,isDataOnly:"undefined"!=typeof message.message.isDataOnly?message.message.isDataOnly:isData(connection.session),dontGetRemoteStream:"undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction,dontAttachLocalStream:!!message.message.dontGetRemoteStream,connectionDescription:message,successCallback:function(){("undefined"!=typeof message.message.isOneWay?message.message.isOneWay:!!connection.session.oneway||"one-way"===connection.direction)&&connection.addNewBroadcaster(message.sender,userPreferences),(connection.session.oneway||"one-way"===connection.direction||isData(connection.session))&&connection.addNewBroadcaster(message.sender,userPreferences)}};return void connection.onNewParticipant(message.sender,userPreferences)}return message.message.shiftedModerationControl?void connection.onShiftedModerationControl(message.sender,message.message.broadcasters):(message.message.changedUUID&&connection.peers[message.message.oldUUID]&&(connection.peers[message.message.newUUID]=connection.peers[message.message.oldUUID],delete connection.peers[message.message.oldUUID]),message.message.userLeft?(mPeer.onUserLeft(message.sender),void(message.message.autoCloseEntireSession&&connection.leave())):void mPeer.addNegotiatedMessage(message.message,message.sender))}}),socket.on("user-left",function(userid){onUserLeft(userid),connection.onUserStatusChanged({userid:userid,status:"offline",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}}),connection.onleave({userid:userid,extra:{}})}),socket.on("connect",function(){connection.enableLogs&&console.info("socket.io connection is opened."),socket.emit("extra-data-updated",connection.extra),connectCallback&&connectCallback(socket)}),socket.on("disconnect",function(){connection.enableLogs&&(console.info("socket.io connection is closed"),console.warn("socket.io reconnecting"))}),socket.on("join-with-password",function(remoteUserId){connection.onJoinWithPassword(remoteUserId)}),socket.on("invalid-password",function(remoteUserId,oldPassword){connection.onInvalidPassword(remoteUserId,oldPassword)}),socket.on("password-max-tries-over",function(remoteUserId){connection.onPasswordMaxTriesOver(remoteUserId)}),socket.on("user-disconnected",function(remoteUserId){remoteUserId!==connection.userid&&(connection.onUserStatusChanged({userid:remoteUserId,status:"offline",extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra||{}:{}}),connection.deletePeer(remoteUserId))}),socket.on("user-connected",function(userid){userid!==connection.userid&&connection.onUserStatusChanged({userid:userid,status:"online",extra:connection.peers[userid]?connection.peers[userid].extra||{}:{}})}),socket.on("logs",function(log){connection.enableLogs&&console.debug("server-logs",log)}),socket}function MultiPeers(connection){function initFileBufferReader(){fbr=new FileBufferReader,fbr.onProgress=function(chunk){connection.onFileProgress(chunk)},fbr.onBegin=function(file){connection.onFileStart(file)},fbr.onEnd=function(file){connection.onFileEnd(file)}}var self=this,skipPeers=["getAllParticipants","getLength","selectFirst","streams","send","forEach"];connection.peers={getLength:function(){var numberOfPeers=0;for(var peer in this)-1==skipPeers.indexOf(peer)&&numberOfPeers++;return numberOfPeers},selectFirst:function(){var firstPeer;for(var peer in this)-1==skipPeers.indexOf(peer)&&(firstPeer=this[peer]);return firstPeer},getAllParticipants:function(sender){var allPeers=[];for(var peer in this)-1==skipPeers.indexOf(peer)&&peer!=sender&&allPeers.push(peer);return allPeers},forEach:function(callbcak){this.getAllParticipants().forEach(function(participant){callbcak(connection.peers[participant])})},send:function(data,remoteUserId){var that=this;if(!isNull(data.size)&&!isNull(data.type))return void self.shareFile(data,remoteUserId);if(!("text"===data.type||data instanceof ArrayBuffer||data instanceof DataView))return void TextSender.send({text:data,channel:this,connection:connection,remoteUserId:remoteUserId});if("text"===data.type&&(data=JSON.stringify(data)),remoteUserId){var remoteUser=connection.peers[remoteUserId];if(remoteUser)return void remoteUser.channels.forEach(function(channel){channel.send(data)})}this.getAllParticipants().forEach(function(participant){that[participant].channels.forEach(function(channel){channel.send(data)})})}},this.uuid=connection.userid,this.getLocalConfig=function(remoteSdp,remoteUserId,userPreferences){return userPreferences||(userPreferences={}),{streamsToShare:userPreferences.streamsToShare||{},rtcMultiConnection:connection,connectionDescription:userPreferences.connectionDescription,remoteUserId:remoteUserId,localPeerSdpConstraints:userPreferences.localPeerSdpConstraints,remotePeerSdpConstraints:userPreferences.remotePeerSdpConstraints,dontGetRemoteStream:!!userPreferences.dontGetRemoteStream,dontAttachLocalStream:!!userPreferences.dontAttachLocalStream,renegotiatingPeer:!!userPreferences.renegotiatingPeer,peerRef:userPreferences.peerRef,onLocalSdp:function(localSdp){self.onNegotiationNeeded(localSdp,remoteUserId)},onLocalCandidate:function(localCandidate){localCandidate=OnIceCandidateHandler.processCandidates(connection,localCandidate),localCandidate&&self.onNegotiationNeeded(localCandidate,remoteUserId)},remoteSdp:remoteSdp,onDataChannelMessage:function(message){if(!fbr&&connection.enableFileSharing&&initFileBufferReader(),"string"==typeof message||!connection.enableFileSharing)return void self.onDataChannelMessage(message,remoteUserId);var that=this;return message instanceof ArrayBuffer||message instanceof DataView?void fbr.convertToObject(message,function(object){that.onDataChannelMessage(object)}):message.readyForNextChunk?void fbr.getNextChunk(message.uuid,function(nextChunk,isLastChunk){connection.peers[remoteUserId].channels.forEach(function(channel){channel.send(nextChunk)})},remoteUserId):void fbr.addChunk(message,function(promptNextChunk){connection.peers[remoteUserId].peer.channel.send(promptNextChunk)})},onDataChannelError:function(error){self.onDataChannelError(error,remoteUserId)},onDataChannelOpened:function(channel){self.onDataChannelOpened(channel,remoteUserId)},onDataChannelClosed:function(event){self.onDataChannelClosed(event,remoteUserId)},onRemoteStream:function(stream){if(connection.peers[remoteUserId].streams.push(stream),isPluginRTC){var mediaElement=document.createElement("video"),body=connection.videosContainer;return body.insertBefore(mediaElement,body.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),self.onGettingRemoteMedia(mediaElement,remoteUserId)},3e3)}self.onGettingRemoteMedia(stream,remoteUserId)},onRemoteStreamRemoved:function(stream){self.onRemovingRemoteMedia(stream,remoteUserId)},onPeerStateChanged:function(states){self.onPeerStateChanged(states),"new"===states.iceConnectionState&&self.onNegotiationStarted(remoteUserId,states),"connected"===states.iceConnectionState&&self.onNegotiationCompleted(remoteUserId,states),-1!==states.iceConnectionState.search(/closed|failed/gi)&&(self.onUserLeft(remoteUserId),self.disconnectWith(remoteUserId))}}},this.createNewPeer=function(remoteUserId,userPreferences){if(!(connection.maxParticipantsAllowed<=connection.getAllParticipants().length)){if(userPreferences=userPreferences||{},!userPreferences.isOneWay&&!userPreferences.isDataOnly)return userPreferences.isOneWay=!0,void this.onNegotiationNeeded({enableMedia:!0,userPreferences:userPreferences},remoteUserId);userPreferences=connection.setUserPreferences(userPreferences,remoteUserId);var localConfig=this.getLocalConfig(null,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)}},this.createAnsweringPeer=function(remoteSdp,remoteUserId,userPreferences){userPreferences=connection.setUserPreferences(userPreferences||{},remoteUserId);var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.renegotiatePeer=function(remoteUserId,userPreferences,remoteSdp){if(!connection.peers[remoteUserId])return void(connection.enableLogs&&console.error("This peer ("+remoteUserId+") does not exists. Renegotiation skipped."));userPreferences||(userPreferences={}),userPreferences.renegotiatingPeer=!0,userPreferences.peerRef=connection.peers[remoteUserId].peer;var localConfig=this.getLocalConfig(remoteSdp,remoteUserId,userPreferences);connection.peers[remoteUserId]=new PeerInitiator(localConfig)},this.replaceTrack=function(track,remoteUserId,isVideoTrack){if(!connection.peers[remoteUserId])throw"This peer ("+remoteUserId+") does not exists.";var peer=connection.peers[remoteUserId].peer;return peer.getSenders&&"function"==typeof peer.getSenders&&peer.getSenders().length?void peer.getSenders().forEach(function(rtpSender){isVideoTrack&&rtpSender.track instanceof VideoStreamTrack&&(connection.peers[remoteUserId].peer.lastVideoTrack=rtpSender.track,rtpSender.replaceTrack(track)),!isVideoTrack&&rtpSender.track instanceof AudioStreamTrack&&(connection.peers[remoteUserId].peer.lastAudioTrack=rtpSender.track,rtpSender.replaceTrack(track))}):(console.warn("RTPSender.replaceTrack is NOT supported."),void this.renegotiatePeer(remoteUserId))},this.onNegotiationNeeded=function(message,remoteUserId){},this.addNegotiatedMessage=function(message,remoteUserId){function invokeGetUserMedia(mediaConstraints){getUserMediaHandler({onGettingLocalMedia:function(localStream){self.onGettingLocalMedia(localStream);var streamsToShare={};connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},onLocalMediaError:function(error,constraints){return self.onLocalMediaError(error,constraints),constraints.audio&&constraints.video&&-1!==(error.name||"").toString().indexOf("DevicesNotFound")?(constraints.video=!1,void invokeGetUserMedia(constraints)):void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)},localMediaConstraints:mediaConstraints})}if(message.type&&message.sdp)return"answer"==message.type&&connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteSdp(message),"offer"==message.type&&(message.renegotiatingPeer?this.renegotiatePeer(remoteUserId,null,message):this.createAnsweringPeer(message,remoteUserId)),void(connection.enableLogs&&console.log("Remote peer's sdp:",message.sdp));if(message.candidate)return connection.peers[remoteUserId]&&connection.peers[remoteUserId].addRemoteCandidate(message),void(connection.enableLogs&&console.log("Remote peer's candidate pairs:",message.candidate));if(message.enableMedia){if(connection.attachStreams.length||connection.dontCaptureUserMedia){var streamsToShare={};return connection.attachStreams.forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),message.userPreferences.streamsToShare=streamsToShare,void self.onNegotiationNeeded({readyForOffer:!0,userPreferences:message.userPreferences},remoteUserId)}var localMediaConstraints={},userPreferences=message.userPreferences;userPreferences.localPeerSdpConstraints.OfferToReceiveAudio&&(localMediaConstraints.audio=connection.mediaConstraints.audio),userPreferences.localPeerSdpConstraints.OfferToReceiveVideo&&(localMediaConstraints.video=connection.mediaConstraints.video),invokeGetUserMedia(localMediaConstraints)}message.readyForOffer&&connection.onReadyForOffer(remoteUserId,message.userPreferences)},this.connectNewParticipantWithAllBroadcasters=function(newParticipantId,userPreferences,broadcastersList){if(broadcastersList=broadcastersList.split("|-,-|"),broadcastersList.length){var firstBroadcaster=broadcastersList[0];self.onNegotiationNeeded({newParticipant:newParticipantId,userPreferences:userPreferences||!1},firstBroadcaster),delete broadcastersList[0];var array=[];broadcastersList.forEach(function(broadcaster){broadcaster&&array.push(broadcaster)}),setTimeout(function(){self.connectNewParticipantWithAllBroadcasters(newParticipantId,userPreferences,array.join("|-,-|"))},1e4)}},this.onGettingRemoteMedia=function(stream,remoteUserId){},this.onRemovingRemoteMedia=function(stream,remoteUserId){},this.onGettingLocalMedia=function(localStream){},this.onLocalMediaError=function(error,constraints){connection.onMediaError(error,constraints)};var fbr;this.shareFile=function(file,remoteUserId){if(!connection.enableFileSharing)throw'"connection.enableFileSharing" is false.';initFileBufferReader(),fbr.readAsArrayBuffer(file,function(uuid){var arrayOfUsers=connection.getAllParticipants();remoteUserId&&(arrayOfUsers=[remoteUserId]),arrayOfUsers.forEach(function(participant){fbr.getNextChunk(uuid,function(nextChunk){connection.peers[participant].channels.forEach(function(channel){channel.send(nextChunk)})},participant)})},{userid:connection.userid,chunkSize:isFirefox?15e3:connection.chunkSize||0})};var textReceiver=new TextReceiver(connection);this.onDataChannelMessage=function(message,remoteUserId){textReceiver.receive(JSON.parse(message),remoteUserId,connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{})},this.onDataChannelClosed=function(event,remoteUserId){event.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onclose(event)},this.onDataChannelError=function(error,remoteUserId){error.userid=remoteUserId,event.extra=connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},connection.onerror(error)},this.onDataChannelOpened=function(channel,remoteUserId){connection.peers[remoteUserId].channels.length||(connection.peers[remoteUserId].channels.push(channel),connection.onopen({userid:remoteUserId,extra:connection.peers[remoteUserId]?connection.peers[remoteUserId].extra:{},channel:channel}),connection.observers.all())},this.onPeerStateChanged=function(state){connection.onPeerStateChanged(state)},this.onNegotiationStarted=function(remoteUserId,states){},this.onNegotiationCompleted=function(remoteUserId,states){},this.getRemoteStreams=function(remoteUserId){return remoteUserId=remoteUserId||connection.peers.getAllParticipants()[0],connection.peers[remoteUserId]?connection.peers[remoteUserId].streams:[]},this.isPluginRTC=connection.isPluginRTC=isPluginRTC}function fireEvent(obj,eventName,args){if("undefined"!=typeof CustomEvent){var eventDetail={arguments:args,__exposedProps__:args},event=new CustomEvent(eventName,eventDetail);obj.dispatchEvent(event)}}function setHarkEvents(connection,streamEvent){if(!connection||!streamEvent)throw"Both arguments are required.";if(connection.onspeaking&&connection.onsilence){if("undefined"==typeof hark)throw"hark.js not found.";hark(streamEvent.stream,{onspeaking:function(){connection.onspeaking(streamEvent)},onsilence:function(){connection.onsilence(streamEvent)},onvolumechange:function(volume,threshold){connection.onvolumechange&&connection.onvolumechange(merge({volume:volume,threshold:threshold},streamEvent))}})}}function setMuteHandlers(connection,streamEvent){streamEvent.stream.addEventListener("mute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.muteType,video:"video"===event.muteType},connection.onmute(event)},!1),streamEvent.stream.addEventListener("unmute",function(event){event=connection.streamEvents[event.target.streamid],event.session={audio:"audio"===event.unmuteType,video:"video"===event.unmuteType},connection.onunmute(event)},!1)}function getRandomString(){if(window.crypto&&window.crypto.getRandomValues&&-1===navigator.userAgent.indexOf("Safari")){for(var a=window.crypto.getRandomValues(new Uint32Array(3)),token="",i=0,l=a.length;l>i;i++)token+=a[i].toString(36);return token}return(Math.random()*(new Date).getTime()).toString(36).replace(/\./g,"")}function getRMCMediaElement(stream,callback,connection){var isAudioOnly=!1;stream.getVideoTracks().length||(isAudioOnly=!0);var mediaElement=document.createElement(isAudioOnly?"audio":"video");return isPluginRTC?(connection.videosContainer.insertBefore(mediaElement,connection.videosContainer.firstChild),void setTimeout(function(){Plugin.attachMediaStream(mediaElement,stream),callback(mediaElement)},1e3)):(mediaElement[isFirefox?"mozSrcObject":"src"]=isFirefox?stream:window.URL.createObjectURL(stream),mediaElement.controls=!0,isFirefox&&mediaElement.addEventListener("ended",function(){if(currentUserMediaRequest.remove(stream.idInstance),"local"===stream.type){StreamsHandler.onSyncNeeded(stream.streamid,"ended"),connection.attachStreams.forEach(function(aStream,idx){stream.streamid===aStream.streamid&&delete connection.attachStreams[idx]});var newStreamsArray=[];connection.attachStreams.forEach(function(aStream){aStream&&newStreamsArray.push(aStream)}),connection.attachStreams=newStreamsArray,connection.observers.all();var streamEvent=connection.streamEvents[stream.streamid];if(streamEvent)return void connection.onstreamended(streamEvent);this.parentNode&&this.parentNode.removeChild(this)}},!1),mediaElement.play(),void callback(mediaElement))}function listenEventHandler(eventName,eventHandler){window.removeEventListener(eventName,eventHandler),window.addEventListener(eventName,eventHandler,!1)}function removeNullEntries(array){var newArray=[];return array.forEach(function(item){item&&newArray.push(item)}),newArray}function isData(session){return!session.audio&&!session.video&&!session.screen&&session.data}function isNull(obj){return"undefined"==typeof obj}function isString(obj){return"string"==typeof obj}function observeObject(obj,callback){Object.observe&&(isMobileDevice||Object.observe(obj,function(changes){var jsonStringified=JSON.stringify(changes);lastChanges!=jsonStringified&&(lastChanges=jsonStringified,callback(changes))}))}function setCordovaAPIs(){if("iOS"===DetectRTC.osName&&"undefined"!=typeof cordova&&"undefined"!=typeof cordova.plugins&&"undefined"!=typeof cordova.plugins.iosrtc){var iosrtc=cordova.plugins.iosrtc;window.webkitRTCPeerConnection=iosrtc.RTCPeerConnection,window.RTCSessionDescription=iosrtc.RTCSessionDescription,window.RTCIceCandidate=iosrtc.RTCIceCandidate,window.MediaStream=iosrtc.MediaStream,window.MediaStreamTrack=iosrtc.MediaStreamTrack,navigator.getUserMedia=navigator.webkitGetUserMedia=iosrtc.getUserMedia,iosrtc.debug.enable("iosrtc*"),iosrtc.registerGlobals()}}function setSdpConstraints(config){var sdpConstraints,sdpConstraints_mandatory={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo};return sdpConstraints={mandatory:sdpConstraints_mandatory,optional:[{VoiceActivityDetection:!1}]},navigator.mozGetUserMedia&&firefoxVersion>34&&(sdpConstraints={OfferToReceiveAudio:!!config.OfferToReceiveAudio,OfferToReceiveVideo:!!config.OfferToReceiveVideo}),sdpConstraints}function onPluginRTCInitialized(pluginRTCObject){Plugin=pluginRTCObject,MediaStreamTrack=Plugin.MediaStreamTrack,RTCPeerConnection=Plugin.RTCPeerConnection,RTCIceCandidate=Plugin.RTCIceCandidate,RTCSessionDescription=Plugin.RTCSessionDescription}function PeerInitiator(config){function createDataChannel(){if(!isOfferer)return void(peer.ondatachannel=function(event){var channel=event.channel;setChannelEvents(channel)});var channel=peer.createDataChannel("RTCDataChannel",{});setChannelEvents(channel)}function setChannelEvents(channel){channel.binaryType="arraybuffer",channel.onmessage=function(event){config.onDataChannelMessage(event.data)},channel.onopen=function(){config.onDataChannelOpened(channel)},channel.onerror=function(error){config.onDataChannelError(error)},channel.onclose=function(event){config.onDataChannelClosed(event)},channel.internalSend=channel.send,channel.send=function(data){"open"===channel.readyState&&channel.internalSend(data)},peer.channel=channel}var connection=config.rtcMultiConnection;this.extra=config.remoteSdp?config.remoteSdp.extra:connection.extra,this.remoteUserId=config.remoteUserId,this.streams=[],this.channels=[],this.connectionDescription=config.connectionDescription;var that=this;config.remoteSdp&&(this.connectionDescription=config.remoteSdp.connectionDescription);var allRemoteStreams={};if(Object.observe){var that=this;Object.observe(this.channels,function(changes){changes.forEach(function(change){"add"===change.type&&change.object[change.name].addEventListener("close",function(){delete that.channels[that.channels.indexOf(change.object[change.name])],that.channels=removeNullEntries(that.channels)},!1),("remove"===change.type||"delete"===change.type)&&-1!==that.channels.indexOf(change.object[change.name])&&delete that.channels.indexOf(change.object[change.name]),that.channels=removeNullEntries(that.channels)})})}defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:!0,OfferToReceiveVideo:!0});var peer,renegotiatingPeer=!!config.renegotiatingPeer;config.remoteSdp&&(renegotiatingPeer=!!config.remoteSdp.renegotiatingPeer);var localStreams=[];connection.attachStreams.forEach(function(stream){stream&&localStreams.push(stream)}),renegotiatingPeer?(peer=config.peerRef,peer.getLocalStreams().forEach(function(stream){localStreams.forEach(function(localStream,index){stream==localStream&&delete localStreams[index]}),connection.removeStreams.forEach(function(streamToRemove,index){stream===streamToRemove&&(stream=connection.beforeRemovingStream(stream),stream&&peer.removeStream&&peer.removeStream(stream),localStreams.forEach(function(localStream,index){streamToRemove==localStream&&delete localStreams[index]}))})})):peer=new RTCPeerConnection(navigator.onLine?{iceServers:connection.iceServers,iceTransports:"all"}:null,connection.optionalArgument),"Firefox"===connection.DetectRTC.browser.name&&(peer.removeStream=function(stream){stream.mute(),connection.StreamsHandler.onSyncNeeded(stream.streamid,"stream-removed")}),peer.onicecandidate=function(event){event.candidate&&config.onLocalCandidate({candidate:event.candidate.candidate,sdpMid:event.candidate.sdpMid,sdpMLineIndex:event.candidate.sdpMLineIndex})};var isFirefoxOffered=!isFirefox;config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.isFirefoxOffered&&(isFirefoxOffered=!0),localStreams.forEach(function(localStream){config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream||config.dontAttachLocalStream||(localStream=connection.beforeAddingStream(localStream),localStream&&peer.addStream(localStream))}),peer.oniceconnectionstatechange=peer.onsignalingstatechange=function(){var extra=that.extra;connection.peers[that.remoteUserId]&&(extra=connection.peers[that.remoteUserId].extra||extra),peer&&config.onPeerStateChanged({iceConnectionState:peer.iceConnectionState,iceGatheringState:peer.iceGatheringState,signalingState:peer.signalingState,extra:extra,userid:that.remoteUserId})};var sdpConstraints={OfferToReceiveAudio:!!localStreams.length,OfferToReceiveVideo:!!localStreams.length};config.localPeerSdpConstraints&&(sdpConstraints=config.localPeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),peer.onaddstream=function(event){var streamsToShare={};config.remoteSdp&&config.remoteSdp.streamsToShare?streamsToShare=config.remoteSdp.streamsToShare:config.streamsToShare&&(streamsToShare=config.streamsToShare);var streamToShare=streamsToShare[event.stream.id];streamToShare&&(event.stream.isAudio=streamToShare.isAudio,event.stream.isVideo=streamToShare.isVideo,event.stream.isScreen=streamToShare.isScreen),event.stream.streamid=event.stream.id,event.stream.stop||(event.stream.stop=function(){isFirefox&&fireEvent(this,"ended")}),allRemoteStreams[event.stream.id]=event.stream,config.onRemoteStream(event.stream)},peer.onremovestream=function(event){event.stream.streamid=event.stream.id,allRemoteStreams[event.stream.id]&&delete allRemoteStreams[event.stream.id],config.onRemoteStreamRemoved(event.stream)},this.addRemoteCandidate=function(remoteCandidate){peer.addIceCandidate(new RTCIceCandidate(remoteCandidate))},this.addRemoteSdp=function(remoteSdp){remoteSdp.sdp=connection.processSdp(remoteSdp.sdp),peer.setRemoteDescription(new RTCSessionDescription(remoteSdp),function(){},function(error){connection.enableLogs&&console.error(JSON.stringify(error,null," "))})};var isOfferer=!0;config.remoteSdp&&(isOfferer=!1),connection.session.data===!0&&createDataChannel(),config.remoteSdp&&(config.remoteSdp.remotePeerSdpConstraints&&(sdpConstraints=config.remoteSdp.remotePeerSdpConstraints),defaults.sdpConstraints=setSdpConstraints(sdpConstraints),this.addRemoteSdp(config.remoteSdp)),("two-way"==connection.session.audio||"two-way"==connection.session.video||"two-way"==connection.session.screen)&&(defaults.sdpConstraints=setSdpConstraints({OfferToReceiveAudio:"two-way"==connection.session.audio||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio,OfferToReceiveVideo:"two-way"==connection.session.video||"two-way"==connection.session.screen||config.remoteSdp&&config.remoteSdp.remotePeerSdpConstraints&&config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio}));var streamsToShare={};peer.getLocalStreams().forEach(function(stream){streamsToShare[stream.streamid]={isAudio:!!stream.isAudio,isVideo:!!stream.isVideo,isScreen:!!stream.isScreen}}),peer[isOfferer?"createOffer":"createAnswer"](function(localSdp){localSdp.sdp=connection.processSdp(localSdp.sdp),peer.setLocalDescription(localSdp),config.onLocalSdp({type:localSdp.type,sdp:localSdp.sdp,remotePeerSdpConstraints:config.remotePeerSdpConstraints||!1,renegotiatingPeer:!!config.renegotiatingPeer||!1,connectionDescription:that.connectionDescription,dontGetRemoteStream:!!config.dontGetRemoteStream,extra:connection?connection.extra:{},streamsToShare:streamsToShare,isFirefoxOffered:isFirefox})},function(error){connection.enableLogs&&console.error("sdp-error",error)},defaults.sdpConstraints),peer.nativeClose=peer.close,peer.close=function(){if(peer){try{-1===peer.iceConnectionState.search(/closed|failed/gi)&&peer.getRemoteStreams().forEach(function(stream){stream.stop()}),peer.nativeClose()}catch(e){}peer=null,that.peer=null}},this.peer=peer}function loadIceFrame(callback,skip){if(!loadedIceFrame){ +if(!skip)return loadIceFrame(callback,!0);loadedIceFrame=!0;var iframe=document.createElement("iframe");iframe.onload=function(){function iFrameLoaderCallback(event){event.data&&event.data.iceServers&&(callback(event.data.iceServers),window.removeEventListener("message",iFrameLoaderCallback))}iframe.isLoaded=!0,listenEventHandler("message",iFrameLoaderCallback),iframe.contentWindow.postMessage("get-ice-servers","*")},iframe.src="https://cdn.webrtc-experiment.com/getIceServers/",iframe.style.display="none",(document.body||document.documentElement).appendChild(iframe)}}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}function setStreamType(constraints,stream){constraints.mandatory&&constraints.mandatory.chromeMediaSource?stream.isScreen=!0:constraints.mozMediaSource||constraints.mediaSource?stream.isScreen=!0:constraints.video?stream.isVideo=!0:constraints.audio&&(stream.isAudio=!0)}function getUserMediaHandler(options){function streaming(stream,returnBack){setStreamType(options.localMediaConstraints,stream),options.onGettingLocalMedia(stream,returnBack),stream.addEventListener("ended",function(){delete currentUserMediaRequest.streams[idInstance],currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.indexOf(options)&&(delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)],currentUserMediaRequest.queueRequests=removeNullEntries(currentUserMediaRequest.queueRequests))},!1),currentUserMediaRequest.streams[idInstance]={stream:stream},currentUserMediaRequest.mutex=!1,currentUserMediaRequest.queueRequests.length&&getUserMediaHandler(currentUserMediaRequest.queueRequests.shift())}if(currentUserMediaRequest.mutex===!0)return void currentUserMediaRequest.queueRequests.push(options);currentUserMediaRequest.mutex=!0;var idInstance=JSON.stringify(options.localMediaConstraints);if(currentUserMediaRequest.streams[idInstance])streaming(currentUserMediaRequest.streams[idInstance].stream,!0);else{if(isPluginRTC){document.createElement("video");return void Plugin.getUserMedia({audio:!0,video:!0},function(stream){stream.streamid=stream.id||getRandomString(),streaming(stream)},function(error){})}navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream){stream.streamid=stream.streamid||stream.id||getRandomString(),stream.idInstance=idInstance,streaming(stream)})["catch"](function(error){options.onLocalMediaError(error,options.localMediaConstraints)})}}function TextReceiver(connection){function receive(data,userid,extra){var uuid=data.uuid;if(content[uuid]||(content[uuid]=[]),content[uuid].push(data.message),data.last){var message=content[uuid].join("");data.isobject&&(message=JSON.parse(message));var receivingTime=(new Date).getTime(),latency=receivingTime-data.sendingTime,e={data:message,userid:userid,extra:extra,latency:latency};connection.autoTranslateText?(e.original=e.data,connection.Translator.TranslateText(e.data,function(translatedText){e.data=translatedText,connection.onmessage(e)})):connection.onmessage(e),delete content[uuid]}}var content={};return{receive:receive}}var isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode,isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);"undefined"!=typeof cordova&&(isMobileDevice=!0,isChrome=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0,isChrome=!0);var isPluginRTC=!isMobileDevice&&(isSafari||isIE),chromeVersion=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),50),matchArray=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);isChrome&&matchArray&&matchArray[2]&&(chromeVersion=parseInt(matchArray[2],10));var firefoxVersion=50;matchArray=navigator.userAgent.match(/Firefox\/(.*)/),isFirefox&&matchArray&&matchArray[1]&&(firefoxVersion=parseInt(matchArray[1],10)),window.addEventListener||(window.addEventListener=function(el,eventName,eventHandler){el.attachEvent&&el.attachEvent("on"+eventName,eventHandler)}),window.attachEventListener=function(video,type,listener,useCapture){video.addEventListener(type,listener,useCapture)};var MediaStream=window.MediaStream;"undefined"==typeof MediaStream&&"undefined"!=typeof webkitMediaStream&&(MediaStream=webkitMediaStream),"undefined"==typeof MediaStream||"stop"in MediaStream.prototype||(MediaStream.prototype.stop=function(){!this.getAudioTracks&&this.getTracks&&(this.getAudioTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("audio")&&array.push(track)}),array}),!this.getVideoTracks&&this.getTracks&&(this.getVideoTracks=function(){var array=[];return this.getTracks.forEach(function(track){-1!==track.kind.toString().indexOf("video")&&array.push(track)}),array}),this.getAudioTracks().forEach(function(track){track.stop&&track.stop()}),this.getVideoTracks().forEach(function(track){track.stop&&track.stop()}),isFirefox&&fireEvent(this,"ended")}),"undefined"!=typeof MediaStream&&("getAudioTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getAudioTracks||(MediaStream.prototype.getAudioTracks=function(){}),"getVideoTracks"in MediaStream.prototype&&"function"==typeof MediaStream.prototype.getVideoTracks||(MediaStream.prototype.getVideoTracks=function(){}));var lastChanges="";!function(){function LoadPluginRTC(){function getPlugin(){return document.getElementById("WebrtcEverywherePluginId")}window.PluginRTC={};var extractPluginObj=function(elt){return elt.isWebRtcPlugin?elt:elt.pluginObj},attachEventListener=function(elt,type,listener,useCapture){var _pluginObj=extractPluginObj(elt);_pluginObj?_pluginObj.bindEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEventListener?elt.addEventListener(type,listener,useCapture):"undefined"!=typeof elt.addEvent&&elt.addEventListener("on"+type,listener,useCapture)},installPlugin=function(){if(!document.getElementById("WebrtcEverywherePluginId")){var pluginObj=document.createElement("object");isIE?pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):pluginObj.setAttribute("type","application/webrtc-everywhere"),pluginObj.setAttribute("id","WebrtcEverywherePluginId"),(document.body||document.documentElement).appendChild(pluginObj),pluginObj.setAttribute("width","0"),pluginObj.setAttribute("height","0")}};document.body?installPlugin():(attachEventListener(window,"load",function(){installPlugin()}),attachEventListener(document,"readystatechange",function(){"complete"==document.readyState&&installPlugin()}));var getUserMediaDelayed;window.PluginRTC.getUserMedia=navigator.getUserMedia=function(constraints,successCallback,errorCallback){"complete"!==document.readyState?getUserMediaDelayed||(getUserMediaDelayed=!0,attachEventListener(document,"readystatechange",function(){getUserMediaDelayed&&"complete"==document.readyState&&(getUserMediaDelayed=!1,getPlugin().getUserMedia(constraints,successCallback,errorCallback))})):getPlugin().getUserMedia(constraints,successCallback,errorCallback)},window.PluginRTC.attachMediaStream=function(element,stream){if(element.isWebRtcPlugin)return element.src=stream,element;if("video"===element.nodeName.toLowerCase()){if(!element.pluginObj&&stream){var _pluginObj=document.createElement("object"),_isIE=Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window;_isIE?_pluginObj.setAttribute("classid","CLSID:7FD49E23-C8D7-4C4F-93A1-F7EACFA1EC53"):_pluginObj.setAttribute("type","application/webrtc-everywhere"),element.pluginObj=_pluginObj,_pluginObj.setAttribute("className",element.className),_pluginObj.setAttribute("innerHTML",element.innerHTML);var width=element.getAttribute("width"),height=element.getAttribute("height"),bounds=element.getBoundingClientRect();if(width||(width=bounds.right-bounds.left),height||(height=bounds.bottom-bounds.top),"getComputedStyle"in window){var computedStyle=window.getComputedStyle(element,null);width||"auto"==computedStyle.width||"0px"==computedStyle.width||(width=computedStyle.width),height||"auto"==computedStyle.height||"0px"==computedStyle.height||(height=computedStyle.height)}width?_pluginObj.setAttribute("width",width):_pluginObj.setAttribute("autowidth",!0),height?_pluginObj.setAttribute("height",height):_pluginObj.setAttribute("autoheight",!0),(document.body||document.documentElement).appendChild(_pluginObj),element.parentNode&&(element.parentNode.replaceChild(_pluginObj,element),document.body.appendChild(element),element.style.visibility="hidden")}return element.pluginObj&&(element.pluginObj.bindEventListener("play",function(objvid){element.pluginObj&&(element.pluginObj.getAttribute("autowidth")&&objvid.videoWidth&&element.pluginObj.setAttribute("width",objvid.videoWidth),element.pluginObj.getAttribute("autoheight")&&objvid.videoHeight&&element.pluginObj.setAttribute("height",objvid.videoHeight))}),element.pluginObj.src=stream),element.pluginObj}return"audio"===element.nodeName.toLowerCase()?element:void 0},window.PluginRTC.MediaStreamTrack={};var getSourcesDelayed;window.PluginRTC.MediaStreamTrack.getSources=function(gotSources){"complete"!==document.readyState?getSourcesDelayed||(getSourcesDelayed=!0,attachEventListener(document,"readystatechange",function(){getSourcesDelayed&&"complete"==document.readyState&&(getSourcesDelayed=!1,getPlugin().getSources(gotSources))})):getPlugin().getSources(gotSources)},window.PluginRTC.RTCPeerConnection=function(configuration,constraints){return getPlugin().createPeerConnection(configuration,constraints)},window.PluginRTC.RTCIceCandidate=function(RTCIceCandidateInit){return getPlugin().createIceCandidate(RTCIceCandidateInit)},window.PluginRTC.RTCSessionDescription=function(RTCSessionDescriptionInit){return getPlugin().createSessionDescription(RTCSessionDescriptionInit)},window.onPluginRTCInitialized&&window.onPluginRTCInitialized(window.PluginRTC)}var ua=navigator.userAgent.toLowerCase(),isSafari=-1!=ua.indexOf("safari")&&-1==ua.indexOf("chrome"),isIE=!!(Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(window,"ActiveXObject")||"ActiveXObject"in window);(isSafari||isIE)&&window.addEventListener("load",LoadPluginRTC,!1)}(),function(){function getBrowserInfo(){var nameOffset,verOffset,ix,nAgt=(navigator.appVersion,navigator.userAgent),browserName=navigator.appName,fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10);if(isOpera){browserName="Opera";try{fullVersion=navigator.userAgent.split("OPR/")[1].split(" ")[0],majorVersion=fullVersion.split(".")[0]}catch(e){fullVersion="0.0.0.0",majorVersion=0}}else isIE?(verOffset=nAgt.indexOf("MSIE"),browserName="IE",fullVersion=nAgt.substring(verOffset+5)):isChrome?(verOffset=nAgt.indexOf("Chrome"),browserName="Chrome",fullVersion=nAgt.substring(verOffset+7)):isSafari?(verOffset=nAgt.indexOf("Safari"),browserName="Safari",fullVersion=nAgt.substring(verOffset+7),-1!==(verOffset=nAgt.indexOf("Version"))&&(fullVersion=nAgt.substring(verOffset+8))):isFirefox?(verOffset=nAgt.indexOf("Firefox"),browserName="Firefox",fullVersion=nAgt.substring(verOffset+8)):(nameOffset=nAgt.lastIndexOf(" ")+1)<(verOffset=nAgt.lastIndexOf("/"))&&(browserName=nAgt.substring(nameOffset,verOffset),fullVersion=nAgt.substring(verOffset+1),browserName.toLowerCase()===browserName.toUpperCase()&&(browserName=navigator.appName));return isEdge&&(browserName="Edge",fullVersion=parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2],10).toString()),-1!==(ix=fullVersion.indexOf(";"))&&(fullVersion=fullVersion.substring(0,ix)),-1!==(ix=fullVersion.indexOf(" "))&&(fullVersion=fullVersion.substring(0,ix)),majorVersion=parseInt(""+fullVersion,10),isNaN(majorVersion)&&(fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10)),{fullVersion:fullVersion,version:majorVersion,name:browserName,isPrivateBrowsing:!1}}function retry(isDone,next){var currentTrial=0,maxRetry=50,isTimeout=!1,id=window.setInterval(function(){isDone()&&(window.clearInterval(id),next(isTimeout)),currentTrial++>maxRetry&&(window.clearInterval(id),isTimeout=!0,next(isTimeout))},10)}function isIE10OrLater(userAgent){var ua=userAgent.toLowerCase();if(0===ua.indexOf("msie")&&0===ua.indexOf("trident"))return!1;var match=/(?:msie|rv:)\s?([\d\.]+)/.exec(ua);return match&&parseInt(match[1],10)>=10?!0:!1}function detectPrivateMode(callback){var isPrivate;if(window.webkitRequestFileSystem)window.webkitRequestFileSystem(window.TEMPORARY,1,function(){isPrivate=!1},function(e){console.log(e),isPrivate=!0});else if(window.indexedDB&&/Firefox/.test(window.navigator.userAgent)){var db;try{db=window.indexedDB.open("test")}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&retry(function(){return"done"===db.readyState?!0:!1},function(isTimeout){isTimeout||(isPrivate=db.result?!1:!0)})}else if(isIE10OrLater(window.navigator.userAgent)){isPrivate=!1;try{window.indexedDB||(isPrivate=!0)}catch(e){isPrivate=!0}}else if(window.localStorage&&/Safari/.test(window.navigator.userAgent)){try{window.localStorage.setItem("test",1)}catch(e){isPrivate=!0}"undefined"==typeof isPrivate&&(isPrivate=!1,window.localStorage.removeItem("test"))}retry(function(){return"undefined"!=typeof isPrivate?!0:!1},function(isTimeout){callback(isPrivate)})}function detectDesktopOS(){var unknown="-",nVer=navigator.appVersion,nAgt=navigator.userAgent,os=unknown,clientStrings=[{s:"Windows 10",r:/(Windows 10.0|Windows NT 10.0)/},{s:"Windows 8.1",r:/(Windows 8.1|Windows NT 6.3)/},{s:"Windows 8",r:/(Windows 8|Windows NT 6.2)/},{s:"Windows 7",r:/(Windows 7|Windows NT 6.1)/},{s:"Windows Vista",r:/Windows NT 6.0/},{s:"Windows Server 2003",r:/Windows NT 5.2/},{s:"Windows XP",r:/(Windows NT 5.1|Windows XP)/},{s:"Windows 2000",r:/(Windows NT 5.0|Windows 2000)/},{s:"Windows ME",r:/(Win 9x 4.90|Windows ME)/},{s:"Windows 98",r:/(Windows 98|Win98)/},{s:"Windows 95",r:/(Windows 95|Win95|Windows_95)/},{s:"Windows NT 4.0",r:/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/},{s:"Windows CE",r:/Windows CE/},{s:"Windows 3.11",r:/Win16/},{s:"Android",r:/Android/},{s:"Open BSD",r:/OpenBSD/},{s:"Sun OS",r:/SunOS/},{s:"Linux",r:/(Linux|X11)/},{s:"iOS",r:/(iPhone|iPad|iPod)/},{s:"Mac OS X",r:/Mac OS X/},{s:"Mac OS",r:/(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},{s:"QNX",r:/QNX/},{s:"UNIX",r:/UNIX/},{s:"BeOS",r:/BeOS/},{s:"OS/2",r:/OS\/2/},{s:"Search Bot",r:/(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/}];for(var id in clientStrings){var cs=clientStrings[id];if(cs.r.test(nAgt)){os=cs.s;break}}var osVersion=unknown;switch(/Windows/.test(os)&&(/Windows (.*)/.test(os)&&(osVersion=/Windows (.*)/.exec(os)[1]),os="Windows"),os){case"Mac OS X":/Mac OS X (10[\.\_\d]+)/.test(nAgt)&&(osVersion=/Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1]);break;case"Android":/Android ([\.\_\d]+)/.test(nAgt)&&(osVersion=/Android ([\.\_\d]+)/.exec(nAgt)[1]);break;case"iOS":/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)&&(osVersion=/OS (\d+)_(\d+)_?(\d+)?/.exec(nVer),osVersion=osVersion[1]+"."+osVersion[2]+"."+(0|osVersion[3]))}return{osName:os,osVersion:osVersion}}function DetectLocalIPAddress(callback){DetectRTC.isWebRTCSupported&&(DetectRTC.isORTCSupported||getIPs(function(ip){callback(ip.match(/^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/)?"Local: "+ip:"Public: "+ip)}))}function getIPs(callback){function handleCandidate(candidate){var ipRegex=/([0-9]{1,3}(\.[0-9]{1,3}){3})/,match=ipRegex.exec(candidate);if(!match)return void console.warn("Could not match IP address in",candidate);var ipAddress=match[1];void 0===ipDuplicates[ipAddress]&&callback(ipAddress),ipDuplicates[ipAddress]=!0}var ipDuplicates={},RTCPeerConnection=window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection,useWebKit=!!window.webkitRTCPeerConnection;if(!RTCPeerConnection){var iframe=document.getElementById("iframe");if(!iframe)throw"NOTE: you need to have an iframe in the page right above the script tag.";var win=iframe.contentWindow;RTCPeerConnection=win.RTCPeerConnection||win.mozRTCPeerConnection||win.webkitRTCPeerConnection,useWebKit=!!win.webkitRTCPeerConnection}if(RTCPeerConnection){var servers,mediaConstraints={optional:[{RtpDataChannels:!0}]};useWebKit&&(servers={iceServers:[{urls:"stun:stun.services.mozilla.com"}]},"undefined"!=typeof DetectRTC&&DetectRTC.browser.isFirefox&&DetectRTC.browser.version<=38&&(servers[0]={url:servers[0].urls}));var pc=new RTCPeerConnection(servers,mediaConstraints);pc.onicecandidate=function(ice){ice.candidate&&handleCandidate(ice.candidate.candidate)},pc.createDataChannel(""),pc.createOffer(function(result){pc.setLocalDescription(result,function(){},function(){})},function(){}),setTimeout(function(){var lines=pc.localDescription.sdp.split("\n");lines.forEach(function(line){0===line.indexOf("a=candidate:")&&handleCandidate(line)})},1e3)}}function checkDeviceSupport(callback){if(canEnumerate){if(!navigator.enumerateDevices&&window.MediaStreamTrack&&window.MediaStreamTrack.getSources&&(navigator.enumerateDevices=window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack)),!navigator.enumerateDevices&&navigator.enumerateDevices&&(navigator.enumerateDevices=navigator.enumerateDevices.bind(navigator)),!navigator.enumerateDevices)return void(callback&&callback());MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[],navigator.enumerateDevices(function(devices){devices.forEach(function(_device){var device={};for(var d in _device)device[d]=_device[d];"audio"===device.kind&&(device.kind="audioinput"),"video"===device.kind&&(device.kind="videoinput");var skip;MediaDevices.forEach(function(d){d.id===device.id&&d.kind===device.kind&&(skip=!0)}),skip||(device.deviceId||(device.deviceId=device.id),device.id||(device.id=device.deviceId),device.label?("videoinput"!==device.kind||isWebsiteHasWebcamPermissions||(isWebsiteHasWebcamPermissions=!0),"audioinput"!==device.kind||isWebsiteHasMicrophonePermissions||(isWebsiteHasMicrophonePermissions=!0)):(device.label="Please invoke getUserMedia once.","https:"!==location.protocol&&document.domain.search&&-1===document.domain.search(/localhost|127.0./g)&&(device.label="HTTPs is required to get label of this "+device.kind+" device.")),"audioinput"===device.kind&&(hasMicrophone=!0,-1===audioInputDevices.indexOf(device)&&audioInputDevices.push(device)),"audiooutput"===device.kind&&(hasSpeakers=!0,-1===audioOutputDevices.indexOf(device)&&audioOutputDevices.push(device)),"videoinput"===device.kind&&(hasWebcam=!0,-1===videoInputDevices.indexOf(device)&&videoInputDevices.push(device)),-1===MediaDevices.indexOf(device)&&MediaDevices.push(device))}),"undefined"!=typeof DetectRTC&&(DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices),callback&&callback()})}}var navigator=window.navigator;"undefined"!=typeof navigator?("undefined"!=typeof navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),"undefined"!=typeof navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia)):navigator={getUserMedia:function(){},userAgent:"Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45"};var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i),isEdge=!(-1===navigator.userAgent.indexOf("Edge")||!navigator.msSaveOrOpenBlob&&!navigator.msSaveBlob),isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isFirefox="undefined"!=typeof window.InstallTrigger,isSafari=Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0,isChrome=!!window.chrome&&!isOpera,isIE=!!document.documentMode&&!isEdge,isMobile={Android:function(){return navigator.userAgent.match(/Android/i)},BlackBerry:function(){return navigator.userAgent.match(/BlackBerry/i)},iOS:function(){return navigator.userAgent.match(/iPhone|iPad|iPod/i)},Opera:function(){return navigator.userAgent.match(/Opera Mini/i)},Windows:function(){return navigator.userAgent.match(/IEMobile/i)},any:function(){return isMobile.Android()||isMobile.BlackBerry()||isMobile.iOS()||isMobile.Opera()||isMobile.Windows()},getOsName:function(){var osName="Unknown OS";return isMobile.Android()&&(osName="Android"),isMobile.BlackBerry()&&(osName="BlackBerry"),isMobile.iOS()&&(osName="iOS"),isMobile.Opera()&&(osName="Opera Mini"),isMobile.Windows()&&(osName="Windows"),osName}},osName="Unknown OS",osVersion="Unknown OS Version";if(isMobile.any())osName=isMobile.getOsName();else{var osInfo=detectDesktopOS();osName=osInfo.osName,osVersion=osInfo.osVersion}var isCanvasSupportsStreamCapturing=!1,isVideoSupportsStreamCapturing=!1;["captureStream","mozCaptureStream","webkitCaptureStream"].forEach(function(item){!isCanvasSupportsStreamCapturing&&item in document.createElement("canvas")&&(isCanvasSupportsStreamCapturing=!0),!isVideoSupportsStreamCapturing&&item in document.createElement("video")&&(isVideoSupportsStreamCapturing=!0)});var MediaDevices=[],audioInputDevices=[],audioOutputDevices=[],videoInputDevices=[];navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(navigator.enumerateDevices=function(callback){navigator.mediaDevices.enumerateDevices().then(callback)});var canEnumerate=!1;"undefined"!=typeof MediaStreamTrack&&"getSources"in MediaStreamTrack?canEnumerate=!0:navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices&&(canEnumerate=!0);var hasMicrophone=!1,hasSpeakers=!1,hasWebcam=!1,isWebsiteHasMicrophonePermissions=!1,isWebsiteHasWebcamPermissions=!1;checkDeviceSupport();var DetectRTC=window.DetectRTC||{};DetectRTC.browser=getBrowserInfo(),detectPrivateMode(function(isPrivateBrowsing){DetectRTC.browser.isPrivateBrowsing=!!isPrivateBrowsing}),DetectRTC.browser["is"+DetectRTC.browser.name]=!0;var isWebRTCSupported=(!!(window.process&&"object"==typeof window.process&&window.process.versions&&window.process.versions["node-webkit"]),!1);["RTCPeerConnection","webkitRTCPeerConnection","mozRTCPeerConnection","RTCIceGatherer"].forEach(function(item){isWebRTCSupported||item in window&&(isWebRTCSupported=!0)}),DetectRTC.isWebRTCSupported=isWebRTCSupported,DetectRTC.isORTCSupported="undefined"!=typeof RTCIceGatherer;var isScreenCapturingSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>=35?isScreenCapturingSupported=!0:DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=34&&(isScreenCapturingSupported=!0),"https:"!==location.protocol&&(isScreenCapturingSupported=!1),DetectRTC.isScreenCapturingSupported=isScreenCapturingSupported;var webAudio={isSupported:!1,isCreateMediaStreamSourceSupported:!1};["AudioContext","webkitAudioContext","mozAudioContext","msAudioContext"].forEach(function(item){webAudio.isSupported||item in window&&(webAudio.isSupported=!0,"createMediaStreamSource"in window[item].prototype&&(webAudio.isCreateMediaStreamSourceSupported=!0))}),DetectRTC.isAudioContextSupported=webAudio.isSupported,DetectRTC.isCreateMediaStreamSourceSupported=webAudio.isCreateMediaStreamSourceSupported;var isRtpDataChannelsSupported=!1;DetectRTC.browser.isChrome&&DetectRTC.browser.version>31&&(isRtpDataChannelsSupported=!0),DetectRTC.isRtpDataChannelsSupported=isRtpDataChannelsSupported;var isSCTPSupportd=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>28?isSCTPSupportd=!0:DetectRTC.browser.isChrome&&DetectRTC.browser.version>25?isSCTPSupportd=!0:DetectRTC.browser.isOpera&&DetectRTC.browser.version>=11&&(isSCTPSupportd=!0),DetectRTC.isSctpDataChannelsSupported=isSCTPSupportd,DetectRTC.isMobileDevice=isMobileDevice;var isGetUserMediaSupported=!1;navigator.getUserMedia?isGetUserMediaSupported=!0:navigator.mediaDevices&&navigator.mediaDevices.getUserMedia&&(isGetUserMediaSupported=!0),DetectRTC.browser.isChrome&&DetectRTC.browser.version>=46&&"https:"!==location.protocol&&(DetectRTC.isGetUserMediaSupported="Requires HTTPs"),DetectRTC.isGetUserMediaSupported=isGetUserMediaSupported,DetectRTC.osName=osName,DetectRTC.osVersion=osVersion;var displayResolution="";if(screen.width){var width=screen.width?screen.width:"",height=screen.height?screen.height:"";displayResolution+=""+width+" x "+height}DetectRTC.displayResolution=displayResolution,DetectRTC.isCanvasSupportsStreamCapturing=isCanvasSupportsStreamCapturing,DetectRTC.isVideoSupportsStreamCapturing=isVideoSupportsStreamCapturing,DetectRTC.DetectLocalIPAddress=DetectLocalIPAddress,DetectRTC.isWebSocketsSupported="WebSocket"in window&&2===window.WebSocket.CLOSING,DetectRTC.isWebSocketsBlocked=!DetectRTC.isWebSocketsSupported,DetectRTC.checkWebSocketsSupport=function(callback){callback=callback||function(){};try{var websocket=new WebSocket("wss://echo.websocket.org:443/");websocket.onopen=function(){DetectRTC.isWebSocketsBlocked=!1,callback(),websocket.close(),websocket=null},websocket.onerror=function(){DetectRTC.isWebSocketsBlocked=!0,callback()}}catch(e){DetectRTC.isWebSocketsBlocked=!0,callback()}},DetectRTC.load=function(callback){callback=callback||function(){},checkDeviceSupport(callback)},DetectRTC.MediaDevices=MediaDevices,DetectRTC.hasMicrophone=hasMicrophone,DetectRTC.hasSpeakers=hasSpeakers,DetectRTC.hasWebcam=hasWebcam,DetectRTC.isWebsiteHasWebcamPermissions=isWebsiteHasWebcamPermissions,DetectRTC.isWebsiteHasMicrophonePermissions=isWebsiteHasMicrophonePermissions,DetectRTC.audioInputDevices=audioInputDevices,DetectRTC.audioOutputDevices=audioOutputDevices,DetectRTC.videoInputDevices=videoInputDevices;var isSetSinkIdSupported=!1;"setSinkId"in document.createElement("video")&&(isSetSinkIdSupported=!0),DetectRTC.isSetSinkIdSupported=isSetSinkIdSupported;var isRTPSenderReplaceTracksSupported=!1;DetectRTC.browser.isFirefox?"getSenders"in mozRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0):DetectRTC.browser.isChrome&&"undefined"!=typeof webkitRTCPeerConnection&&"getSenders"in webkitRTCPeerConnection.prototype&&(isRTPSenderReplaceTracksSupported=!0),DetectRTC.isRTPSenderReplaceTracksSupported=isRTPSenderReplaceTracksSupported;var isRemoteStreamProcessingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>38&&(isRemoteStreamProcessingSupported=!0),DetectRTC.isRemoteStreamProcessingSupported=isRemoteStreamProcessingSupported;var isApplyConstraintsSupported=!1;"undefined"!=typeof MediaStreamTrack&&"applyConstraints"in MediaStreamTrack.prototype&&(isApplyConstraintsSupported=!0),DetectRTC.isApplyConstraintsSupported=isApplyConstraintsSupported;var isMultiMonitorScreenCapturingSupported=!1;DetectRTC.browser.isFirefox&&DetectRTC.browser.version>=43&&(isMultiMonitorScreenCapturingSupported=!0),DetectRTC.isMultiMonitorScreenCapturingSupported=isMultiMonitorScreenCapturingSupported,window.DetectRTC=DetectRTC}(),document.addEventListener("deviceready",setCordovaAPIs,!1),setCordovaAPIs();var RTCPeerConnection,defaults={};"undefined"!=typeof mozRTCPeerConnection?RTCPeerConnection=mozRTCPeerConnection:"undefined"!=typeof webkitRTCPeerConnection?RTCPeerConnection=webkitRTCPeerConnection:"undefined"!=typeof window.RTCPeerConnection?RTCPeerConnection=window.RTCPeerConnection:(console.error("WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser."),RTCPeerConnection=window.RTCSessionDescription=window.RTCIceCandidate=function(){});var RTCSessionDescription=window.RTCSessionDescription||window.mozRTCSessionDescription,RTCIceCandidate=window.RTCIceCandidate||window.mozRTCIceCandidate,MediaStreamTrack=window.MediaStreamTrack,Plugin={};"undefined"!=typeof PluginRTC&&onPluginRTCInitialized(PluginRTC);var CodecsHandler=function(){function removeVPX(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtpmap:100 VP8/90000\r\n",""),sdp=sdp.replace("a=rtpmap:101 VP9/90000\r\n",""),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 100/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF ([0-9 ]*) 101/g,"m=video $1 RTP/SAVPF $2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 100([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace(/m=video ([0-9]+) RTP\/SAVPF 101([0-9 ]*)/g,"m=video $1 RTP/SAVPF$2"),sdp=sdp.replace("a=rtcp-fb:120 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:120 ccm fir\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 nack pli\r\n",""),sdp=sdp.replace("a=rtcp-fb:101 ccm fir\r\n","")}function disableNACK(sdp){if(!sdp||"string"!=typeof sdp)throw"Invalid arguments.";return sdp=sdp.replace("a=rtcp-fb:126 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:126 nack pli\r\n","a=rtcp-fb:126 pli\r\n"),sdp=sdp.replace("a=rtcp-fb:97 nack\r\n",""),sdp=sdp.replace("a=rtcp-fb:97 nack pli\r\n","a=rtcp-fb:97 pli\r\n")}function prioritize(codecMimeType,peer){if(peer&&peer.getSenders&&peer.getSenders().length){if(!codecMimeType||"string"!=typeof codecMimeType)throw"Invalid arguments.";peer.getSenders().forEach(function(sender){for(var params=sender.getParameters(),i=0;ii;++i)if(0===sdpLines[i].indexOf(prefix)&&(!substr||-1!==sdpLines[i].toLowerCase().indexOf(substr.toLowerCase())))return i;return null}function getCodecPayloadType(sdpLine){var pattern=new RegExp("a=rtpmap:(\\d+) \\w+\\/\\d+"),result=sdpLine.match(pattern);return result&&2===result.length?result[1]:null}function setVideoBitrates(sdp,params){if(isMobileDevice)return sdp;params=params||{};var vp8Payload,xgoogle_min_bitrate=params.min,xgoogle_max_bitrate=params.max,sdpLines=sdp.split("\r\n"),vp8Index=findLine(sdpLines,"a=rtpmap","VP8/90000");if(vp8Index&&(vp8Payload=getCodecPayloadType(sdpLines[vp8Index])),!vp8Payload)return sdp;var rtxPayload,rtxIndex=findLine(sdpLines,"a=rtpmap","rtx/90000");if(rtxIndex&&(rtxPayload=getCodecPayloadType(sdpLines[rtxIndex])),!rtxIndex)return sdp;var rtxFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+rtxPayload.toString());if(null!==rtxFmtpLineIndex){var appendrtxNext="\r\n";appendrtxNext+="a=fmtp:"+vp8Payload+" x-google-min-bitrate="+(xgoogle_min_bitrate||"228")+"; x-google-max-bitrate="+(xgoogle_max_bitrate||"228"),sdpLines[rtxFmtpLineIndex]=sdpLines[rtxFmtpLineIndex].concat(appendrtxNext), +sdp=sdpLines.join("\r\n")}return sdp}function setOpusAttributes(sdp,params){if(isMobileDevice)return sdp;params=params||{};var opusPayload,sdpLines=sdp.split("\r\n"),opusIndex=findLine(sdpLines,"a=rtpmap","opus/48000");if(opusIndex&&(opusPayload=getCodecPayloadType(sdpLines[opusIndex])),!opusPayload)return sdp;var opusFmtpLineIndex=findLine(sdpLines,"a=fmtp:"+opusPayload.toString());if(null===opusFmtpLineIndex)return sdp;var appendOpusNext="";return appendOpusNext+="; stereo="+("undefined"!=typeof params.stereo?params.stereo:"1"),appendOpusNext+="; sprop-stereo="+("undefined"!=typeof params["sprop-stereo"]?params["sprop-stereo"]:"1"),"undefined"!=typeof params.maxaveragebitrate&&(appendOpusNext+="; maxaveragebitrate="+(params.maxaveragebitrate||1048576)),"undefined"!=typeof params.maxplaybackrate&&(appendOpusNext+="; maxplaybackrate="+(params.maxplaybackrate||1048576)),"undefined"!=typeof params.cbr&&(appendOpusNext+="; cbr="+("undefined"!=typeof params.cbr?params.cbr:"1")),"undefined"!=typeof params.useinbandfec&&(appendOpusNext+="; useinbandfec="+params.useinbandfec),"undefined"!=typeof params.usedtx&&(appendOpusNext+="; usedtx="+params.usedtx),"undefined"!=typeof params.maxptime&&(appendOpusNext+="\r\na=maxptime:"+params.maxptime),sdpLines[opusFmtpLineIndex]=sdpLines[opusFmtpLineIndex].concat(appendOpusNext),sdp=sdpLines.join("\r\n")}function preferVP9(sdp){return-1===sdp.indexOf("SAVPF 100 101")||-1===sdp.indexOf("VP9/90000")?sdp:sdp.replace("SAVPF 100 101","SAVPF 101 100")}var isMobileDevice=!!navigator.userAgent.match(/Android|iPhone|iPad|iPod|BlackBerry|IEMobile/i);return"undefined"!=typeof cordova&&(isMobileDevice=!0),navigator&&navigator.userAgent&&-1!==navigator.userAgent.indexOf("Crosswalk")&&(isMobileDevice=!0),{removeVPX:removeVPX,disableNACK:disableNACK,prioritize:prioritize,removeNonG722:removeNonG722,setApplicationSpecificBandwidth:function(sdp,bandwidth,isScreen){return setBAS(sdp,bandwidth,isScreen)},setVideoBitrates:function(sdp,params){return setVideoBitrates(sdp,params)},setOpusAttributes:function(sdp,params){return setOpusAttributes(sdp,params)},preferVP9:preferVP9}}();window.BandwidthHandler=CodecsHandler;var loadedIceFrame,OnIceCandidateHandler=function(){function processCandidates(connection,icePair){var candidate=icePair.candidate,iceRestrictions=connection.candidates,stun=iceRestrictions.stun,turn=iceRestrictions.turn;if(isNull(iceRestrictions.reflexive)||(stun=iceRestrictions.reflexive),isNull(iceRestrictions.relay)||(turn=iceRestrictions.relay),(iceRestrictions.host||!candidate.match(/typ host/g))&&(turn||!candidate.match(/typ relay/g))&&(stun||!candidate.match(/typ srflx/g))){var protocol=connection.iceProtocols;if((protocol.udp||!candidate.match(/ udp /g))&&(protocol.tcp||!candidate.match(/ tcp /g)))return connection.enableLogs&&console.debug("Your candidate pairs:",candidate),{candidate:candidate,sdpMid:icePair.sdpMid,sdpMLineIndex:icePair.sdpMLineIndex}}}return{processCandidates:processCandidates}}();"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&loadIceFrame(function(externalIceServers){externalIceServers&&externalIceServers.length&&(window.RMCExternalIceServers=externalIceServers,window.iceServersLoadCallback&&"function"==typeof window.iceServersLoadCallback&&window.iceServersLoadCallback(externalIceServers))});var IceServersHandler=function(){function getIceServers(connection){var iceServers=[];return iceServers.push({urls:"stun:stun.l.google.com:19302"},{urls:"stun:mmt-stun.verkstad.net"},{urls:"stun:stun.anyfirewall.com:3478"}),iceServers.push({urls:"turn:turn.bistri.com:80",credential:"homeo",username:"homeo"}),iceServers.push({urls:"turn:turn.anyfirewall.com:443",credential:"webrtc",username:"webrtc"}),iceServers.push({urls:"turn:mmt-turn.verkstad.net",username:"webrtc",credential:"secret"}),window.RMCExternalIceServers?(iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers):"undefined"!=typeof window.getExternalIceServers&&1==window.getExternalIceServers&&(window.iceServersLoadCallback=function(){iceServers=window.RMCExternalIceServers.concat(iceServers),connection.iceServers=iceServers}),iceServers}return{getIceServers:getIceServers}}(),getUserMedia=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils=window.webrtcUtils||{};if(webrtcUtils.enableLogs||(webrtcUtils.enableLogs=!0),webrtcUtils.log||(webrtcUtils.log=function(){webrtcUtils.enableLogs&&("undefined"!=typeof module||"function"==typeof require&&"function"==typeof define||console.log.apply(console,arguments))}),webrtcUtils.extractVersion||(webrtcUtils.extractVersion=function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos],10)}),"object"==typeof window&&(!window.HTMLMediaElement||"srcObject"in window.HTMLMediaElement.prototype||Object.defineProperty(window.HTMLMediaElement.prototype,"srcObject",{get:function(){return"mozSrcObject"in this?this.mozSrcObject:this._srcObject},set:function(stream){"mozSrcObject"in this?this.mozSrcObject=stream:(this._srcObject=stream,this.src=stream?URL.createObjectURL(stream):null)}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia&&window.mozRTCPeerConnection){if(webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,getUserMedia=function(constraints,onSuccess,onError){var constraintsToFF37=function(c){if("object"!=typeof c||c.require)return c;var require=[];return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r=c[key]="object"==typeof c[key]?c[key]:{ideal:c[key]};if((void 0!==r.min||void 0!==r.max||void 0!==r.exact)&&require.push(key),void 0!==r.exact&&("number"==typeof r.exact?r.min=r.max=r.exact:c[key]=r.exact,delete r.exact),void 0!==r.ideal){c.advanced=c.advanced||[];var oc={};"number"==typeof r.ideal?oc[key]={min:r.ideal,max:r.ideal}:oc[key]=r.ideal,c.advanced.push(oc),delete r.ideal,Object.keys(r).length||delete c[key]}}}),require.length&&(c.require=require),c};return 38>webrtcDetectedVersion&&(webrtcUtils.log("spec: "+JSON.stringify(constraints)),constraints.audio&&(constraints.audio=constraintsToFF37(constraints.audio)),constraints.video&&(constraints.video=constraintsToFF37(constraints.video)),webrtcUtils.log("ff37: "+JSON.stringify(constraints))),navigator.mozGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia,addEventListener:function(){},removeEventListener:function(){}}),navigator.mediaDevices.enumerateDevices=navigator.mediaDevices.enumerateDevices||function(){return new Promise(function(resolve){var infos=[{kind:"audioinput",deviceId:"default",label:"",groupId:""},{kind:"videoinput",deviceId:"default",label:"",groupId:""}];resolve(infos)})},41>webrtcDetectedVersion){var orgEnumerateDevices=navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);navigator.mediaDevices.enumerateDevices=function(){return orgEnumerateDevices().then(void 0,function(e){if("NotFoundError"===e.name)return[];throw e})}}}else if(navigator.webkitGetUserMedia&&window.webkitRTCPeerConnection){webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38;var constraintsToChrome=function(c){if("object"!=typeof c||c.mandatory||c.optional)return c;var cc={};return Object.keys(c).forEach(function(key){if("require"!==key&&"advanced"!==key&&"mediaSource"!==key){var r="object"==typeof c[key]?c[key]:{ideal:c[key]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);var oldname=function(prefix,name){return prefix?prefix+name.charAt(0).toUpperCase()+name.slice(1):"deviceId"===name?"sourceId":name};if(void 0!==r.ideal){cc.optional=cc.optional||[];var oc={};"number"==typeof r.ideal?(oc[oldname("min",key)]=r.ideal,cc.optional.push(oc),oc={},oc[oldname("max",key)]=r.ideal,cc.optional.push(oc)):(oc[oldname("",key)]=r.ideal,cc.optional.push(oc))}void 0!==r.exact&&"number"!=typeof r.exact?(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname("",key)]=r.exact):["min","max"].forEach(function(mix){void 0!==r[mix]&&(cc.mandatory=cc.mandatory||{},cc.mandatory[oldname(mix,key)]=r[mix])})}}),c.advanced&&(cc.optional=(cc.optional||[]).concat(c.advanced)),cc};if(getUserMedia=function(constraints,onSuccess,onError){return constraints.audio&&(constraints.audio=constraintsToChrome(constraints.audio)),constraints.video&&(constraints.video=constraintsToChrome(constraints.video)),webrtcUtils.log("chrome: "+JSON.stringify(constraints)),navigator.webkitGetUserMedia(constraints,onSuccess,onError)},navigator.getUserMedia=getUserMedia,navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:requestUserMedia}),navigator.mediaDevices.getUserMedia){var origGetUserMedia=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=function(c){return webrtcUtils.log("spec: "+JSON.stringify(c)),c.audio=constraintsToChrome(c.audio),c.video=constraintsToChrome(c.video),webrtcUtils.log("chrome: "+JSON.stringify(c)),origGetUserMedia(c)}}else navigator.mediaDevices.getUserMedia=function(constraints){return requestUserMedia(constraints)};"undefined"==typeof navigator.mediaDevices.addEventListener&&(navigator.mediaDevices.addEventListener=function(){webrtcUtils.log("Dummy mediaDevices.addEventListener called.")}),"undefined"==typeof navigator.mediaDevices.removeEventListener&&(navigator.mediaDevices.removeEventListener=function(){webrtcUtils.log("Dummy mediaDevices.removeEventListener called.")})}else navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?(webrtcUtils.log("This appears to be Edge"),webrtcDetectedBrowser="edge",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),webrtcMinimumVersion=12):webrtcUtils.log("Browser does not appear to be WebRTC-capable");else webrtcDetectedBrowser="not a browser";"undefined"!=typeof module?module.exports={getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}:"function"==typeof require&&"function"==typeof define&&define([],function(){return{getUserMedia:getUserMedia,webrtcDetectedBrowser:webrtcDetectedBrowser,webrtcDetectedVersion:webrtcDetectedVersion,webrtcMinimumVersion:webrtcMinimumVersion,webrtcUtils:webrtcUtils}}),"undefined"!=typeof webrtcUtils&&(webrtcUtils.enableLogs=!1);var currentUserMediaRequest={streams:[],mutex:!1,queueRequests:[],remove:function(idInstance){this.mutex=!1;var stream=this.streams[idInstance];if(stream){stream=stream.stream;var options=stream.currentUserMediaRequestOptions;this.queueRequests.indexOf(options)&&(delete this.queueRequests[this.queueRequests.indexOf(options)],this.queueRequests=removeNullEntries(this.queueRequests)),this.streams[idInstance].stream=null,delete this.streams[idInstance]}}},StreamsHandler=function(){function handleType(type){return type?"string"==typeof type||"undefined"==typeof type?type:type.audio&&type.video?null:type.audio?"audio":type.video?"video":void 0:void 0}function setHandlers(stream,syncAction,connection){function graduallyIncreaseVolume(){if(connection.streamEvents[stream.streamid].mediaElement){var mediaElement=connection.streamEvents[stream.streamid].mediaElement;mediaElement.volume=0,afterEach(200,5,function(){mediaElement.volume+=.2})}}("undefined"==typeof syncAction||1==syncAction)&&stream.addEventListener("ended",function(){StreamsHandler.onSyncNeeded(this.streamid,"ended")},!1),stream.mute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!1,connection.streamEvents[stream.streamid].isAudioMuted=!0}),("undefined"==typeof type||"video"==type)&&stream.getVideoTracks().forEach(function(track){track.enabled=!1}),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"mute",type),connection.streamEvents[stream.streamid].muteType=type||"both",fireEvent(stream,"mute",type)},stream.unmute=function(type,isSyncAction){type=handleType(type),"undefined"!=typeof isSyncAction&&(syncAction=isSyncAction),graduallyIncreaseVolume(),("undefined"==typeof type||"audio"==type)&&stream.getAudioTracks().forEach(function(track){track.enabled=!0,connection.streamEvents[stream.streamid].isAudioMuted=!1}),("undefined"==typeof type||"video"==type)&&(stream.getVideoTracks().forEach(function(track){track.enabled=!0}),"undefined"!=typeof type&&"video"==type&&connection.streamEvents[stream.streamid].isAudioMuted&&!function looper(times){times||(times=0),times++,100>times&&connection.streamEvents[stream.streamid].isAudioMuted&&(stream.mute("audio"),setTimeout(function(){looper(times)},50))}()),("undefined"==typeof syncAction||1==syncAction)&&StreamsHandler.onSyncNeeded(stream.streamid,"unmute",type),connection.streamEvents[stream.streamid].unmuteType=type||"both",fireEvent(stream,"unmute",type)}}function afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes){startedTimes=(startedTimes||0)+1,startedTimes>=numberOfTimes||setTimeout(function(){callback(),afterEach(setTimeoutInteval,numberOfTimes,callback,startedTimes)},setTimeoutInteval)}return{setHandlers:setHandlers,onSyncNeeded:function(streamid,action,type){}}}();!function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}(),function(){function getScreenConstraints(error,sourceId){var screen_constraints={audio:!1,video:{mandatory:{chromeMediaSource:error?"screen":"desktop",maxWidth:29999,maxHeight:8640,minFrameRate:30,maxFrameRate:128,minAspectRatio:1.77,googLeakyBucket:!0},optional:[]}};return sourceId&&(screen_constraints.video.mandatory.chromeMediaSourceId=sourceId),screen_constraints}function postMessage(){return iframe?iframe.isLoaded?void iframe.contentWindow.postMessage({captureSourceId:!0},"*"):void setTimeout(postMessage,100):void loadIFrame(postMessage)}function loadIFrame(loadCallback){return iframe?void loadCallback():(iframe=document.createElement("iframe"),iframe.onload=function(){iframe.isLoaded=!0,loadCallback()},iframe.src="https://www.webrtc-experiment.com/getSourceId/",iframe.style.display="none",void(document.body||document.documentElement).appendChild(iframe))}if(-1!==document.domain.indexOf("webrtc-experiment.com")){window.getScreenId=function(callback){function onIFrameCallback(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?callback("permission-denied"):callback(null,event.data.chromeMediaSourceId,getScreenConstraints(null,event.data.chromeMediaSourceId))),event.data.chromeExtensionStatus&&callback(event.data.chromeExtensionStatus,null,getScreenConstraints(event.data.chromeExtensionStatus)),window.removeEventListener("message",onIFrameCallback))}return navigator.mozGetUserMedia?void callback(null,"firefox",{video:{mozMediaSource:"window",mediaSource:"window",width:29999,height:8640}}):(postMessage(),void window.addEventListener("message",onIFrameCallback))};var iframe;window.getScreenConstraints=function(callback){loadIFrame(function(){getScreenId(function(error,sourceId,screen_constraints){callback(error,screen_constraints.video)})})}}}();var TextSender={send:function(config){function sendText(textMessage,text){var data={type:"text",uuid:uuid,sendingTime:sendingTime};textMessage&&(text=textMessage,data.packets=parseInt(text.length/packetSize)),text.length>packetSize?data.message=text.slice(0,packetSize):(data.message=text,data.last=!0,data.isobject=isobject),channel.send(data,remoteUserId),textToTransfer=text.slice(data.message.length),textToTransfer.length&&setTimeout(function(){sendText(null,textToTransfer)},connection.chunkInterval||100)}var connection=config.connection,channel=config.channel,remoteUserId=config.remoteUserId,initialText=config.text,packetSize=connection.chunkSize||1e3,textToTransfer="",isobject=!1;isString(initialText)||(isobject=!0,initialText=JSON.stringify(initialText));var uuid=getRandomString(),sendingTime=(new Date).getTime();sendText(initialText)}},FileProgressBarHandler=function(){function handle(connection){function updateLabel(progress,label){if(-1!==progress.position){var position=+progress.position.toFixed(2).split(".")[1]||100;label.innerHTML=position+"%"}}var progressHelper={};connection.onFileStart=function(file){var div=document.createElement("div");return div.title=file.name,div.innerHTML=" ",file.remoteUserId&&(div.innerHTML+=" (Sharing with:"+file.remoteUserId+")"),connection.filesContainer||(connection.filesContainer=document.body||document.documentElement),connection.filesContainer.insertBefore(div,connection.filesContainer.firstChild),file.remoteUserId?(progressHelper[file.uuid]||(progressHelper[file.uuid]={}),progressHelper[file.uuid][file.remoteUserId]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid][file.remoteUserId].progress.max=file.maxChunks)):(progressHelper[file.uuid]={div:div,progress:div.querySelector("progress"),label:div.querySelector("label")},void(progressHelper[file.uuid].progress.max=file.maxChunks))},connection.onFileProgress=function(chunk){var helper=progressHelper[chunk.uuid];helper&&(!chunk.remoteUserId||(helper=progressHelper[chunk.uuid][chunk.remoteUserId]))&&(helper.progress.value=chunk.currentPosition||chunk.maxChunks||helper.progress.max,updateLabel(helper.progress,helper.label))},connection.onFileEnd=function(file){var helper=progressHelper[file.uuid];if(!helper)return void console.error("No such progress-helper element exists.",file);if(!file.remoteUserId||(helper=progressHelper[file.uuid][file.remoteUserId])){var div=helper.div;-1!=file.type.indexOf("image")?div.innerHTML='Download '+file.name+'
':div.innerHTML='Download '+file.name+'
'}}}return{handle:handle}}(),TranslationHandler=function(){function handle(connection){connection.autoTranslateText=!1,connection.language="en",connection.googKey="AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE",connection.Translator={TranslateText:function(text,callback){var newScript=document.createElement("script");newScript.type="text/javascript";var sourceText=encodeURIComponent(text),randomNumber="method"+connection.token();window[randomNumber]=function(response){response.data&&response.data.translations[0]&&callback&&callback(response.data.translations[0].translatedText),response.error&&"Daily Limit Exceeded"===response.error.message&&(warn('Text translation failed. Error message: "Daily Limit Exceeded."'),callback(text))};var source="https://www.googleapis.com/language/translate/v2?key="+connection.googKey+"&target="+(connection.language||"en-US")+"&callback=window."+randomNumber+"&q="+sourceText;newScript.src=source,document.getElementsByTagName("head")[0].appendChild(newScript)}}}return{handle:handle}}();window.RTCMultiConnection=RTCMultiConnection}(); \ No newline at end of file diff --git a/package.json b/package.json index 18dc9ea3..1181ec3e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rtcmulticonnection-v3", "preferGlobal": false, - "version": "3.2.92", + "version": "3.2.93", "author": { "name": "Muaz Khan", "email": "muazkh@gmail.com", @@ -50,14 +50,15 @@ "_from": "rtcmulticonnection-v3@", "devDependencies": { "grunt": "0.4.5", + "grunt-bump": "0.7.0", "grunt-cli": "0.1.13", - "load-grunt-tasks": "3.4.0", + "grunt-contrib-clean": "0.6.0", "grunt-contrib-concat": "0.5.1", + "grunt-contrib-copy": "0.8.2", "grunt-contrib-uglify": "0.11.0", "grunt-jsbeautifier": "0.2.10", - "grunt-bump": "0.7.0", - "grunt-contrib-clean": "0.6.0", - "grunt-contrib-copy": "0.8.2", - "grunt-replace": "0.11.0" + "grunt-replace": "0.11.0", + "load-grunt-tasks": "3.4.0", + "socket.io": "^0.9.17" } }