diff --git a/Gruntfile.js b/Gruntfile.js
index 61060a3..9dea07d 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -115,13 +115,13 @@ module.exports = function(grunt) {
},
includereplace: {
- production: {
+ withGoogle: {
options: {
// Task-specific options go here.
- prefix: '@@',
+ prefix: '@Goo@',
includesDir: '.',
processIncludeContents: function (includeContents, localVars, filePath) {
- if (filePath.indexOf(grunt.config.get('googleAdapterPath')) != -1) {
+ if (filePath.indexOf(grunt.config.get('googleAdapterPath')) !== -1) {
// Indent file and indent Google's exports
return includeContents
// Comment export
@@ -141,7 +141,20 @@ module.exports = function(grunt) {
],
// Destination directory to copy files to
dest: './'
- }
+ },
+ withPluginInfo: {
+ options: {
+ // Task-specific options go here.
+ prefix: '@Tem@',
+ includesDir: '<%= pluginInfoRoot %>/',
+ },
+ // Files to perform replacements and includes with
+ src: [
+ '<%= production %>/*.js',
+ ],
+ // Destination directory to copy files to
+ dest: './'
+ },
},
jshint: {
@@ -159,7 +172,8 @@ module.exports = function(grunt) {
node: true
}, grunt.file.readJSON('.jshintrc')),
src: [
- 'tests/*_test.js'
+ 'tests/*.js',
+ 'tests/unit/*.js'
]
},
app: {
@@ -279,6 +293,13 @@ module.exports = function(grunt) {
grunt.log.writeln('bamboo/vars file successfully created');
});
+ grunt.registerTask('CheckSubmodules', 'Checks that third_party/adapter is properly checked out', function() {
+ if(!grunt.file.exists(grunt.config.get('googleAdapterPath'))) {
+ grunt.fail.fatal('Couldn\'t find ' + grunt.config.get('googleAdapterPath') + '\n' +
+ 'Output would be incomplete. Did you remember to initialize submodules?\nPlease run: git submodule update --init');
+ }
+ });
+
grunt.registerTask('CheckPluginInfo', 'Checks for existing config file', function() {
var fullPath = grunt.config.get('pluginInfoRoot') + '/' + grunt.config.get('pluginInfoFile');
grunt.verbose.writeln('Checking that the plugin info file exists.');
@@ -290,18 +311,24 @@ module.exports = function(grunt) {
}
});
- grunt.registerTask('dev', [
- 'CheckPluginInfo',
- 'versionise',
- 'clean:production',
- 'concat',
- 'replace:production',
- 'includereplace:production',
- 'uglify'
- ]);
+ // NOTE(J-O) Prep for webrtc-adapter 0.2.10, will need to be compiled
+ grunt.registerTask('webrtc-adapter', 'Build the webrtc-adapter submodule', function() {
+ grunt.verbose.writeln('Spawning child process to compile webrtc-adapter subgrunt.');
+ var done = this.async();
+ var child = grunt.util.spawn({
+ grunt: true,
+ args: ['--gruntfile', './third_party/adapter/Gruntfile.js', 'build'],
+ opts: {stdio: 'inherit'},
+ }, function(error, result) {});
+ child.on('close', function (code) {
+ done(code === 0);
+ });
+ });
grunt.registerTask('publish', [
+ 'CheckSubmodules',
'CheckPluginInfo',
+ // 'webrtc-adapter',
'versionise',
'clean:production',
'concat',
diff --git a/bower.json b/bower.json
index 5433cfe..bf83468 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
"name": "adapterjs",
"description": "Creating a common API for WebRTC in the browser",
- "version": "0.13.0",
+ "version": "0.13.1",
"homepage": "https://temasys.github.io/",
"author": {
"name": "Temasys Communications Pte. Ltd.",
diff --git a/package.json b/package.json
index 6db0089..d10899a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "adapterjs",
"description": "Creating a common API for WebRTC in the browser",
- "version": "0.13.0",
+ "version": "0.13.1",
"homepage": "https://temasys.github.io/",
"author": {
"name": "Temasys Communications Pte. Ltd.",
diff --git a/publish/adapter.debug.js b/publish/adapter.debug.js
index b2ab827..4cfed27 100644
--- a/publish/adapter.debug.js
+++ b/publish/adapter.debug.js
@@ -1,4 +1,4 @@
-/*! adapterjs - v0.13.0 - 2016-01-08 */
+/*! adapterjs - v0.13.1 - 2016-03-15 */
// Adapter's interface.
var AdapterJS = AdapterJS || {};
@@ -17,7 +17,7 @@ AdapterJS.options = AdapterJS.options || {};
// AdapterJS.options.hidePluginInstallPrompt = true;
// AdapterJS version
-AdapterJS.VERSION = '0.13.0';
+AdapterJS.VERSION = '0.13.1';
// This function will be called when the WebRTC API is ready to be used
// Whether it is the native implementation (Chrome, Firefox, Opera) or
@@ -58,6 +58,7 @@ AdapterJS.webRTCReady = function (callback) {
AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {};
// The object to store plugin information
+/* jshint ignore:start */
AdapterJS.WebRTCPlugin.pluginInfo = {
prefix : 'Tem',
plugName : 'TemWebRTCPlugin',
@@ -74,6 +75,7 @@ if(!!navigator.platform.match(/^Mac/i)) {
else if(!!navigator.platform.match(/^Win/i)) {
AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN';
}
+/* jshint ignore:end */
AdapterJS.WebRTCPlugin.TAGS = {
NONE : 'none',
@@ -160,16 +162,14 @@ AdapterJS.WebRTCPlugin.callWhenPluginReady = null;
// This function is the only private function that is not encapsulated to
// allow the plugin method to be called.
__TemWebRTCReady0 = function () {
- webrtcDetectedVersion = AdapterJS.WebRTCPlugin.plugin.version;
-
if (document.readyState === 'complete') {
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
AdapterJS.maybeThroughWebRTCReady();
} else {
- AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () {
+ var timer = setInterval(function () {
if (document.readyState === 'complete') {
// TODO: update comments, we wait for the document to be ready
- clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval);
+ clearInterval(timer);
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
AdapterJS.maybeThroughWebRTCReady();
}
@@ -240,65 +240,62 @@ AdapterJS.isDefined = null;
// This sets:
// - webrtcDetectedBrowser: The browser agent name.
// - webrtcDetectedVersion: The browser version.
+// - webrtcMinimumVersion: The minimum browser version still supported by AJS.
// - webrtcDetectedType: The types of webRTC support.
// - 'moz': Mozilla implementation of webRTC.
// - 'webkit': WebKit implementation of webRTC.
// - 'plugin': Using the plugin implementation.
AdapterJS.parseWebrtcDetectedBrowser = function () {
- var hasMatch, checkMatch = navigator.userAgent.match(
- /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
- if (/trident/i.test(checkMatch[1])) {
- hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ var hasMatch = null;
+ if ((!!window.opr && !!opr.addons) ||
+ !!window.opera ||
+ navigator.userAgent.indexOf(' OPR/') >= 0) {
+ // Opera 8.0+
+ webrtcDetectedBrowser = 'opera';
+ webrtcDetectedType = 'webkit';
+ webrtcMinimumVersion = 26;
+ hasMatch = /OPR\/(\d+)/i.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ } else if (typeof InstallTrigger !== 'undefined') {
+ // Firefox 1.0+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = 'moz';
+ } else if (Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
+ // Safari
+ webrtcDetectedBrowser = 'safari';
+ webrtcDetectedType = 'plugin';
+ webrtcMinimumVersion = 7;
+ hasMatch = /version\/(\d+)/i.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ } else if (/*@cc_on!@*/false || !!document.documentMode) {
+ // Internet Explorer 6-11
webrtcDetectedBrowser = 'IE';
+ webrtcDetectedType = 'plugin';
+ webrtcMinimumVersion = 9;
+ hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
- } else if (checkMatch[1] === 'Chrome') {
- hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/);
- if (hasMatch !== null) {
- webrtcDetectedBrowser = 'opera';
- webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ if (!webrtcDetectedVersion) {
+ hasMatch = /\bMSIE[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
}
+ } else if (!!window.StyleMedia) {
+ // Edge 20+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = '';
+ } else if (!!window.chrome && !!window.chrome.webstore) {
+ // Chrome 1+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = 'webkit';
+ } else if ((webrtcDetectedBrowser === 'chrome'|| webrtcDetectedBrowser === 'opera') &&
+ !!window.CSS) {
+ // Blink engine detection
+ webrtcDetectedBrowser = 'blink';
+ // TODO: detected WebRTC version
}
- if (navigator.userAgent.indexOf('Safari')) {
- if (typeof InstallTrigger !== 'undefined') {
- webrtcDetectedBrowser = 'firefox';
- } else if (/*@cc_on!@*/ false || !!document.documentMode) {
- webrtcDetectedBrowser = 'IE';
- } else if (
- Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
- webrtcDetectedBrowser = 'safari';
- } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
- webrtcDetectedBrowser = 'opera';
- } else if (!!window.chrome) {
- webrtcDetectedBrowser = 'chrome';
- }
- }
- if (!webrtcDetectedBrowser) {
- webrtcDetectedVersion = checkMatch[1];
- }
- if (!webrtcDetectedVersion) {
- try {
- checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] :
- [navigator.appName, navigator.appVersion, '-?'];
- if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) {
- checkMatch.splice(1, 1, hasMatch[1]);
- }
- webrtcDetectedVersion = parseInt(checkMatch[1], 10);
- } catch (error) { }
- }
-};
-// To fix configuration as some browsers does not support
-// the 'urls' attribute.
-AdapterJS.maybeFixConfiguration = function (pcConfig) {
- if (pcConfig === null) {
- return;
- }
- for (var i = 0; i < pcConfig.iceServers.length; i++) {
- if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
- pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
- delete pcConfig.iceServers[i].urls;
- }
- }
+ window.webrtcDetectedBrowser = webrtcDetectedBrowser;
+ window.webrtcDetectedVersion = webrtcDetectedVersion;
+ window.webrtcMinimumVersion = webrtcMinimumVersion;
};
AdapterJS.addEvent = function(elem, evnt, func) {
@@ -336,7 +333,7 @@ AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNe
i.style.transition = 'all .5s ease-out';
}
document.body.appendChild(i);
- c = (i.contentWindow) ? i.contentWindow :
+ var c = (i.contentWindow) ? i.contentWindow :
(i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
c.document.open();
c.document.write(' -1) {
+ parts.attribute = line.substr(sp + 1, colon - sp - 1);
+ parts.value = line.substr(colon + 1);
+ } else {
+ parts.attribute = line.substr(sp + 1);
+ }
+ return parts;
+ };
+
+ // Extracts DTLS parameters from SDP media section or sessionpart.
+ // FIXME: for consistency with other functions this should only
+ // get the fingerprint line as input. See also getIceParameters.
+ SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
+ var lines = SDPUtils.splitLines(mediaSection);
+ lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
+ var fpLine = lines.filter(function(line) {
+ return line.indexOf('a=fingerprint:') === 0;
+ })[0].substr(14);
+ // Note: a=setup line is ignored since we use the 'auto' role.
+ var dtlsParameters = {
+ role: 'auto',
+ fingerprints: [{
+ algorithm: fpLine.split(' ')[0],
+ value: fpLine.split(' ')[1]
+ }]
+ };
+ return dtlsParameters;
+ };
+
+ // Serializes DTLS parameters to SDP.
+ SDPUtils.writeDtlsParameters = function(params, setupType) {
+ var sdp = 'a=setup:' + setupType + '\r\n';
+ params.fingerprints.forEach(function(fp) {
+ sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
+ });
+ return sdp;
+ };
+ // Parses ICE information from SDP media section or sessionpart.
+ // FIXME: for consistency with other functions this should only
+ // get the ice-ufrag and ice-pwd lines as input.
+ SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
+ var lines = SDPUtils.splitLines(mediaSection);
+ lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
+ var iceParameters = {
+ usernameFragment: lines.filter(function(line) {
+ return line.indexOf('a=ice-ufrag:') === 0;
+ })[0].substr(12),
+ password: lines.filter(function(line) {
+ return line.indexOf('a=ice-pwd:') === 0;
+ })[0].substr(10)
+ };
+ return iceParameters;
+ };
+
+ // Serializes ICE parameters to SDP.
+ SDPUtils.writeIceParameters = function(params) {
+ return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
+ 'a=ice-pwd:' + params.password + '\r\n';
+ };
+
+ // Parses the SDP media section and returns RTCRtpParameters.
+ SDPUtils.parseRtpParameters = function(mediaSection) {
+ var description = {
+ codecs: [],
+ headerExtensions: [],
+ fecMechanisms: [],
+ rtcp: []
+ };
+ var lines = SDPUtils.splitLines(mediaSection);
+ var mline = lines[0].split(' ');
+ for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
+ var pt = mline[i];
+ var rtpmapline = SDPUtils.matchPrefix(
+ mediaSection, 'a=rtpmap:' + pt + ' ')[0];
+ if (rtpmapline) {
+ var codec = SDPUtils.parseRtpMap(rtpmapline);
+ var fmtps = SDPUtils.matchPrefix(
+ mediaSection, 'a=fmtp:' + pt + ' ');
+ // Only the first a=fmtp: is considered.
+ codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
+ codec.rtcpFeedback = SDPUtils.matchPrefix(
+ mediaSection, 'a=rtcp-fb:' + pt + ' ')
+ .map(SDPUtils.parseRtcpFb);
+ description.codecs.push(codec);
+ }
+ }
+ // FIXME: parse headerExtensions, fecMechanisms and rtcp.
+ return description;
+ };
+
+ // Generates parts of the SDP media section describing the capabilities / parameters.
+ SDPUtils.writeRtpDescription = function(kind, caps) {
+ var sdp = '';
+
+ // Build the mline.
+ sdp += 'm=' + kind + ' ';
+ sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
+ sdp += ' UDP/TLS/RTP/SAVPF ';
+ sdp += caps.codecs.map(function(codec) {
+ if (codec.preferredPayloadType !== undefined) {
+ return codec.preferredPayloadType;
+ }
+ return codec.payloadType;
+ }).join(' ') + '\r\n';
+
+ sdp += 'c=IN IP4 0.0.0.0\r\n';
+ sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
+
+ // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
+ caps.codecs.forEach(function(codec) {
+ sdp += SDPUtils.writeRtpMap(codec);
+ sdp += SDPUtils.writeFtmp(codec);
+ sdp += SDPUtils.writeRtcpFb(codec);
+ });
+ // FIXME: add headerExtensions, fecMechanismÅŸ and rtcp.
+ sdp += 'a=rtcp-mux\r\n';
+ return sdp;
+ };
+
+ SDPUtils.writeSessionBoilerplate = function() {
+ // FIXME: sess-id should be an NTP timestamp.
+ return 'v=0\r\n' +
+ 'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' +
+ 's=-\r\n' +
+ 't=0 0\r\n';
+ };
+
+ SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
+ var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
+
+ // Map ICE parameters (ufrag, pwd) to SDP.
+ sdp += SDPUtils.writeIceParameters(
+ transceiver.iceGatherer.getLocalParameters());
+
+ // Map DTLS parameters to SDP.
+ sdp += SDPUtils.writeDtlsParameters(
+ transceiver.dtlsTransport.getLocalParameters(),
+ type === 'offer' ? 'actpass' : 'active');
+
+ sdp += 'a=mid:' + transceiver.mid + '\r\n';
+
+ if (transceiver.rtpSender && transceiver.rtpReceiver) {
+ sdp += 'a=sendrecv\r\n';
+ } else if (transceiver.rtpSender) {
+ sdp += 'a=sendonly\r\n';
+ } else if (transceiver.rtpReceiver) {
+ sdp += 'a=recvonly\r\n';
+ } else {
+ sdp += 'a=inactive\r\n';
+ }
+
+ // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
+ if (transceiver.rtpSender) {
+ var msid = 'msid:' + stream.id + ' ' +
+ transceiver.rtpSender.track.id + '\r\n';
+ sdp += 'a=' + msid;
+ sdp += 'a=ssrc:' + transceiver.sendSsrc + ' ' + msid;
+ }
+ // FIXME: this should be written by writeRtpDescription.
+ sdp += 'a=ssrc:' + transceiver.sendSsrc + ' cname:' +
+ localCName + '\r\n';
+ return sdp;
+ };
+
+ // Gets the direction from the mediaSection or the sessionpart.
+ SDPUtils.getDirection = function(mediaSection, sessionpart) {
+ // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
+ var lines = SDPUtils.splitLines(mediaSection);
+ for (var i = 0; i < lines.length; i++) {
+ switch (lines[i]) {
+ case 'a=sendrecv':
+ case 'a=sendonly':
+ case 'a=recvonly':
+ case 'a=inactive':
+ return lines[i].substr(2);
+ }
+ }
+ if (sessionpart) {
+ return SDPUtils.getDirection(sessionpart);
+ }
+ return 'sendrecv';
+ };
+
+ // ORTC defines an RTCIceCandidate object but no constructor.
+ // Not implemented in Edge.
+ if (!window.RTCIceCandidate) {
+ window.RTCIceCandidate = function(args) {
+ return args;
+ };
+ }
+ // ORTC does not have a session description object but
+ // other browsers (i.e. Chrome) that will support both PC and ORTC
+ // in the future might have this defined already.
+ if (!window.RTCSessionDescription) {
+ window.RTCSessionDescription = function(args) {
+ return args;
+ };
+ }
+
+ window.RTCPeerConnection = function(config) {
+ var self = this;
+
+ this.onicecandidate = null;
+ this.onaddstream = null;
+ this.onremovestream = null;
+ this.onsignalingstatechange = null;
+ this.oniceconnectionstatechange = null;
+ this.onnegotiationneeded = null;
+ this.ondatachannel = null;
+
+ this.localStreams = [];
+ this.remoteStreams = [];
+ this.getLocalStreams = function() { return self.localStreams; };
+ this.getRemoteStreams = function() { return self.remoteStreams; };
+
+ this.localDescription = new RTCSessionDescription({
+ type: '',
+ sdp: ''
+ });
+ this.remoteDescription = new RTCSessionDescription({
+ type: '',
+ sdp: ''
+ });
+ this.signalingState = 'stable';
+ this.iceConnectionState = 'new';
+
+ this.iceOptions = {
+ gatherPolicy: 'all',
+ iceServers: []
+ };
+ if (config && config.iceTransportPolicy) {
+ switch (config.iceTransportPolicy) {
+ case 'all':
+ case 'relay':
+ this.iceOptions.gatherPolicy = config.iceTransportPolicy;
+ break;
+ case 'none':
+ // FIXME: remove once implementation and spec have added this.
+ throw new TypeError('iceTransportPolicy "none" not supported');
+ }
+ }
+ if (config && config.iceServers) {
+ // Edge does not like
+ // 1) stun:
+ // 2) turn: that does not have all of turn:host:port?transport=udp
+ // 3) an array of urls
+ config.iceServers.forEach(function(server) {
+ if (server.urls) {
+ var url;
+ if (typeof(server.urls) === 'string') {
+ url = server.urls;
+ } else {
+ url = server.urls[0];
+ }
+ if (url.indexOf('transport=udp') !== -1) {
+ self.iceServers.push({
+ username: server.username,
+ credential: server.credential,
+ urls: url
+ });
+ }
+ }
+ });
+ }
+
+ // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
+ // everything that is needed to describe a SDP m-line.
+ this.transceivers = [];
+
+ // since the iceGatherer is currently created in createOffer but we
+ // must not emit candidates until after setLocalDescription we buffer
+ // them in this array.
+ this._localIceCandidatesBuffer = [];
+ };
+
+ window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {
+ var self = this;
+ // FIXME: need to apply ice candidates in a way which is async but in-order
+ this._localIceCandidatesBuffer.forEach(function(event) {
+ if (self.onicecandidate !== null) {
+ self.onicecandidate(event);
+ }
+ });
+ this._localIceCandidatesBuffer = [];
+ };
+
+ window.RTCPeerConnection.prototype.addStream = function(stream) {
+ // Clone is necessary for local demos mostly, attaching directly
+ // to two different senders does not work (build 10547).
+ this.localStreams.push(stream.clone());
+ this._maybeFireNegotiationNeeded();
+ };
+
+ window.RTCPeerConnection.prototype.removeStream = function(stream) {
+ var idx = this.localStreams.indexOf(stream);
+ if (idx > -1) {
+ this.localStreams.splice(idx, 1);
+ this._maybeFireNegotiationNeeded();
+ }
+ };
+
+ // Determines the intersection of local and remote capabilities.
+ window.RTCPeerConnection.prototype._getCommonCapabilities =
+ function(localCapabilities, remoteCapabilities) {
+ var commonCapabilities = {
+ codecs: [],
+ headerExtensions: [],
+ fecMechanisms: []
+ };
+ localCapabilities.codecs.forEach(function(lCodec) {
+ for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
+ var rCodec = remoteCapabilities.codecs[i];
+ if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
+ lCodec.clockRate === rCodec.clockRate &&
+ lCodec.numChannels === rCodec.numChannels) {
+ // push rCodec so we reply with offerer payload type
+ commonCapabilities.codecs.push(rCodec);
+
+ // FIXME: also need to determine intersection between
+ // .rtcpFeedback and .parameters
+ break;
+ }
+ }
+ });
+
+ localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
+ for (var i = 0; i < remoteCapabilities.headerExtensions.length; i++) {
+ var rHeaderExtension = remoteCapabilities.headerExtensions[i];
+ if (lHeaderExtension.uri === rHeaderExtension.uri) {
+ commonCapabilities.headerExtensions.push(rHeaderExtension);
+ break;
+ }
+ }
+ });
+
+ // FIXME: fecMechanisms
+ return commonCapabilities;
+ };
+
+ // Create ICE gatherer, ICE transport and DTLS transport.
+ window.RTCPeerConnection.prototype._createIceAndDtlsTransports =
+ function(mid, sdpMLineIndex) {
+ var self = this;
+ var iceGatherer = new RTCIceGatherer(self.iceOptions);
+ var iceTransport = new RTCIceTransport(iceGatherer);
+ iceGatherer.onlocalcandidate = function(evt) {
+ var event = {};
+ event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
+
+ var cand = evt.candidate;
+ // Edge emits an empty object for RTCIceCandidateComplete‥
+ if (!cand || Object.keys(cand).length === 0) {
+ // polyfill since RTCIceGatherer.state is not implemented in Edge 10547 yet.
+ if (iceGatherer.state === undefined) {
+ iceGatherer.state = 'completed';
+ }
+
+ // Emit a candidate with type endOfCandidates to make the samples work.
+ // Edge requires addIceCandidate with this empty candidate to start checking.
+ // The real solution is to signal end-of-candidates to the other side when
+ // getting the null candidate but some apps (like the samples) don't do that.
+ event.candidate.candidate =
+ 'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
+ } else {
+ // RTCIceCandidate doesn't have a component, needs to be added
+ cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
+ event.candidate.candidate = SDPUtils.writeCandidate(cand);
+ }
+
+ var complete = self.transceivers.every(function(transceiver) {
+ return transceiver.iceGatherer &&
+ transceiver.iceGatherer.state === 'completed';
+ });
+ // FIXME: update .localDescription with candidate and (potentially) end-of-candidates.
+ // To make this harder, the gatherer might emit candidates before localdescription
+ // is set. To make things worse, gather.getLocalCandidates still errors in
+ // Edge 10547 when no candidates have been gathered yet.
+
+ if (self.onicecandidate !== null) {
+ // Emit candidate if localDescription is set.
+ // Also emits null candidate when all gatherers are complete.
+ if (self.localDescription && self.localDescription.type === '') {
+ self._localIceCandidatesBuffer.push(event);
+ if (complete) {
+ self._localIceCandidatesBuffer.push({});
+ }
+ } else {
+ self.onicecandidate(event);
+ if (complete) {
+ self.onicecandidate({});
+ }
+ }
+ }
+ };
+ iceTransport.onicestatechange = function() {
+ self._updateConnectionState();
+ };
+
+ var dtlsTransport = new RTCDtlsTransport(iceTransport);
+ dtlsTransport.ondtlsstatechange = function() {
+ self._updateConnectionState();
+ };
+ dtlsTransport.onerror = function() {
+ // onerror does not set state to failed by itself.
+ dtlsTransport.state = 'failed';
+ self._updateConnectionState();
+ };
+
+ return {
+ iceGatherer: iceGatherer,
+ iceTransport: iceTransport,
+ dtlsTransport: dtlsTransport
+ };
+ };
+
+ // Start the RTP Sender and Receiver for a transceiver.
+ window.RTCPeerConnection.prototype._transceive = function(transceiver,
+ send, recv) {
+ var params = this._getCommonCapabilities(transceiver.localCapabilities,
+ transceiver.remoteCapabilities);
+ if (send && transceiver.rtpSender) {
+ params.encodings = [{
+ ssrc: transceiver.sendSsrc
+ }];
+ params.rtcp = {
+ cname: localCName,
+ ssrc: transceiver.recvSsrc
+ };
+ transceiver.rtpSender.send(params);
+ }
+ if (recv && transceiver.rtpReceiver) {
+ params.encodings = [{
+ ssrc: transceiver.recvSsrc
+ }];
+ params.rtcp = {
+ cname: transceiver.cname,
+ ssrc: transceiver.sendSsrc
+ };
+ transceiver.rtpReceiver.receive(params);
+ }
+ };
+
+ window.RTCPeerConnection.prototype.setLocalDescription =
+ function(description) {
+ var self = this;
+ if (description.type === 'offer') {
+ if (!this._pendingOffer) {
+ } else {
+ this.transceivers = this._pendingOffer;
+ delete this._pendingOffer;
+ }
+ } else if (description.type === 'answer') {
+ var sections = SDPUtils.splitSections(self.remoteDescription.sdp);
+ var sessionpart = sections.shift();
+ sections.forEach(function(mediaSection, sdpMLineIndex) {
+ var transceiver = self.transceivers[sdpMLineIndex];
+ var iceGatherer = transceiver.iceGatherer;
+ var iceTransport = transceiver.iceTransport;
+ var dtlsTransport = transceiver.dtlsTransport;
+ var localCapabilities = transceiver.localCapabilities;
+ var remoteCapabilities = transceiver.remoteCapabilities;
+ var rejected = mediaSection.split('\n', 1)[0]
+ .split(' ', 2)[1] === '0';
+
+ if (!rejected) {
+ var remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
+ sessionpart);
+ iceTransport.start(iceGatherer, remoteIceParameters, 'controlled');
+
+ var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
+ sessionpart);
+ dtlsTransport.start(remoteDtlsParameters);
+
+ // Calculate intersection of capabilities.
+ var params = self._getCommonCapabilities(localCapabilities,
+ remoteCapabilities);
+
+ // Start the RTCRtpSender. The RTCRtpReceiver for this transceiver
+ // has already been started in setRemoteDescription.
+ self._transceive(transceiver,
+ params.codecs.length > 0,
+ false);
+ }
+ });
+ }
+
+ this.localDescription = description;
+ switch (description.type) {
+ case 'offer':
+ this._updateSignalingState('have-local-offer');
+ break;
+ case 'answer':
+ this._updateSignalingState('stable');
+ break;
+ default:
+ throw new TypeError('unsupported type "' + description.type + '"');
+ }
+
+ // If a success callback was provided, emit ICE candidates after it has been
+ // executed. Otherwise, emit callback after the Promise is resolved.
+ var hasCallback = arguments.length > 1 &&
+ typeof arguments[1] === 'function';
+ if (hasCallback) {
+ var cb = arguments[1];
+ window.setTimeout(function() {
+ cb();
+ self._emitBufferedCandidates();
+ }, 0);
+ }
+ var p = Promise.resolve();
+ p.then(function() {
+ if (!hasCallback) {
+ window.setTimeout(self._emitBufferedCandidates.bind(self), 0);
+ }
+ });
+ return p;
+ };
+
+ window.RTCPeerConnection.prototype.setRemoteDescription =
+ function(description) {
+ var self = this;
+ var stream = new MediaStream();
+ var sections = SDPUtils.splitSections(description.sdp);
+ var sessionpart = sections.shift();
+ sections.forEach(function(mediaSection, sdpMLineIndex) {
+ var lines = SDPUtils.splitLines(mediaSection);
+ var mline = lines[0].substr(2).split(' ');
+ var kind = mline[0];
+ var rejected = mline[1] === '0';
+ var direction = SDPUtils.getDirection(mediaSection, sessionpart);
+
+ var transceiver;
+ var iceGatherer;
+ var iceTransport;
+ var dtlsTransport;
+ var rtpSender;
+ var rtpReceiver;
+ var sendSsrc;
+ var recvSsrc;
+ var localCapabilities;
+
+ // FIXME: ensure the mediaSection has rtcp-mux set.
+ var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
+ var remoteIceParameters;
+ var remoteDtlsParameters;
+ if (!rejected) {
+ remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
+ sessionpart);
+ remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
+ sessionpart);
+ }
+ var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0].substr(6);
+
+ var cname;
+ // Gets the first SSRC. Note that with RTX there might be multiple SSRCs.
+ var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
+ .map(function(line) {
+ return SDPUtils.parseSsrcMedia(line);
+ })
+ .filter(function(obj) {
+ return obj.attribute === 'cname';
+ })[0];
+ if (remoteSsrc) {
+ recvSsrc = parseInt(remoteSsrc.ssrc, 10);
+ cname = remoteSsrc.value;
+ }
+
+ if (description.type === 'offer') {
+ var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);
+
+ localCapabilities = RTCRtpReceiver.getCapabilities(kind);
+ sendSsrc = (2 * sdpMLineIndex + 2) * 1001;
+
+ rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
+
+ // FIXME: not correct when there are multiple streams but that is
+ // not currently supported in this shim.
+ stream.addTrack(rtpReceiver.track);
+
+ // FIXME: look at direction.
+ if (self.localStreams.length > 0 &&
+ self.localStreams[0].getTracks().length >= sdpMLineIndex) {
+ // FIXME: actually more complicated, needs to match types etc
+ var localtrack = self.localStreams[0].getTracks()[sdpMLineIndex];
+ rtpSender = new RTCRtpSender(localtrack, transports.dtlsTransport);
+ }
+
+ self.transceivers[sdpMLineIndex] = {
+ iceGatherer: transports.iceGatherer,
+ iceTransport: transports.iceTransport,
+ dtlsTransport: transports.dtlsTransport,
+ localCapabilities: localCapabilities,
+ remoteCapabilities: remoteCapabilities,
+ rtpSender: rtpSender,
+ rtpReceiver: rtpReceiver,
+ kind: kind,
+ mid: mid,
+ cname: cname,
+ sendSsrc: sendSsrc,
+ recvSsrc: recvSsrc
+ };
+ // Start the RTCRtpReceiver now. The RTPSender is started in setLocalDescription.
+ self._transceive(self.transceivers[sdpMLineIndex],
+ false,
+ direction === 'sendrecv' || direction === 'sendonly');
+ } else if (description.type === 'answer' && !rejected) {
+ transceiver = self.transceivers[sdpMLineIndex];
+ iceGatherer = transceiver.iceGatherer;
+ iceTransport = transceiver.iceTransport;
+ dtlsTransport = transceiver.dtlsTransport;
+ rtpSender = transceiver.rtpSender;
+ rtpReceiver = transceiver.rtpReceiver;
+ sendSsrc = transceiver.sendSsrc;
+ //recvSsrc = transceiver.recvSsrc;
+ localCapabilities = transceiver.localCapabilities;
+
+ self.transceivers[sdpMLineIndex].recvSsrc = recvSsrc;
+ self.transceivers[sdpMLineIndex].remoteCapabilities =
+ remoteCapabilities;
+ self.transceivers[sdpMLineIndex].cname = cname;
+
+ iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
+ dtlsTransport.start(remoteDtlsParameters);
+
+ self._transceive(transceiver,
+ direction === 'sendrecv' || direction === 'recvonly',
+ direction === 'sendrecv' || direction === 'sendonly');
+
+ if (rtpReceiver &&
+ (direction === 'sendrecv' || direction === 'sendonly')) {
+ stream.addTrack(rtpReceiver.track);
+ } else {
+ // FIXME: actually the receiver should be created later.
+ delete transceiver.rtpReceiver;
+ }
+ }
+ });
+
+ this.remoteDescription = description;
+ switch (description.type) {
+ case 'offer':
+ this._updateSignalingState('have-remote-offer');
+ break;
+ case 'answer':
+ this._updateSignalingState('stable');
+ break;
+ default:
+ throw new TypeError('unsupported type "' + description.type + '"');
+ }
+ window.setTimeout(function() {
+ if (self.onaddstream !== null && stream.getTracks().length) {
+ self.remoteStreams.push(stream);
+ window.setTimeout(function() {
+ self.onaddstream({stream: stream});
+ }, 0);
+ }
+ }, 0);
+ if (arguments.length > 1 && typeof arguments[1] === 'function') {
+ window.setTimeout(arguments[1], 0);
+ }
+ return Promise.resolve();
+ };
+
+ window.RTCPeerConnection.prototype.close = function() {
+ this.transceivers.forEach(function(transceiver) {
+ /* not yet
+ if (transceiver.iceGatherer) {
+ transceiver.iceGatherer.close();
+ }
+ */
+ if (transceiver.iceTransport) {
+ transceiver.iceTransport.stop();
+ }
+ if (transceiver.dtlsTransport) {
+ transceiver.dtlsTransport.stop();
+ }
+ if (transceiver.rtpSender) {
+ transceiver.rtpSender.stop();
+ }
+ if (transceiver.rtpReceiver) {
+ transceiver.rtpReceiver.stop();
+ }
+ });
+ // FIXME: clean up tracks, local streams, remote streams, etc
+ this._updateSignalingState('closed');
+ };
+
+ // Update the signaling state.
+ window.RTCPeerConnection.prototype._updateSignalingState =
+ function(newState) {
+ this.signalingState = newState;
+ if (this.onsignalingstatechange !== null) {
+ this.onsignalingstatechange();
+ }
+ };
+
+ // Determine whether to fire the negotiationneeded event.
+ window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =
+ function() {
+ // Fire away (for now).
+ if (this.onnegotiationneeded !== null) {
+ this.onnegotiationneeded();
+ }
+ };
+
+ // Update the connection state.
+ window.RTCPeerConnection.prototype._updateConnectionState =
+ function() {
+ var self = this;
+ var newState;
+ var states = {
+ 'new': 0,
+ closed: 0,
+ connecting: 0,
+ checking: 0,
+ connected: 0,
+ completed: 0,
+ failed: 0
+ };
+ this.transceivers.forEach(function(transceiver) {
+ states[transceiver.iceTransport.state]++;
+ states[transceiver.dtlsTransport.state]++;
+ });
+ // ICETransport.completed and connected are the same for this purpose.
+ states.connected += states.completed;
+
+ newState = 'new';
+ if (states.failed > 0) {
+ newState = 'failed';
+ } else if (states.connecting > 0 || states.checking > 0) {
+ newState = 'connecting';
+ } else if (states.disconnected > 0) {
+ newState = 'disconnected';
+ } else if (states.new > 0) {
+ newState = 'new';
+ } else if (states.connecting > 0 || states.completed > 0) {
+ newState = 'connected';
+ }
+
+ if (newState !== self.iceConnectionState) {
+ self.iceConnectionState = newState;
+ if (this.oniceconnectionstatechange !== null) {
+ this.oniceconnectionstatechange();
+ }
+ }
+ };
+
+ window.RTCPeerConnection.prototype.createOffer = function() {
+ var self = this;
+ if (this._pendingOffer) {
+ throw new Error('createOffer called while there is a pending offer.');
+ }
+ var offerOptions;
+ if (arguments.length === 1 && typeof arguments[0] !== 'function') {
+ offerOptions = arguments[0];
+ } else if (arguments.length === 3) {
+ offerOptions = arguments[2];
+ }
+
+ var tracks = [];
+ var numAudioTracks = 0;
+ var numVideoTracks = 0;
+ // Default to sendrecv.
+ if (this.localStreams.length) {
+ numAudioTracks = this.localStreams[0].getAudioTracks().length;
+ numVideoTracks = this.localStreams[0].getVideoTracks().length;
+ }
+ // Determine number of audio and video tracks we need to send/recv.
+ if (offerOptions) {
+ // Reject Chrome legacy constraints.
+ if (offerOptions.mandatory || offerOptions.optional) {
+ throw new TypeError(
+ 'Legacy mandatory/optional constraints not supported.');
+ }
+ if (offerOptions.offerToReceiveAudio !== undefined) {
+ numAudioTracks = offerOptions.offerToReceiveAudio;
+ }
+ if (offerOptions.offerToReceiveVideo !== undefined) {
+ numVideoTracks = offerOptions.offerToReceiveVideo;
+ }
+ }
+ if (this.localStreams.length) {
+ // Push local streams.
+ this.localStreams[0].getTracks().forEach(function(track) {
+ tracks.push({
+ kind: track.kind,
+ track: track,
+ wantReceive: track.kind === 'audio' ?
+ numAudioTracks > 0 : numVideoTracks > 0
+ });
+ if (track.kind === 'audio') {
+ numAudioTracks--;
+ } else if (track.kind === 'video') {
+ numVideoTracks--;
+ }
+ });
+ }
+ // Create M-lines for recvonly streams.
+ while (numAudioTracks > 0 || numVideoTracks > 0) {
+ if (numAudioTracks > 0) {
+ tracks.push({
+ kind: 'audio',
+ wantReceive: true
+ });
+ numAudioTracks--;
+ }
+ if (numVideoTracks > 0) {
+ tracks.push({
+ kind: 'video',
+ wantReceive: true
+ });
+ numVideoTracks--;
+ }
+ }
+
+ var sdp = SDPUtils.writeSessionBoilerplate();
+ var transceivers = [];
+ tracks.forEach(function(mline, sdpMLineIndex) {
+ // For each track, create an ice gatherer, ice transport, dtls transport,
+ // potentially rtpsender and rtpreceiver.
+ var track = mline.track;
+ var kind = mline.kind;
+ var mid = generateIdentifier();
+
+ var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);
+
+ var localCapabilities = RTCRtpSender.getCapabilities(kind);
+ var rtpSender;
+ var rtpReceiver;
+
+ // generate an ssrc now, to be used later in rtpSender.send
+ var sendSsrc = (2 * sdpMLineIndex + 1) * 1001;
+ if (track) {
+ rtpSender = new RTCRtpSender(track, transports.dtlsTransport);
+ }
+
+ if (mline.wantReceive) {
+ rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
+ }
+
+ transceivers[sdpMLineIndex] = {
+ iceGatherer: transports.iceGatherer,
+ iceTransport: transports.iceTransport,
+ dtlsTransport: transports.dtlsTransport,
+ localCapabilities: localCapabilities,
+ remoteCapabilities: null,
+ rtpSender: rtpSender,
+ rtpReceiver: rtpReceiver,
+ kind: kind,
+ mid: mid,
+ sendSsrc: sendSsrc,
+ recvSsrc: null
+ };
+ var transceiver = transceivers[sdpMLineIndex];
+ sdp += SDPUtils.writeMediaSection(transceiver,
+ transceiver.localCapabilities, 'offer', self.localStreams[0]);
+ });
+
+ this._pendingOffer = transceivers;
+ var desc = new RTCSessionDescription({
+ type: 'offer',
+ sdp: sdp
+ });
+ if (arguments.length && typeof arguments[0] === 'function') {
+ window.setTimeout(arguments[0], 0, desc);
+ }
+ return Promise.resolve(desc);
+ };
+
+ window.RTCPeerConnection.prototype.createAnswer = function() {
+ var self = this;
+ var answerOptions;
+ if (arguments.length === 1 && typeof arguments[0] !== 'function') {
+ answerOptions = arguments[0];
+ } else if (arguments.length === 3) {
+ answerOptions = arguments[2];
+ }
+
+ var sdp = SDPUtils.writeSessionBoilerplate();
+ this.transceivers.forEach(function(transceiver) {
+ // Calculate intersection of capabilities.
+ var commonCapabilities = self._getCommonCapabilities(
+ transceiver.localCapabilities,
+ transceiver.remoteCapabilities);
+
+ sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
+ 'answer', self.localStreams[0]);
+ });
+
+ var desc = new RTCSessionDescription({
+ type: 'answer',
+ sdp: sdp
+ });
+ if (arguments.length && typeof arguments[0] === 'function') {
+ window.setTimeout(arguments[0], 0, desc);
+ }
+ return Promise.resolve(desc);
+ };
+
+ window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
+ var mLineIndex = candidate.sdpMLineIndex;
+ if (candidate.sdpMid) {
+ for (var i = 0; i < this.transceivers.length; i++) {
+ if (this.transceivers[i].mid === candidate.sdpMid) {
+ mLineIndex = i;
+ break;
+ }
+ }
+ }
+ var transceiver = this.transceivers[mLineIndex];
+ if (transceiver) {
+ var cand = Object.keys(candidate.candidate).length > 0 ?
+ SDPUtils.parseCandidate(candidate.candidate) : {};
+ // Ignore Chrome's invalid candidates since Edge does not like them.
+ if (cand.protocol === 'tcp' && cand.port === 0) {
+ return;
+ }
+ // Ignore RTCP candidates, we assume RTCP-MUX.
+ if (cand.component !== '1') {
+ return;
+ }
+ // A dirty hack to make samples work.
+ if (cand.type === 'endOfCandidates') {
+ cand = {};
+ }
+ transceiver.iceTransport.addRemoteCandidate(cand);
+ }
+ if (arguments.length > 1 && typeof arguments[1] === 'function') {
+ window.setTimeout(arguments[1], 0);
+ }
+ return Promise.resolve();
+ };
+
+ window.RTCPeerConnection.prototype.getStats = function() {
+ var promises = [];
+ this.transceivers.forEach(function(transceiver) {
+ ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
+ 'dtlsTransport'].forEach(function(method) {
+ if (transceiver[method]) {
+ promises.push(transceiver[method].getStats());
+ }
+ });
+ });
+ var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
+ arguments[1];
+ return new Promise(function(resolve) {
+ var results = {};
+ Promise.all(promises).then(function(res) {
+ res.forEach(function(result) {
+ Object.keys(result).forEach(function(id) {
+ results[id] = result[id];
+ });
+ });
+ if (cb) {
+ window.setTimeout(cb, 0, results);
+ }
+ resolve(results);
+ });
+ });
+ };
+ }
} else {
webrtcUtils.log('Browser does not appear to be WebRTC-capable');
}
@@ -1074,11 +2256,17 @@ if ( navigator.mozGetUserMedia
/* Orginal exports removed in favor of AdapterJS custom export.
if (typeof module !== 'undefined') {
var RTCPeerConnection;
+ var RTCIceCandidate;
+ var RTCSessionDescription;
if (typeof window !== 'undefined') {
RTCPeerConnection = window.RTCPeerConnection;
+ RTCIceCandidate = window.RTCIceCandidate;
+ RTCSessionDescription = window.RTCSessionDescription;
}
module.exports = {
RTCPeerConnection: RTCPeerConnection,
+ RTCIceCandidate: RTCIceCandidate,
+ RTCSessionDescription: RTCSessionDescription,
getUserMedia: getUserMedia,
attachMediaStream: attachMediaStream,
reattachMediaStream: reattachMediaStream,
@@ -1095,6 +2283,8 @@ if ( navigator.mozGetUserMedia
define([], function() {
return {
RTCPeerConnection: window.RTCPeerConnection,
+ RTCIceCandidate: window.RTCIceCandidate,
+ RTCSessionDescription: window.RTCSessionDescription,
getUserMedia: getUserMedia,
attachMediaStream: attachMediaStream,
reattachMediaStream: reattachMediaStream,
@@ -1110,13 +2300,16 @@ if ( navigator.mozGetUserMedia
}
*/
+/* jshint ignore:end */
// END OF INJECTION OF GOOGLE'S ADAPTER.JS CONTENT
///////////////////////////////////////////////////////////////////
+ AdapterJS.parseWebrtcDetectedBrowser();
+
///////////////////////////////////////////////////////////////////
// EXTENSION FOR CHROME, FIREFOX AND EDGE
- // Includes legacy functions
+ // Includes legacy functions
// -- createIceServer
// -- createIceServers
// -- MediaStreamTrack.getSources
@@ -1142,25 +2335,26 @@ if ( navigator.mozGetUserMedia
createIceServer = function (url, username, password) {
console.warn('createIceServer is deprecated. It should be replaced with an application level implementation.');
-
+ // Note: Google's import of AJS will auto-reverse to 'url': '...' for FF < 38
+
var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
- iceServer = { url : url };
- } else if (url_parts[0].indexOf('turn') === 0) {
+ var urlParts = url.split(':');
+ if (urlParts[0].indexOf('stun') === 0) {
+ iceServer = { urls : [url] };
+ } else if (urlParts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 27) {
- var turn_url_parts = url.split('?');
- if (turn_url_parts.length === 1 ||
- turn_url_parts[1].indexOf('transport=udp') === 0) {
+ var turnUrlParts = url.split('?');
+ if (turnUrlParts.length === 1 ||
+ turnUrlParts[1].indexOf('transport=udp') === 0) {
iceServer = {
- url : turn_url_parts[0],
+ urls : [turnUrlParts[0]],
credential : password,
username : username
};
}
} else {
iceServer = {
- url : url,
+ urls : [url],
credential : password,
username : username
};
@@ -1184,12 +2378,12 @@ if ( navigator.mozGetUserMedia
} else if ( navigator.webkitGetUserMedia ) {
createIceServer = function (url, username, password) {
console.warn('createIceServer is deprecated. It should be replaced with an application level implementation.');
-
+
var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
+ var urlParts = url.split(':');
+ if (urlParts[0].indexOf('stun') === 0) {
iceServer = { 'url' : url };
- } else if (url_parts[0].indexOf('turn') === 0) {
+ } else if (urlParts[0].indexOf('turn') === 0) {
iceServer = {
'url' : url,
'credential' : password,
@@ -1225,7 +2419,7 @@ if ( navigator.mozGetUserMedia
// attachMediaStream and reattachMediaStream for Egde
if (navigator.mediaDevices && navigator.userAgent.match(
/Edge\/(\d+).(\d+)$/)) {
- window.getUserMedia = navigator.getUserMedia.bind(navigator);
+ getUserMedia = window.getUserMedia = navigator.getUserMedia.bind(navigator);
attachMediaStream = function(element, stream) {
element.srcObject = stream;
return element;
@@ -1236,11 +2430,18 @@ if ( navigator.mozGetUserMedia
};
}
- // Need to override attachMediaStream and reattachMediaStream
+ // Need to override attachMediaStream and reattachMediaStream
// to support the plugin's logic
attachMediaStream_base = attachMediaStream;
attachMediaStream = function (element, stream) {
- attachMediaStream_base(element, stream);
+ if ((webrtcDetectedBrowser === 'chrome' ||
+ webrtcDetectedBrowser === 'opera') &&
+ !stream) {
+ // Chrome does not support "src = null"
+ element.src = '';
+ } else {
+ attachMediaStream_base(element, stream);
+ }
return element;
};
reattachMediaStream_base = reattachMediaStream;
@@ -1249,6 +2450,14 @@ if ( navigator.mozGetUserMedia
return to;
};
+ // Propagate attachMediaStream and gUM in window and AdapterJS
+ window.attachMediaStream = attachMediaStream;
+ window.reattachMediaStream = reattachMediaStream;
+ window.getUserMedia = getUserMedia;
+ AdapterJS.attachMediaStream = attachMediaStream;
+ AdapterJS.reattachMediaStream = reattachMediaStream;
+ AdapterJS.getUserMedia = getUserMedia;
+
// Removed Google defined promises when promise is not defined
if (typeof Promise === 'undefined') {
requestUserMedia = null;
@@ -1305,7 +2514,6 @@ if ( navigator.mozGetUserMedia
console.groupEnd = function (arg) {};
/* jshint +W020 */
}
- webrtcDetectedType = 'plugin';
AdapterJS.parseWebrtcDetectedBrowser();
isIE = webrtcDetectedBrowser === 'IE';
@@ -1431,7 +2639,7 @@ if ( navigator.mozGetUserMedia
AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
if (AdapterJS.WebRTCPlugin.pluginState ===
AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
- console.error("AdapterJS - WebRTC interface has already been defined");
+ console.error('AdapterJS - WebRTC interface has already been defined');
return;
}
@@ -1443,13 +2651,13 @@ if ( navigator.mozGetUserMedia
createIceServer = function (url, username, password) {
var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
+ var urlParts = url.split(':');
+ if (urlParts[0].indexOf('stun') === 0) {
iceServer = {
'url' : url,
'hasCredentials' : false
};
- } else if (url_parts[0].indexOf('turn') === 0) {
+ } else if (urlParts[0].indexOf('turn') === 0) {
iceServer = {
'url' : url,
'hasCredentials' : true,
@@ -1475,27 +2683,58 @@ if ( navigator.mozGetUserMedia
};
RTCPeerConnection = function (servers, constraints) {
- var iceServers = null;
- if (servers) {
- iceServers = servers.iceServers;
- for (var i = 0; i < iceServers.length; i++) {
- if (iceServers[i].urls && !iceServers[i].url) {
- iceServers[i].url = iceServers[i].urls;
- }
- iceServers[i].hasCredentials = AdapterJS.
- isDefined(iceServers[i].username) &&
- AdapterJS.isDefined(iceServers[i].credential);
+ // Validate server argumenr
+ if (!(servers === undefined ||
+ servers === null ||
+ Array.isArray(servers.iceServers))) {
+ throw new Error('Failed to construct \'RTCPeerConnection\': Malformed RTCConfiguration');
+ }
+
+ // Validate constraints argument
+ if (typeof constraints !== 'undefined' && constraints !== null) {
+ var invalidConstraits = false;
+ invalidConstraits |= typeof constraints !== 'object';
+ invalidConstraits |= constraints.hasOwnProperty('mandatory') &&
+ constraints.mandatory !== undefined &&
+ constraints.mandatory !== null &&
+ constraints.mandatory.constructor !== Object;
+ invalidConstraits |= constraints.hasOwnProperty('optional') &&
+ constraints.optional !== undefined &&
+ constraints.optional !== null &&
+ !Array.isArray(constraints.optional);
+ if (invalidConstraits) {
+ throw new Error('Failed to construct \'RTCPeerConnection\': Malformed constraints object');
}
}
- var mandatory = (constraints && constraints.mandatory) ?
- constraints.mandatory : null;
- var optional = (constraints && constraints.optional) ?
- constraints.optional : null;
+ // Call relevant PeerConnection constructor according to plugin version
AdapterJS.WebRTCPlugin.WaitForPluginReady();
- return AdapterJS.WebRTCPlugin.plugin.
- PeerConnection(AdapterJS.WebRTCPlugin.pageId,
- iceServers, mandatory, optional);
+ if (AdapterJS.WebRTCPlugin.plugin.PEER_CONNECTION_VERSION &&
+ AdapterJS.WebRTCPlugin.plugin.PEER_CONNECTION_VERSION > 1) {
+ // RTCPeerConnection prototype from the new spec
+ return AdapterJS.WebRTCPlugin.plugin.PeerConnection(servers);
+ } else {
+ // RTCPeerConnection prototype from the old spec
+ var iceServers = null;
+ if (servers && Array.isArray(servers.iceServers)) {
+ iceServers = servers.iceServers;
+ for (var i = 0; i < iceServers.length; i++) {
+ if (iceServers[i].urls && !iceServers[i].url) {
+ iceServers[i].url = iceServers[i].urls;
+ }
+ iceServers[i].hasCredentials = AdapterJS.
+ isDefined(iceServers[i].username) &&
+ AdapterJS.isDefined(iceServers[i].credential);
+ }
+ }
+ var mandatory = (constraints && constraints.mandatory) ?
+ constraints.mandatory : null;
+ var optional = (constraints && constraints.optional) ?
+ constraints.optional : null;
+ return AdapterJS.WebRTCPlugin.plugin.
+ PeerConnection(AdapterJS.WebRTCPlugin.pageId,
+ iceServers, mandatory, optional);
+ }
};
MediaStreamTrack = {};
@@ -1505,7 +2744,7 @@ if ( navigator.mozGetUserMedia
});
};
- window.getUserMedia = function (constraints, successCallback, failureCallback) {
+ getUserMedia = function (constraints, successCallback, failureCallback) {
constraints.audio = constraints.audio || false;
constraints.video = constraints.video || false;
@@ -1514,11 +2753,16 @@ if ( navigator.mozGetUserMedia
getUserMedia(constraints, successCallback, failureCallback);
});
};
- window.navigator.getUserMedia = window.getUserMedia;
+ window.navigator.getUserMedia = getUserMedia;
// Defined mediaDevices when promises are available
- if ( !navigator.mediaDevices
- && typeof Promise !== 'undefined') {
+ if ( !navigator.mediaDevices &&
+ typeof Promise !== 'undefined') {
+ requestUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ getUserMedia(constraints, resolve, reject);
+ });
+ };
navigator.mediaDevices = {getUserMedia: requestUserMedia,
enumerateDevices: function() {
return new Promise(function(resolve) {
@@ -1527,6 +2771,7 @@ if ( navigator.mozGetUserMedia
resolve(devices.map(function(device) {
return {label: device.label,
kind: kinds[device.kind],
+ id: device.id,
deviceId: device.id,
groupId: ''};
}));
@@ -1636,31 +2881,32 @@ if ( navigator.mozGetUserMedia
}
};
- AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
+ // Propagate attachMediaStream and gUM in window and AdapterJS
+ window.attachMediaStream = attachMediaStream;
+ window.reattachMediaStream = reattachMediaStream;
+ window.getUserMedia = getUserMedia;
+ AdapterJS.attachMediaStream = attachMediaStream;
+ AdapterJS.reattachMediaStream = reattachMediaStream;
+ AdapterJS.getUserMedia = getUserMedia;
+ AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
properties = Object.getOwnPropertyNames( prototype );
-
- for(prop in properties) {
- propName = properties[prop];
-
- if (typeof(propName.slice) === 'function') {
- if (propName.slice(0,2) == 'on' && srcElem[propName] != null) {
- if (isIE) {
- destElem.attachEvent(propName,srcElem[propName]);
- } else {
- destElem.addEventListener(propName.slice(2), srcElem[propName], false)
- }
- } else {
- //TODO (http://jira.temasys.com.sg/browse/TWP-328) Forward non-event properties ?
+ for(var prop in properties) {
+ if (prop) {
+ propName = properties[prop];
+
+ if (typeof propName.slice === 'function' &&
+ propName.slice(0,2) === 'on' &&
+ typeof srcElem[propName] === 'function') {
+ AdapterJS.addEvent(destElem, propName.slice(2), srcElem[propName]);
}
}
}
-
- var subPrototype = Object.getPrototypeOf(prototype)
- if(subPrototype != null) {
+ var subPrototype = Object.getPrototypeOf(prototype);
+ if(!!subPrototype) {
AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype);
}
- }
+ };
RTCIceCandidate = function (candidate) {
if (!candidate.sdpMid) {
diff --git a/publish/adapter.min.js b/publish/adapter.min.js
index 076c8cf..5980c08 100644
--- a/publish/adapter.min.js
+++ b/publish/adapter.min.js
@@ -1,2 +1,3 @@
-/*! adapterjs - v0.13.0 - 2016-01-08 */
-function trace(text){if("\n"===text[text.length-1]&&(text=text.substring(0,text.length-1)),window.performance){var now=(window.performance.now()/1e3).toFixed(3);webrtcUtils.log(now+": "+text)}else webrtcUtils.log(text)}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}var AdapterJS=AdapterJS||{};if("undefined"!=typeof exports&&(module.exports=AdapterJS),AdapterJS.options=AdapterJS.options||{},AdapterJS.VERSION="0.13.0",AdapterJS.onwebrtcready=AdapterJS.onwebrtcready||function(isUsingPlugin){},AdapterJS._onwebrtcreadies=[],AdapterJS.webRTCReady=function(callback){if("function"!=typeof callback)throw new Error("Callback provided is not a function");!0===AdapterJS.onwebrtcreadyDone?callback(null!==AdapterJS.WebRTCPlugin.plugin):AdapterJS._onwebrtcreadies.push(callback)},AdapterJS.WebRTCPlugin=AdapterJS.WebRTCPlugin||{},AdapterJS.WebRTCPlugin.pluginInfo={prefix:"Tem",plugName:"TemWebRTCPlugin",pluginId:"plugin0",type:"application/x-temwebrtcplugin",onload:"__TemWebRTCReady0",portalLink:"http://skylink.io/plugin/",downloadLink:null,companyName:"Temasys"},navigator.platform.match(/^Mac/i)?AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1n77hco":navigator.platform.match(/^Win/i)&&(AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1kkS4FN"),AdapterJS.WebRTCPlugin.TAGS={NONE:"none",AUDIO:"audio",VIDEO:"video"},AdapterJS.WebRTCPlugin.pageId=Math.random().toString(36).slice(2),AdapterJS.WebRTCPlugin.plugin=null,AdapterJS.WebRTCPlugin.setLogLevel=null,AdapterJS.WebRTCPlugin.defineWebRTCInterface=null,AdapterJS.WebRTCPlugin.isPluginInstalled=null,AdapterJS.WebRTCPlugin.pluginInjectionInterval=null,AdapterJS.WebRTCPlugin.injectPlugin=null,AdapterJS.WebRTCPlugin.PLUGIN_STATES={NONE:0,INITIALIZING:1,INJECTING:2,INJECTED:3,READY:4},AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE,AdapterJS.onwebrtcreadyDone=!1,AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS={NONE:"NONE",ERROR:"ERROR",WARNING:"WARNING",INFO:"INFO",VERBOSE:"VERBOSE",SENSITIVE:"SENSITIVE"},AdapterJS.WebRTCPlugin.WaitForPluginReady=null,AdapterJS.WebRTCPlugin.callWhenPluginReady=null,__TemWebRTCReady0=function(){webrtcDetectedVersion=AdapterJS.WebRTCPlugin.plugin.version,"complete"===document.readyState?(AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady()):AdapterJS.WebRTCPlugin.documentReadyInterval=setInterval(function(){"complete"===document.readyState&&(clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval),AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady())},100)},AdapterJS.maybeThroughWebRTCReady=function(){AdapterJS.onwebrtcreadyDone||(AdapterJS.onwebrtcreadyDone=!0,AdapterJS._onwebrtcreadies.length?AdapterJS._onwebrtcreadies.forEach(function(callback){"function"==typeof callback&&callback(null!==AdapterJS.WebRTCPlugin.plugin)}):"function"==typeof AdapterJS.onwebrtcready&&AdapterJS.onwebrtcready(null!==AdapterJS.WebRTCPlugin.plugin))},AdapterJS.TEXT={PLUGIN:{REQUIRE_INSTALLATION:"This website requires you to install a WebRTC-enabling plugin to work on this browser.",NOT_SUPPORTED:"Your browser does not support WebRTC.",BUTTON:"Install Now"},REFRESH:{REQUIRE_REFRESH:"Please refresh page",BUTTON:"Refresh Page"}},AdapterJS._iceConnectionStates={starting:"starting",checking:"checking",connected:"connected",completed:"connected",done:"completed",disconnected:"disconnected",failed:"failed",closed:"closed"},AdapterJS._iceConnectionFiredStates=[],AdapterJS.isDefined=null,AdapterJS.parseWebrtcDetectedBrowser=function(){var hasMatch,checkMatch=navigator.userAgent.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i)||[];if(/trident/i.test(checkMatch[1])?(hasMatch=/\brv[ :]+(\d+)/g.exec(navigator.userAgent)||[],webrtcDetectedBrowser="IE",webrtcDetectedVersion=parseInt(hasMatch[1]||"0",10)):"Chrome"===checkMatch[1]&&(hasMatch=navigator.userAgent.match(/\bOPR\/(\d+)/),null!==hasMatch&&(webrtcDetectedBrowser="opera",webrtcDetectedVersion=parseInt(hasMatch[1],10))),navigator.userAgent.indexOf("Safari")&&("undefined"!=typeof InstallTrigger?webrtcDetectedBrowser="firefox":document.documentMode?webrtcDetectedBrowser="IE":Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0?webrtcDetectedBrowser="safari":window.opera||navigator.userAgent.indexOf(" OPR/")>=0?webrtcDetectedBrowser="opera":window.chrome&&(webrtcDetectedBrowser="chrome")),webrtcDetectedBrowser||(webrtcDetectedVersion=checkMatch[1]),!webrtcDetectedVersion)try{checkMatch=checkMatch[2]?[checkMatch[1],checkMatch[2]]:[navigator.appName,navigator.appVersion,"-?"],null!==(hasMatch=navigator.userAgent.match(/version\/(\d+)/i))&&checkMatch.splice(1,1,hasMatch[1]),webrtcDetectedVersion=parseInt(checkMatch[1],10)}catch(error){}},AdapterJS.maybeFixConfiguration=function(pcConfig){if(null!==pcConfig)for(var i=0;i'+text+""),buttonText&&buttonLink?(c.document.write(''),c.document.close(),AdapterJS.addEvent(c.document.getElementById("okay"),"click",function(e){displayRefreshBar&&AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION?AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH:AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH,AdapterJS.TEXT.REFRESH.BUTTON,"javascript:location.reload()"),window.open(buttonLink,openNewTab?"_blank":"_top"),e.preventDefault();try{event.cancelBubble=!0}catch(error){}var pluginInstallInterval=setInterval(function(){isIE||navigator.plugins.refresh(!1),AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,function(){clearInterval(pluginInstallInterval),AdapterJS.WebRTCPlugin.defineWebRTCInterface()},function(){})},500)}),AdapterJS.addEvent(c.document.getElementById("cancel"),"click",function(e){w.document.body.removeChild(i)})):c.document.close(),setTimeout(function(){"string"==typeof i.style.webkitTransform?i.style.webkitTransform="translateY(40px)":"string"==typeof i.style.transform?i.style.transform="translateY(40px)":i.style.top="0px"},300)}},webrtcDetectedType=null,checkMediaDataChannelSettings=function(peerBrowserAgent,peerBrowserVersion,callback,constraints){if("function"==typeof callback){var beOfferer=!0,isLocalFirefox="firefox"===webrtcDetectedBrowser,isLocalFirefoxInterop="moz"===webrtcDetectedType&&webrtcDetectedVersion>30,isPeerFirefox="firefox"===peerBrowserAgent;if(isLocalFirefox&&isPeerFirefox||isLocalFirefoxInterop)try{delete constraints.mandatory.MozDontOfferDataChannel}catch(error){}else isLocalFirefox&&!isPeerFirefox&&(constraints.mandatory.MozDontOfferDataChannel=!0);if(!isLocalFirefox)for(var prop in constraints.mandatory)constraints.mandatory.hasOwnProperty(prop)&&-1!==prop.indexOf("Moz")&&delete constraints.mandatory[prop];!isLocalFirefox||isPeerFirefox||isLocalFirefoxInterop||(beOfferer=!1),callback(beOfferer,constraints)}},checkIceConnectionState=function(peerId,iceConnectionState,callback){"function"==typeof callback&&(peerId=peerId?peerId:"peer",AdapterJS._iceConnectionFiredStates[peerId]&&iceConnectionState!==AdapterJS._iceConnectionStates.disconnected&&iceConnectionState!==AdapterJS._iceConnectionStates.failed&&iceConnectionState!==AdapterJS._iceConnectionStates.closed||(AdapterJS._iceConnectionFiredStates[peerId]=[]),iceConnectionState=AdapterJS._iceConnectionStates[iceConnectionState],AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState)<0&&(AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState),iceConnectionState===AdapterJS._iceConnectionStates.connected&&setTimeout(function(){AdapterJS._iceConnectionFiredStates[peerId].push(AdapterJS._iceConnectionStates.done),callback(AdapterJS._iceConnectionStates.done)},1e3),callback(iceConnectionState)))},createIceServer=null,createIceServers=null,RTCPeerConnection=null,RTCSessionDescription="function"==typeof RTCSessionDescription?RTCSessionDescription:null,RTCIceCandidate="function"==typeof RTCIceCandidate?RTCIceCandidate:null,getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,navigator.mozGetUserMedia||navigator.webkitGetUserMedia||navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)){var getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils={log:function(){"undefined"!=typeof module||"function"==typeof require&&"function"==typeof define},extractVersion:function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos])}};if("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=URL.createObjectURL(stream))}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),attachMediaStream=function(element,stream){element.srcObject=stream},reattachMediaStream=function(to,from){to.srcObject=from.srcObject},"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia&&window.mozRTCPeerConnection){if(webrtcUtils.log("This appears to be Firefox"),webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,window.RTCPeerConnection=function(pcConfig,pcConstraints){if(38>webrtcDetectedVersion&&pcConfig&&pcConfig.iceServers){for(var newIceServers=[],i=0;iwebrtcDetectedVersion&&(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){webrtcUtils.log("This appears to be Chrome"),webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38,window.RTCPeerConnection=function(pcConfig,pcConstraints){pcConfig&&pcConfig.iceTransportPolicy&&(pcConfig.iceTransports=pcConfig.iceTransportPolicy);var pc=new webkitRTCPeerConnection(pcConfig,pcConstraints),origGetStats=pc.getStats.bind(pc);return pc.getStats=function(selector,successCallback,errorCallback){var self=this,args=arguments;if(arguments.length>0&&"function"==typeof selector)return origGetStats(selector,successCallback);var fixChromeStats=function(response){var standardReport={},reports=response.result();return reports.forEach(function(report){var standardStats={id:report.id,timestamp:report.timestamp,type:report.type};report.names().forEach(function(name){standardStats[name]=report.stat(name)}),standardReport[standardStats.id]=standardStats}),standardReport};if(arguments.length>=2){var successCallbackWrapper=function(response){args[1](fixChromeStats(response))};return origGetStats.apply(this,[successCallbackWrapper,arguments[0]])}return new Promise(function(resolve,reject){1===args.length&&null===selector?origGetStats.apply(self,[function(response){resolve.apply(null,[fixChromeStats(response)])},reject]):origGetStats.apply(self,[resolve,reject])})},pc},["createOffer","createAnswer"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var self=this;if(arguments.length<1||1===arguments.length&&"object"==typeof arguments[0]){var opts=1===arguments.length?arguments[0]:void 0;return new Promise(function(resolve,reject){nativeMethod.apply(self,[resolve,reject,opts])})}return nativeMethod.apply(this,arguments)}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var args=arguments,self=this;return new Promise(function(resolve,reject){nativeMethod.apply(self,[args[0],function(){resolve(),args.length>=2&&args[1].apply(null,[])},function(err){reject(err),args.length>=3&&args[2].apply(null,[err])}])})}});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,enumerateDevices:function(){return new Promise(function(resolve){var kinds={audio:"audioinput",video:"videoinput"};return MediaStreamTrack.getSources(function(devices){resolve(devices.map(function(device){return{label:device.label,kind:kinds[device.kind],deviceId:device.id,groupId:""}}))})})}}),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.")}),attachMediaStream=function(element,stream){webrtcDetectedVersion>=43?element.srcObject=stream:"undefined"!=typeof element.src?element.src=URL.createObjectURL(stream):webrtcUtils.log("Error attaching stream to element.")},reattachMediaStream=function(to,from){webrtcDetectedVersion>=43?to.srcObject=from.srcObject:to.src=from.src}}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 webrtcUtils.log("This does not appear to be a browser"),webrtcDetectedBrowser="not a browser";var webrtcTesting={};try{Object.defineProperty(webrtcTesting,"version",{set:function(version){webrtcDetectedVersion=version}})}catch(e){}navigator.mozGetUserMedia?(MediaStreamTrack.getSources=function(successCb){setTimeout(function(){var infos=[{kind:"audio",id:"default",label:"",facing:""},{kind:"video",id:"default",label:"",facing:""}];successCb(infos)},0)},createIceServer=function(url,username,password){var iceServer=null,url_parts=url.split(":");if(0===url_parts[0].indexOf("stun"))iceServer={url:url};else if(0===url_parts[0].indexOf("turn"))if(27>webrtcDetectedVersion){var turn_url_parts=url.split("?");(1===turn_url_parts.length||0===turn_url_parts[1].indexOf("transport=udp"))&&(iceServer={url:turn_url_parts[0],credential:password,username:username})}else iceServer={url:url,credential:password,username:username};return iceServer},createIceServers=function(urls,username,password){var iceServers=[];for(i=0;i=34)iceServers={urls:urls,credential:password,username:username};else for(i=0;i=webrtcDetectedVersion){var frag=document.createDocumentFragment();for(AdapterJS.WebRTCPlugin.plugin=document.createElement("div"),AdapterJS.WebRTCPlugin.plugin.innerHTML='";AdapterJS.WebRTCPlugin.plugin.firstChild;)frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);document.body.appendChild(frag),AdapterJS.WebRTCPlugin.plugin=document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId)}else AdapterJS.WebRTCPlugin.plugin=document.createElement("object"),AdapterJS.WebRTCPlugin.plugin.id=AdapterJS.WebRTCPlugin.pluginInfo.pluginId,isIE?(AdapterJS.WebRTCPlugin.plugin.width="1px",AdapterJS.WebRTCPlugin.plugin.height="1px"):(AdapterJS.WebRTCPlugin.plugin.width="0px",AdapterJS.WebRTCPlugin.plugin.height="0px"),AdapterJS.WebRTCPlugin.plugin.type=AdapterJS.WebRTCPlugin.pluginInfo.type,AdapterJS.WebRTCPlugin.plugin.innerHTML=' '+(AdapterJS.options.getAllCams?'':"")+'',document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED}},AdapterJS.WebRTCPlugin.isPluginInstalled=function(comName,plugName,installedCb,notInstalledCb){if(isIE){try{new ActiveXObject(comName+"."+plugName)}catch(e){return void notInstalledCb()}installedCb()}else{for(var pluginArray=navigator.plugins,i=0;i=0)return void installedCb();notInstalledCb()}},AdapterJS.WebRTCPlugin.defineWebRTCInterface=function(){AdapterJS.WebRTCPlugin.pluginState!==AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY&&(AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING,AdapterJS.isDefined=function(variable){return null!==variable&&void 0!==variable},createIceServer=function(url,username,password){var iceServer=null,url_parts=url.split(":");return 0===url_parts[0].indexOf("stun")?iceServer={url:url,hasCredentials:!1}:0===url_parts[0].indexOf("turn")&&(iceServer={url:url,hasCredentials:!0,credential:password,username:username}),iceServer},createIceServers=function(urls,username,password){for(var iceServers=[],i=0;i ';temp.firstChild;)frag.appendChild(temp.firstChild);var height="",width="";element.clientWidth||element.clientHeight?(width=element.clientWidth,height=element.clientHeight):(element.width||element.height)&&(width=element.width,height=element.height),element.parentNode.insertBefore(frag,element),frag=document.getElementById(elementId),frag.width=width,frag.height=height,element.parentNode.removeChild(element)}else{for(var children=element.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){children[i].value=streamId;break}element.setStreamId(streamId)}var newElement=document.getElementById(elementId);return AdapterJS.forwardEventHandlers(newElement,element,Object.getPrototypeOf(element)),newElement}},reattachMediaStream=function(to,from){for(var stream=null,children=from.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){AdapterJS.WebRTCPlugin.WaitForPluginReady(),stream=AdapterJS.WebRTCPlugin.plugin.getStreamWithId(AdapterJS.WebRTCPlugin.pageId,children[i].value);break}return null!==stream?attachMediaStream(to,stream):void 0},AdapterJS.forwardEventHandlers=function(destElem,srcElem,prototype){properties=Object.getOwnPropertyNames(prototype);for(prop in properties)propName=properties[prop],"function"==typeof propName.slice&&"on"==propName.slice(0,2)&&null!=srcElem[propName]&&(isIE?destElem.attachEvent(propName,srcElem[propName]):destElem.addEventListener(propName.slice(2),srcElem[propName],!1));var subPrototype=Object.getPrototypeOf(prototype);null!=subPrototype&&AdapterJS.forwardEventHandlers(destElem,srcElem,subPrototype)},RTCIceCandidate=function(candidate){return candidate.sdpMid||(candidate.sdpMid=""),AdapterJS.WebRTCPlugin.WaitForPluginReady(),AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(candidate.sdpMid,candidate.sdpMLineIndex,candidate.candidate)},AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.injectPlugin),AdapterJS.WebRTCPlugin.injectPlugin())},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb=AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb||function(){AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv),AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv()},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv=function(){if(!AdapterJS.options.hidePluginInstallPrompt){var downloadLink=AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;if(downloadLink){var popupString;popupString=AdapterJS.WebRTCPlugin.pluginInfo.portalLink?'This website requires you to install the '+AdapterJS.WebRTCPlugin.pluginInfo.companyName+" WebRTC Plugin to work on this browser.":AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION,AdapterJS.renderNotificationBar(popupString,AdapterJS.TEXT.PLUGIN.BUTTON,downloadLink)}else AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED)}},AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,AdapterJS.WebRTCPlugin.defineWebRTCInterface,AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
+/*! adapterjs - v0.13.1 - 2016-03-15 */
+function trace(text){if("\n"===text[text.length-1]&&(text=text.substring(0,text.length-1)),window.performance){var now=(window.performance.now()/1e3).toFixed(3);webrtcUtils.log(now+": "+text)}else webrtcUtils.log(text)}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}var AdapterJS=AdapterJS||{};if("undefined"!=typeof exports&&(module.exports=AdapterJS),AdapterJS.options=AdapterJS.options||{},AdapterJS.VERSION="0.13.1",AdapterJS.onwebrtcready=AdapterJS.onwebrtcready||function(isUsingPlugin){},AdapterJS._onwebrtcreadies=[],AdapterJS.webRTCReady=function(callback){if("function"!=typeof callback)throw new Error("Callback provided is not a function");!0===AdapterJS.onwebrtcreadyDone?callback(null!==AdapterJS.WebRTCPlugin.plugin):AdapterJS._onwebrtcreadies.push(callback)},AdapterJS.WebRTCPlugin=AdapterJS.WebRTCPlugin||{},AdapterJS.WebRTCPlugin.pluginInfo={prefix:"Tem",plugName:"TemWebRTCPlugin",pluginId:"plugin0",type:"application/x-temwebrtcplugin",onload:"__TemWebRTCReady0",portalLink:"http://skylink.io/plugin/",downloadLink:null,companyName:"Temasys"},navigator.platform.match(/^Mac/i)?AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1n77hco":navigator.platform.match(/^Win/i)&&(AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1kkS4FN"),AdapterJS.WebRTCPlugin.TAGS={NONE:"none",AUDIO:"audio",VIDEO:"video"},AdapterJS.WebRTCPlugin.pageId=Math.random().toString(36).slice(2),AdapterJS.WebRTCPlugin.plugin=null,AdapterJS.WebRTCPlugin.setLogLevel=null,AdapterJS.WebRTCPlugin.defineWebRTCInterface=null,AdapterJS.WebRTCPlugin.isPluginInstalled=null,AdapterJS.WebRTCPlugin.pluginInjectionInterval=null,AdapterJS.WebRTCPlugin.injectPlugin=null,AdapterJS.WebRTCPlugin.PLUGIN_STATES={NONE:0,INITIALIZING:1,INJECTING:2,INJECTED:3,READY:4},AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE,AdapterJS.onwebrtcreadyDone=!1,AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS={NONE:"NONE",ERROR:"ERROR",WARNING:"WARNING",INFO:"INFO",VERBOSE:"VERBOSE",SENSITIVE:"SENSITIVE"},AdapterJS.WebRTCPlugin.WaitForPluginReady=null,AdapterJS.WebRTCPlugin.callWhenPluginReady=null,__TemWebRTCReady0=function(){if("complete"===document.readyState)AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady();else var timer=setInterval(function(){"complete"===document.readyState&&(clearInterval(timer),AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady())},100)},AdapterJS.maybeThroughWebRTCReady=function(){AdapterJS.onwebrtcreadyDone||(AdapterJS.onwebrtcreadyDone=!0,AdapterJS._onwebrtcreadies.length?AdapterJS._onwebrtcreadies.forEach(function(callback){"function"==typeof callback&&callback(null!==AdapterJS.WebRTCPlugin.plugin)}):"function"==typeof AdapterJS.onwebrtcready&&AdapterJS.onwebrtcready(null!==AdapterJS.WebRTCPlugin.plugin))},AdapterJS.TEXT={PLUGIN:{REQUIRE_INSTALLATION:"This website requires you to install a WebRTC-enabling plugin to work on this browser.",NOT_SUPPORTED:"Your browser does not support WebRTC.",BUTTON:"Install Now"},REFRESH:{REQUIRE_REFRESH:"Please refresh page",BUTTON:"Refresh Page"}},AdapterJS._iceConnectionStates={starting:"starting",checking:"checking",connected:"connected",completed:"connected",done:"completed",disconnected:"disconnected",failed:"failed",closed:"closed"},AdapterJS._iceConnectionFiredStates=[],AdapterJS.isDefined=null,AdapterJS.parseWebrtcDetectedBrowser=function(){var hasMatch=null;window.opr&&opr.addons||window.opera||navigator.userAgent.indexOf(" OPR/")>=0?(webrtcDetectedBrowser="opera",webrtcDetectedType="webkit",webrtcMinimumVersion=26,hasMatch=/OPR\/(\d+)/i.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1],10)):"undefined"!=typeof InstallTrigger?webrtcDetectedType="moz":Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0?(webrtcDetectedBrowser="safari",webrtcDetectedType="plugin",webrtcMinimumVersion=7,hasMatch=/version\/(\d+)/i.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1],10)):document.documentMode?(webrtcDetectedBrowser="IE",webrtcDetectedType="plugin",webrtcMinimumVersion=9,hasMatch=/\brv[ :]+(\d+)/g.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1]||"0",10),webrtcDetectedVersion||(hasMatch=/\bMSIE[ :]+(\d+)/g.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1]||"0",10))):window.StyleMedia?webrtcDetectedType="":window.chrome&&window.chrome.webstore?webrtcDetectedType="webkit":"chrome"!==webrtcDetectedBrowser&&"opera"!==webrtcDetectedBrowser||!window.CSS||(webrtcDetectedBrowser="blink"),window.webrtcDetectedBrowser=webrtcDetectedBrowser,window.webrtcDetectedVersion=webrtcDetectedVersion,window.webrtcMinimumVersion=webrtcMinimumVersion},AdapterJS.addEvent=function(elem,evnt,func){elem.addEventListener?elem.addEventListener(evnt,func,!1):elem.attachEvent?elem.attachEvent("on"+evnt,func):elem[evnt]=func},AdapterJS.renderNotificationBar=function(text,buttonText,buttonLink,openNewTab,displayRefreshBar){if("complete"===document.readyState){var w=window,i=document.createElement("iframe");i.name="adapterjs-alert",i.style.position="fixed",i.style.top="-41px",i.style.left=0,i.style.right=0,i.style.width="100%",i.style.height="40px",i.style.backgroundColor="#ffffe1",i.style.border="none",i.style.borderBottom="1px solid #888888",i.style.zIndex="9999999","string"==typeof i.style.webkitTransition?i.style.webkitTransition="all .5s ease-out":"string"==typeof i.style.transition&&(i.style.transition="all .5s ease-out"),document.body.appendChild(i);var c=i.contentWindow?i.contentWindow:i.contentDocument.document?i.contentDocument.document:i.contentDocument;c.document.open(),c.document.write(''+text+""),buttonText&&buttonLink?(c.document.write(''),c.document.close(),AdapterJS.addEvent(c.document.getElementById("okay"),"click",function(e){displayRefreshBar&&AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION?AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH:AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH,AdapterJS.TEXT.REFRESH.BUTTON,"javascript:location.reload()"),window.open(buttonLink,openNewTab?"_blank":"_top"),e.preventDefault();try{e.cancelBubble=!0}catch(error){}var pluginInstallInterval=setInterval(function(){isIE||navigator.plugins.refresh(!1),AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,function(){clearInterval(pluginInstallInterval),AdapterJS.WebRTCPlugin.defineWebRTCInterface()},function(){})},500)}),AdapterJS.addEvent(c.document.getElementById("cancel"),"click",function(e){w.document.body.removeChild(i)})):c.document.close(),setTimeout(function(){"string"==typeof i.style.webkitTransform?i.style.webkitTransform="translateY(40px)":"string"==typeof i.style.transform?i.style.transform="translateY(40px)":i.style.top="0px"},300)}},webrtcDetectedType=null,checkMediaDataChannelSettings=function(peerBrowserAgent,peerBrowserVersion,callback,constraints){if("function"==typeof callback){var beOfferer=!0,isLocalFirefox="firefox"===webrtcDetectedBrowser,isLocalFirefoxInterop="moz"===webrtcDetectedType&&webrtcDetectedVersion>30,isPeerFirefox="firefox"===peerBrowserAgent;if(isLocalFirefox&&isPeerFirefox||isLocalFirefoxInterop)try{delete constraints.mandatory.MozDontOfferDataChannel}catch(error){}else isLocalFirefox&&!isPeerFirefox&&(constraints.mandatory.MozDontOfferDataChannel=!0);if(!isLocalFirefox)for(var prop in constraints.mandatory)constraints.mandatory.hasOwnProperty(prop)&&-1!==prop.indexOf("Moz")&&delete constraints.mandatory[prop];!isLocalFirefox||isPeerFirefox||isLocalFirefoxInterop||(beOfferer=!1),callback(beOfferer,constraints)}},checkIceConnectionState=function(peerId,iceConnectionState,callback){"function"==typeof callback&&(peerId=peerId?peerId:"peer",AdapterJS._iceConnectionFiredStates[peerId]&&iceConnectionState!==AdapterJS._iceConnectionStates.disconnected&&iceConnectionState!==AdapterJS._iceConnectionStates.failed&&iceConnectionState!==AdapterJS._iceConnectionStates.closed||(AdapterJS._iceConnectionFiredStates[peerId]=[]),iceConnectionState=AdapterJS._iceConnectionStates[iceConnectionState],AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState)<0&&(AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState),iceConnectionState===AdapterJS._iceConnectionStates.connected&&setTimeout(function(){AdapterJS._iceConnectionFiredStates[peerId].push(AdapterJS._iceConnectionStates.done),callback(AdapterJS._iceConnectionStates.done)},1e3),callback(iceConnectionState)))},createIceServer=null,createIceServers=null,RTCPeerConnection=null,RTCSessionDescription="function"==typeof RTCSessionDescription?RTCSessionDescription:null,RTCIceCandidate="function"==typeof RTCIceCandidate?RTCIceCandidate:null,getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,navigator.mozGetUserMedia||navigator.webkitGetUserMedia||navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)){var getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils={log:function(){"undefined"!=typeof module||"function"==typeof require&&"function"==typeof define},extractVersion:function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos],10)}};if("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=URL.createObjectURL(stream))}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),attachMediaStream=function(element,stream){element.srcObject=stream},reattachMediaStream=function(to,from){to.srcObject=from.srcObject},"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia){if(webrtcUtils.log("This appears to be Firefox"),webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,window.RTCPeerConnection||(window.RTCPeerConnection=function(pcConfig,pcConstraints){if(38>webrtcDetectedVersion&&pcConfig&&pcConfig.iceServers){for(var newIceServers=[],i=0;iwebrtcDetectedVersion&&(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){webrtcUtils.log("This appears to be Chrome"),webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38,window.RTCPeerConnection=function(pcConfig,pcConstraints){pcConfig&&pcConfig.iceTransportPolicy&&(pcConfig.iceTransports=pcConfig.iceTransportPolicy);var pc=new webkitRTCPeerConnection(pcConfig,pcConstraints),origGetStats=pc.getStats.bind(pc);return pc.getStats=function(selector,successCallback,errorCallback){var self=this,args=arguments;if(arguments.length>0&&"function"==typeof selector)return origGetStats(selector,successCallback);var fixChromeStats=function(response){var standardReport={},reports=response.result();return reports.forEach(function(report){var standardStats={id:report.id,timestamp:report.timestamp,type:report.type};report.names().forEach(function(name){standardStats[name]=report.stat(name)}),standardReport[standardStats.id]=standardStats}),standardReport};if(arguments.length>=2){var successCallbackWrapper=function(response){args[1](fixChromeStats(response))};return origGetStats.apply(this,[successCallbackWrapper,arguments[0]])}return new Promise(function(resolve,reject){1===args.length&&null===selector?origGetStats.apply(self,[function(response){resolve.apply(null,[fixChromeStats(response)])},reject]):origGetStats.apply(self,[resolve,reject])})},pc},webkitRTCPeerConnection.generateCertificate&&Object.defineProperty(window.RTCPeerConnection,"generateCertificate",{get:function(){return arguments.length?webkitRTCPeerConnection.generateCertificate.apply(null,arguments):webkitRTCPeerConnection.generateCertificate}}),["createOffer","createAnswer"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var self=this;if(arguments.length<1||1===arguments.length&&"object"==typeof arguments[0]){var opts=1===arguments.length?arguments[0]:void 0;return new Promise(function(resolve,reject){nativeMethod.apply(self,[resolve,reject,opts])})}return nativeMethod.apply(this,arguments)}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var args=arguments,self=this;return new Promise(function(resolve,reject){nativeMethod.apply(self,[args[0],function(){resolve(),args.length>=2&&args[1].apply(null,[])},function(err){reject(err),args.length>=3&&args[2].apply(null,[err])}])})}});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,enumerateDevices:function(){return new Promise(function(resolve){var kinds={audio:"audioinput",video:"videoinput"};return MediaStreamTrack.getSources(function(devices){resolve(devices.map(function(device){return{label:device.label,kind:kinds[device.kind],deviceId:device.id,groupId:""}}))})})}}),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.")}),attachMediaStream=function(element,stream){webrtcDetectedVersion>=43?element.srcObject=stream:"undefined"!=typeof element.src?element.src=URL.createObjectURL(stream):webrtcUtils.log("Error attaching stream to element.")},reattachMediaStream=function(to,from){webrtcDetectedVersion>=43?to.srcObject=from.srcObject:to.src=from.src}}else if(navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)){if(webrtcUtils.log("This appears to be Edge"),webrtcDetectedBrowser="edge",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),webrtcMinimumVersion=10547,window.RTCIceGatherer){var generateIdentifier=function(){return Math.random().toString(36).substr(2,10)},localCName=generateIdentifier(),SDPUtils={};SDPUtils.splitLines=function(blob){return blob.trim().split("\n").map(function(line){return line.trim()})},SDPUtils.splitSections=function(blob){var parts=blob.split("\r\nm=");return parts.map(function(part,index){return(index>0?"m="+part:part).trim()+"\r\n"})},SDPUtils.matchPrefix=function(blob,prefix){return SDPUtils.splitLines(blob).filter(function(line){return 0===line.indexOf(prefix)})},SDPUtils.parseCandidate=function(line){var parts;parts=0===line.indexOf("a=candidate:")?line.substring(12).split(" "):line.substring(10).split(" ");for(var candidate={foundation:parts[0],component:parts[1],protocol:parts[2].toLowerCase(),priority:parseInt(parts[3],10),ip:parts[4],port:parseInt(parts[5],10),type:parts[7]},i=8;i-1?(parts.attribute=line.substr(sp+1,colon-sp-1),parts.value=line.substr(colon+1)):parts.attribute=line.substr(sp+1),parts},SDPUtils.getDtlsParameters=function(mediaSection,sessionpart){var lines=SDPUtils.splitLines(mediaSection);lines=lines.concat(SDPUtils.splitLines(sessionpart));var fpLine=lines.filter(function(line){return 0===line.indexOf("a=fingerprint:")})[0].substr(14),dtlsParameters={role:"auto",fingerprints:[{algorithm:fpLine.split(" ")[0],value:fpLine.split(" ")[1]}]};return dtlsParameters},SDPUtils.writeDtlsParameters=function(params,setupType){var sdp="a=setup:"+setupType+"\r\n";return params.fingerprints.forEach(function(fp){sdp+="a=fingerprint:"+fp.algorithm+" "+fp.value+"\r\n"}),sdp},SDPUtils.getIceParameters=function(mediaSection,sessionpart){var lines=SDPUtils.splitLines(mediaSection);lines=lines.concat(SDPUtils.splitLines(sessionpart));var iceParameters={usernameFragment:lines.filter(function(line){return 0===line.indexOf("a=ice-ufrag:")})[0].substr(12),password:lines.filter(function(line){return 0===line.indexOf("a=ice-pwd:")})[0].substr(10)};return iceParameters},SDPUtils.writeIceParameters=function(params){return"a=ice-ufrag:"+params.usernameFragment+"\r\na=ice-pwd:"+params.password+"\r\n"},SDPUtils.parseRtpParameters=function(mediaSection){for(var description={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},lines=SDPUtils.splitLines(mediaSection),mline=lines[0].split(" "),i=3;i0?"9":"0",sdp+=" UDP/TLS/RTP/SAVPF ",sdp+=caps.codecs.map(function(codec){return void 0!==codec.preferredPayloadType?codec.preferredPayloadType:codec.payloadType}).join(" ")+"\r\n",sdp+="c=IN IP4 0.0.0.0\r\n",sdp+="a=rtcp:9 IN IP4 0.0.0.0\r\n",caps.codecs.forEach(function(codec){sdp+=SDPUtils.writeRtpMap(codec),sdp+=SDPUtils.writeFtmp(codec),sdp+=SDPUtils.writeRtcpFb(codec)}),sdp+="a=rtcp-mux\r\n"},SDPUtils.writeSessionBoilerplate=function(){return"v=0\r\no=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},SDPUtils.writeMediaSection=function(transceiver,caps,type,stream){var sdp=SDPUtils.writeRtpDescription(transceiver.kind,caps);if(sdp+=SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters()),sdp+=SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(),"offer"===type?"actpass":"active"),sdp+="a=mid:"+transceiver.mid+"\r\n",sdp+=transceiver.rtpSender&&transceiver.rtpReceiver?"a=sendrecv\r\n":transceiver.rtpSender?"a=sendonly\r\n":transceiver.rtpReceiver?"a=recvonly\r\n":"a=inactive\r\n",transceiver.rtpSender){var msid="msid:"+stream.id+" "+transceiver.rtpSender.track.id+"\r\n";sdp+="a="+msid,sdp+="a=ssrc:"+transceiver.sendSsrc+" "+msid}return sdp+="a=ssrc:"+transceiver.sendSsrc+" cname:"+localCName+"\r\n"},SDPUtils.getDirection=function(mediaSection,sessionpart){for(var lines=SDPUtils.splitLines(mediaSection),i=0;i-1&&(this.localStreams.splice(idx,1),this._maybeFireNegotiationNeeded())},window.RTCPeerConnection.prototype._getCommonCapabilities=function(localCapabilities,remoteCapabilities){var commonCapabilities={codecs:[],headerExtensions:[],fecMechanisms:[]};return localCapabilities.codecs.forEach(function(lCodec){for(var i=0;i0,!1)}})}switch(this.localDescription=description,
+description.type){case"offer":this._updateSignalingState("have-local-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+description.type+'"')}var hasCallback=arguments.length>1&&"function"==typeof arguments[1];if(hasCallback){var cb=arguments[1];window.setTimeout(function(){cb(),self._emitBufferedCandidates()},0)}var p=Promise.resolve();return p.then(function(){hasCallback||window.setTimeout(self._emitBufferedCandidates.bind(self),0)}),p},window.RTCPeerConnection.prototype.setRemoteDescription=function(description){var self=this,stream=new MediaStream,sections=SDPUtils.splitSections(description.sdp),sessionpart=sections.shift();switch(sections.forEach(function(mediaSection,sdpMLineIndex){var transceiver,iceGatherer,iceTransport,dtlsTransport,rtpSender,rtpReceiver,sendSsrc,recvSsrc,localCapabilities,remoteIceParameters,remoteDtlsParameters,lines=SDPUtils.splitLines(mediaSection),mline=lines[0].substr(2).split(" "),kind=mline[0],rejected="0"===mline[1],direction=SDPUtils.getDirection(mediaSection,sessionpart),remoteCapabilities=SDPUtils.parseRtpParameters(mediaSection);rejected||(remoteIceParameters=SDPUtils.getIceParameters(mediaSection,sessionpart),remoteDtlsParameters=SDPUtils.getDtlsParameters(mediaSection,sessionpart));var cname,mid=SDPUtils.matchPrefix(mediaSection,"a=mid:")[0].substr(6),remoteSsrc=SDPUtils.matchPrefix(mediaSection,"a=ssrc:").map(function(line){return SDPUtils.parseSsrcMedia(line)}).filter(function(obj){return"cname"===obj.attribute})[0];if(remoteSsrc&&(recvSsrc=parseInt(remoteSsrc.ssrc,10),cname=remoteSsrc.value),"offer"===description.type){var transports=self._createIceAndDtlsTransports(mid,sdpMLineIndex);if(localCapabilities=RTCRtpReceiver.getCapabilities(kind),sendSsrc=1001*(2*sdpMLineIndex+2),rtpReceiver=new RTCRtpReceiver(transports.dtlsTransport,kind),stream.addTrack(rtpReceiver.track),self.localStreams.length>0&&self.localStreams[0].getTracks().length>=sdpMLineIndex){var localtrack=self.localStreams[0].getTracks()[sdpMLineIndex];rtpSender=new RTCRtpSender(localtrack,transports.dtlsTransport)}self.transceivers[sdpMLineIndex]={iceGatherer:transports.iceGatherer,iceTransport:transports.iceTransport,dtlsTransport:transports.dtlsTransport,localCapabilities:localCapabilities,remoteCapabilities:remoteCapabilities,rtpSender:rtpSender,rtpReceiver:rtpReceiver,kind:kind,mid:mid,cname:cname,sendSsrc:sendSsrc,recvSsrc:recvSsrc},self._transceive(self.transceivers[sdpMLineIndex],!1,"sendrecv"===direction||"sendonly"===direction)}else"answer"!==description.type||rejected||(transceiver=self.transceivers[sdpMLineIndex],iceGatherer=transceiver.iceGatherer,iceTransport=transceiver.iceTransport,dtlsTransport=transceiver.dtlsTransport,rtpSender=transceiver.rtpSender,rtpReceiver=transceiver.rtpReceiver,sendSsrc=transceiver.sendSsrc,localCapabilities=transceiver.localCapabilities,self.transceivers[sdpMLineIndex].recvSsrc=recvSsrc,self.transceivers[sdpMLineIndex].remoteCapabilities=remoteCapabilities,self.transceivers[sdpMLineIndex].cname=cname,iceTransport.start(iceGatherer,remoteIceParameters,"controlling"),dtlsTransport.start(remoteDtlsParameters),self._transceive(transceiver,"sendrecv"===direction||"recvonly"===direction,"sendrecv"===direction||"sendonly"===direction),!rtpReceiver||"sendrecv"!==direction&&"sendonly"!==direction?delete transceiver.rtpReceiver:stream.addTrack(rtpReceiver.track))}),this.remoteDescription=description,description.type){case"offer":this._updateSignalingState("have-remote-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+description.type+'"')}return window.setTimeout(function(){null!==self.onaddstream&&stream.getTracks().length&&(self.remoteStreams.push(stream),window.setTimeout(function(){self.onaddstream({stream:stream})},0))},0),arguments.length>1&&"function"==typeof arguments[1]&&window.setTimeout(arguments[1],0),Promise.resolve()},window.RTCPeerConnection.prototype.close=function(){this.transceivers.forEach(function(transceiver){transceiver.iceTransport&&transceiver.iceTransport.stop(),transceiver.dtlsTransport&&transceiver.dtlsTransport.stop(),transceiver.rtpSender&&transceiver.rtpSender.stop(),transceiver.rtpReceiver&&transceiver.rtpReceiver.stop()}),this._updateSignalingState("closed")},window.RTCPeerConnection.prototype._updateSignalingState=function(newState){this.signalingState=newState,null!==this.onsignalingstatechange&&this.onsignalingstatechange()},window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded=function(){null!==this.onnegotiationneeded&&this.onnegotiationneeded()},window.RTCPeerConnection.prototype._updateConnectionState=function(){var newState,self=this,states={"new":0,closed:0,connecting:0,checking:0,connected:0,completed:0,failed:0};this.transceivers.forEach(function(transceiver){states[transceiver.iceTransport.state]++,states[transceiver.dtlsTransport.state]++}),states.connected+=states.completed,newState="new",states.failed>0?newState="failed":states.connecting>0||states.checking>0?newState="connecting":states.disconnected>0?newState="disconnected":states["new"]>0?newState="new":(states.connecting>0||states.completed>0)&&(newState="connected"),newState!==self.iceConnectionState&&(self.iceConnectionState=newState,null!==this.oniceconnectionstatechange&&this.oniceconnectionstatechange())},window.RTCPeerConnection.prototype.createOffer=function(){var self=this;if(this._pendingOffer)throw new Error("createOffer called while there is a pending offer.");var offerOptions;1===arguments.length&&"function"!=typeof arguments[0]?offerOptions=arguments[0]:3===arguments.length&&(offerOptions=arguments[2]);var tracks=[],numAudioTracks=0,numVideoTracks=0;if(this.localStreams.length&&(numAudioTracks=this.localStreams[0].getAudioTracks().length,numVideoTracks=this.localStreams[0].getVideoTracks().length),offerOptions){if(offerOptions.mandatory||offerOptions.optional)throw new TypeError("Legacy mandatory/optional constraints not supported.");void 0!==offerOptions.offerToReceiveAudio&&(numAudioTracks=offerOptions.offerToReceiveAudio),void 0!==offerOptions.offerToReceiveVideo&&(numVideoTracks=offerOptions.offerToReceiveVideo)}for(this.localStreams.length&&this.localStreams[0].getTracks().forEach(function(track){tracks.push({kind:track.kind,track:track,wantReceive:"audio"===track.kind?numAudioTracks>0:numVideoTracks>0}),"audio"===track.kind?numAudioTracks--:"video"===track.kind&&numVideoTracks--});numAudioTracks>0||numVideoTracks>0;)numAudioTracks>0&&(tracks.push({kind:"audio",wantReceive:!0}),numAudioTracks--),numVideoTracks>0&&(tracks.push({kind:"video",wantReceive:!0}),numVideoTracks--);var sdp=SDPUtils.writeSessionBoilerplate(),transceivers=[];tracks.forEach(function(mline,sdpMLineIndex){var rtpSender,rtpReceiver,track=mline.track,kind=mline.kind,mid=generateIdentifier(),transports=self._createIceAndDtlsTransports(mid,sdpMLineIndex),localCapabilities=RTCRtpSender.getCapabilities(kind),sendSsrc=1001*(2*sdpMLineIndex+1);track&&(rtpSender=new RTCRtpSender(track,transports.dtlsTransport)),mline.wantReceive&&(rtpReceiver=new RTCRtpReceiver(transports.dtlsTransport,kind)),transceivers[sdpMLineIndex]={iceGatherer:transports.iceGatherer,iceTransport:transports.iceTransport,dtlsTransport:transports.dtlsTransport,localCapabilities:localCapabilities,remoteCapabilities:null,rtpSender:rtpSender,rtpReceiver:rtpReceiver,kind:kind,mid:mid,sendSsrc:sendSsrc,recvSsrc:null};var transceiver=transceivers[sdpMLineIndex];sdp+=SDPUtils.writeMediaSection(transceiver,transceiver.localCapabilities,"offer",self.localStreams[0])}),this._pendingOffer=transceivers;var desc=new RTCSessionDescription({type:"offer",sdp:sdp});return arguments.length&&"function"==typeof arguments[0]&&window.setTimeout(arguments[0],0,desc),Promise.resolve(desc)},window.RTCPeerConnection.prototype.createAnswer=function(){var answerOptions,self=this;1===arguments.length&&"function"!=typeof arguments[0]?answerOptions=arguments[0]:3===arguments.length&&(answerOptions=arguments[2]);var sdp=SDPUtils.writeSessionBoilerplate();this.transceivers.forEach(function(transceiver){var commonCapabilities=self._getCommonCapabilities(transceiver.localCapabilities,transceiver.remoteCapabilities);sdp+=SDPUtils.writeMediaSection(transceiver,commonCapabilities,"answer",self.localStreams[0])});var desc=new RTCSessionDescription({type:"answer",sdp:sdp});return arguments.length&&"function"==typeof arguments[0]&&window.setTimeout(arguments[0],0,desc),Promise.resolve(desc)},window.RTCPeerConnection.prototype.addIceCandidate=function(candidate){var mLineIndex=candidate.sdpMLineIndex;if(candidate.sdpMid)for(var i=0;i0?SDPUtils.parseCandidate(candidate.candidate):{};if("tcp"===cand.protocol&&0===cand.port)return;if("1"!==cand.component)return;"endOfCandidates"===cand.type&&(cand={}),transceiver.iceTransport.addRemoteCandidate(cand)}return arguments.length>1&&"function"==typeof arguments[1]&&window.setTimeout(arguments[1],0),Promise.resolve()},window.RTCPeerConnection.prototype.getStats=function(){var promises=[];this.transceivers.forEach(function(transceiver){["rtpSender","rtpReceiver","iceGatherer","iceTransport","dtlsTransport"].forEach(function(method){transceiver[method]&&promises.push(transceiver[method].getStats())})});var cb=arguments.length>1&&"function"==typeof arguments[1]&&arguments[1];return new Promise(function(resolve){var results={};Promise.all(promises).then(function(res){res.forEach(function(result){Object.keys(result).forEach(function(id){results[id]=result[id]})}),cb&&window.setTimeout(cb,0,results),resolve(results)})})}}}else webrtcUtils.log("Browser does not appear to be WebRTC-capable");else webrtcUtils.log("This does not appear to be a browser"),webrtcDetectedBrowser="not a browser";var webrtcTesting={};try{Object.defineProperty(webrtcTesting,"version",{set:function(version){webrtcDetectedVersion=version}})}catch(e){}AdapterJS.parseWebrtcDetectedBrowser(),navigator.mozGetUserMedia?(MediaStreamTrack.getSources=function(successCb){setTimeout(function(){var infos=[{kind:"audio",id:"default",label:"",facing:""},{kind:"video",id:"default",label:"",facing:""}];successCb(infos)},0)},createIceServer=function(url,username,password){var iceServer=null,urlParts=url.split(":");if(0===urlParts[0].indexOf("stun"))iceServer={urls:[url]};else if(0===urlParts[0].indexOf("turn"))if(27>webrtcDetectedVersion){var turnUrlParts=url.split("?");(1===turnUrlParts.length||0===turnUrlParts[1].indexOf("transport=udp"))&&(iceServer={urls:[turnUrlParts[0]],credential:password,username:username})}else iceServer={urls:[url],credential:password,username:username};return iceServer},createIceServers=function(urls,username,password){var iceServers=[];for(i=0;i=34)iceServers={urls:urls,credential:password,username:username};else for(i=0;i=webrtcDetectedVersion){var frag=document.createDocumentFragment();for(AdapterJS.WebRTCPlugin.plugin=document.createElement("div"),AdapterJS.WebRTCPlugin.plugin.innerHTML='";AdapterJS.WebRTCPlugin.plugin.firstChild;)frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);document.body.appendChild(frag),AdapterJS.WebRTCPlugin.plugin=document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId)}else AdapterJS.WebRTCPlugin.plugin=document.createElement("object"),AdapterJS.WebRTCPlugin.plugin.id=AdapterJS.WebRTCPlugin.pluginInfo.pluginId,isIE?(AdapterJS.WebRTCPlugin.plugin.width="1px",AdapterJS.WebRTCPlugin.plugin.height="1px"):(AdapterJS.WebRTCPlugin.plugin.width="0px",AdapterJS.WebRTCPlugin.plugin.height="0px"),AdapterJS.WebRTCPlugin.plugin.type=AdapterJS.WebRTCPlugin.pluginInfo.type,AdapterJS.WebRTCPlugin.plugin.innerHTML=' '+(AdapterJS.options.getAllCams?'':"")+'',document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED}},AdapterJS.WebRTCPlugin.isPluginInstalled=function(comName,plugName,installedCb,notInstalledCb){if(isIE){try{new ActiveXObject(comName+"."+plugName)}catch(e){return void notInstalledCb()}installedCb()}else{for(var pluginArray=navigator.plugins,i=0;i=0)return void installedCb();notInstalledCb()}},AdapterJS.WebRTCPlugin.defineWebRTCInterface=function(){AdapterJS.WebRTCPlugin.pluginState!==AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY&&(AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING,AdapterJS.isDefined=function(variable){return null!==variable&&void 0!==variable},createIceServer=function(url,username,password){var iceServer=null,urlParts=url.split(":");return 0===urlParts[0].indexOf("stun")?iceServer={url:url,hasCredentials:!1}:0===urlParts[0].indexOf("turn")&&(iceServer={url:url,hasCredentials:!0,credential:password,username:username}),iceServer},createIceServers=function(urls,username,password){for(var iceServers=[],i=0;i1)return AdapterJS.WebRTCPlugin.plugin.PeerConnection(servers);var iceServers=null;if(servers&&Array.isArray(servers.iceServers)){iceServers=servers.iceServers;for(var i=0;i ';temp.firstChild;)frag.appendChild(temp.firstChild);var height="",width="";element.clientWidth||element.clientHeight?(width=element.clientWidth,height=element.clientHeight):(element.width||element.height)&&(width=element.width,height=element.height),element.parentNode.insertBefore(frag,element),frag=document.getElementById(elementId),frag.width=width,frag.height=height,element.parentNode.removeChild(element)}else{for(var children=element.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){children[i].value=streamId;break}element.setStreamId(streamId)}var newElement=document.getElementById(elementId);return AdapterJS.forwardEventHandlers(newElement,element,Object.getPrototypeOf(element)),newElement}},reattachMediaStream=function(to,from){for(var stream=null,children=from.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){AdapterJS.WebRTCPlugin.WaitForPluginReady(),stream=AdapterJS.WebRTCPlugin.plugin.getStreamWithId(AdapterJS.WebRTCPlugin.pageId,children[i].value);break}return null!==stream?attachMediaStream(to,stream):void 0},window.attachMediaStream=attachMediaStream,window.reattachMediaStream=reattachMediaStream,window.getUserMedia=getUserMedia,AdapterJS.attachMediaStream=attachMediaStream,AdapterJS.reattachMediaStream=reattachMediaStream,AdapterJS.getUserMedia=getUserMedia,AdapterJS.forwardEventHandlers=function(destElem,srcElem,prototype){properties=Object.getOwnPropertyNames(prototype);for(var prop in properties)prop&&(propName=properties[prop],"function"==typeof propName.slice&&"on"===propName.slice(0,2)&&"function"==typeof srcElem[propName]&&AdapterJS.addEvent(destElem,propName.slice(2),srcElem[propName]));var subPrototype=Object.getPrototypeOf(prototype);subPrototype&&AdapterJS.forwardEventHandlers(destElem,srcElem,subPrototype)},RTCIceCandidate=function(candidate){return candidate.sdpMid||(candidate.sdpMid=""),AdapterJS.WebRTCPlugin.WaitForPluginReady(),AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(candidate.sdpMid,candidate.sdpMLineIndex,candidate.candidate)},AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.injectPlugin),AdapterJS.WebRTCPlugin.injectPlugin())},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb=AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb||function(){AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv),AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv()},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv=function(){if(!AdapterJS.options.hidePluginInstallPrompt){var downloadLink=AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;if(downloadLink){var popupString;popupString=AdapterJS.WebRTCPlugin.pluginInfo.portalLink?'This website requires you to install the '+AdapterJS.WebRTCPlugin.pluginInfo.companyName+" WebRTC Plugin to work on this browser.":AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION,AdapterJS.renderNotificationBar(popupString,AdapterJS.TEXT.PLUGIN.BUTTON,downloadLink)}else AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED)}},AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,AdapterJS.WebRTCPlugin.defineWebRTCInterface,AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
\ No newline at end of file
diff --git a/publish/adapter.screenshare.js b/publish/adapter.screenshare.js
index a3b65b9..e4d15b3 100644
--- a/publish/adapter.screenshare.js
+++ b/publish/adapter.screenshare.js
@@ -1,4 +1,4 @@
-/*! adapterjs - v0.13.0 - 2016-01-08 */
+/*! adapterjs - v0.13.1 - 2016-03-15 */
// Adapter's interface.
var AdapterJS = AdapterJS || {};
@@ -17,7 +17,7 @@ AdapterJS.options = AdapterJS.options || {};
// AdapterJS.options.hidePluginInstallPrompt = true;
// AdapterJS version
-AdapterJS.VERSION = '0.13.0';
+AdapterJS.VERSION = '0.13.1';
// This function will be called when the WebRTC API is ready to be used
// Whether it is the native implementation (Chrome, Firefox, Opera) or
@@ -58,6 +58,7 @@ AdapterJS.webRTCReady = function (callback) {
AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {};
// The object to store plugin information
+/* jshint ignore:start */
AdapterJS.WebRTCPlugin.pluginInfo = {
prefix : 'Tem',
plugName : 'TemWebRTCPlugin',
@@ -74,6 +75,7 @@ if(!!navigator.platform.match(/^Mac/i)) {
else if(!!navigator.platform.match(/^Win/i)) {
AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN';
}
+/* jshint ignore:end */
AdapterJS.WebRTCPlugin.TAGS = {
NONE : 'none',
@@ -160,16 +162,14 @@ AdapterJS.WebRTCPlugin.callWhenPluginReady = null;
// This function is the only private function that is not encapsulated to
// allow the plugin method to be called.
__TemWebRTCReady0 = function () {
- webrtcDetectedVersion = AdapterJS.WebRTCPlugin.plugin.version;
-
if (document.readyState === 'complete') {
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
AdapterJS.maybeThroughWebRTCReady();
} else {
- AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () {
+ var timer = setInterval(function () {
if (document.readyState === 'complete') {
// TODO: update comments, we wait for the document to be ready
- clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval);
+ clearInterval(timer);
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
AdapterJS.maybeThroughWebRTCReady();
}
@@ -240,65 +240,62 @@ AdapterJS.isDefined = null;
// This sets:
// - webrtcDetectedBrowser: The browser agent name.
// - webrtcDetectedVersion: The browser version.
+// - webrtcMinimumVersion: The minimum browser version still supported by AJS.
// - webrtcDetectedType: The types of webRTC support.
// - 'moz': Mozilla implementation of webRTC.
// - 'webkit': WebKit implementation of webRTC.
// - 'plugin': Using the plugin implementation.
AdapterJS.parseWebrtcDetectedBrowser = function () {
- var hasMatch, checkMatch = navigator.userAgent.match(
- /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
- if (/trident/i.test(checkMatch[1])) {
- hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ var hasMatch = null;
+ if ((!!window.opr && !!opr.addons) ||
+ !!window.opera ||
+ navigator.userAgent.indexOf(' OPR/') >= 0) {
+ // Opera 8.0+
+ webrtcDetectedBrowser = 'opera';
+ webrtcDetectedType = 'webkit';
+ webrtcMinimumVersion = 26;
+ hasMatch = /OPR\/(\d+)/i.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ } else if (typeof InstallTrigger !== 'undefined') {
+ // Firefox 1.0+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = 'moz';
+ } else if (Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
+ // Safari
+ webrtcDetectedBrowser = 'safari';
+ webrtcDetectedType = 'plugin';
+ webrtcMinimumVersion = 7;
+ hasMatch = /version\/(\d+)/i.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ } else if (/*@cc_on!@*/false || !!document.documentMode) {
+ // Internet Explorer 6-11
webrtcDetectedBrowser = 'IE';
+ webrtcDetectedType = 'plugin';
+ webrtcMinimumVersion = 9;
+ hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
- } else if (checkMatch[1] === 'Chrome') {
- hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/);
- if (hasMatch !== null) {
- webrtcDetectedBrowser = 'opera';
- webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ if (!webrtcDetectedVersion) {
+ hasMatch = /\bMSIE[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
}
+ } else if (!!window.StyleMedia) {
+ // Edge 20+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = '';
+ } else if (!!window.chrome && !!window.chrome.webstore) {
+ // Chrome 1+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = 'webkit';
+ } else if ((webrtcDetectedBrowser === 'chrome'|| webrtcDetectedBrowser === 'opera') &&
+ !!window.CSS) {
+ // Blink engine detection
+ webrtcDetectedBrowser = 'blink';
+ // TODO: detected WebRTC version
}
- if (navigator.userAgent.indexOf('Safari')) {
- if (typeof InstallTrigger !== 'undefined') {
- webrtcDetectedBrowser = 'firefox';
- } else if (/*@cc_on!@*/ false || !!document.documentMode) {
- webrtcDetectedBrowser = 'IE';
- } else if (
- Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
- webrtcDetectedBrowser = 'safari';
- } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
- webrtcDetectedBrowser = 'opera';
- } else if (!!window.chrome) {
- webrtcDetectedBrowser = 'chrome';
- }
- }
- if (!webrtcDetectedBrowser) {
- webrtcDetectedVersion = checkMatch[1];
- }
- if (!webrtcDetectedVersion) {
- try {
- checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] :
- [navigator.appName, navigator.appVersion, '-?'];
- if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) {
- checkMatch.splice(1, 1, hasMatch[1]);
- }
- webrtcDetectedVersion = parseInt(checkMatch[1], 10);
- } catch (error) { }
- }
-};
-// To fix configuration as some browsers does not support
-// the 'urls' attribute.
-AdapterJS.maybeFixConfiguration = function (pcConfig) {
- if (pcConfig === null) {
- return;
- }
- for (var i = 0; i < pcConfig.iceServers.length; i++) {
- if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
- pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
- delete pcConfig.iceServers[i].urls;
- }
- }
+ window.webrtcDetectedBrowser = webrtcDetectedBrowser;
+ window.webrtcDetectedVersion = webrtcDetectedVersion;
+ window.webrtcMinimumVersion = webrtcMinimumVersion;
};
AdapterJS.addEvent = function(elem, evnt, func) {
@@ -336,7 +333,7 @@ AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNe
i.style.transition = 'all .5s ease-out';
}
document.body.appendChild(i);
- c = (i.contentWindow) ? i.contentWindow :
+ var c = (i.contentWindow) ? i.contentWindow :
(i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
c.document.open();
c.document.write(' -1) {
+ parts.attribute = line.substr(sp + 1, colon - sp - 1);
+ parts.value = line.substr(colon + 1);
+ } else {
+ parts.attribute = line.substr(sp + 1);
+ }
+ return parts;
+ };
+
+ // Extracts DTLS parameters from SDP media section or sessionpart.
+ // FIXME: for consistency with other functions this should only
+ // get the fingerprint line as input. See also getIceParameters.
+ SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
+ var lines = SDPUtils.splitLines(mediaSection);
+ lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
+ var fpLine = lines.filter(function(line) {
+ return line.indexOf('a=fingerprint:') === 0;
+ })[0].substr(14);
+ // Note: a=setup line is ignored since we use the 'auto' role.
+ var dtlsParameters = {
+ role: 'auto',
+ fingerprints: [{
+ algorithm: fpLine.split(' ')[0],
+ value: fpLine.split(' ')[1]
+ }]
+ };
+ return dtlsParameters;
+ };
+
+ // Serializes DTLS parameters to SDP.
+ SDPUtils.writeDtlsParameters = function(params, setupType) {
+ var sdp = 'a=setup:' + setupType + '\r\n';
+ params.fingerprints.forEach(function(fp) {
+ sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
+ });
+ return sdp;
+ };
+ // Parses ICE information from SDP media section or sessionpart.
+ // FIXME: for consistency with other functions this should only
+ // get the ice-ufrag and ice-pwd lines as input.
+ SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
+ var lines = SDPUtils.splitLines(mediaSection);
+ lines = lines.concat(SDPUtils.splitLines(sessionpart)); // Search in session part, too.
+ var iceParameters = {
+ usernameFragment: lines.filter(function(line) {
+ return line.indexOf('a=ice-ufrag:') === 0;
+ })[0].substr(12),
+ password: lines.filter(function(line) {
+ return line.indexOf('a=ice-pwd:') === 0;
+ })[0].substr(10)
+ };
+ return iceParameters;
+ };
+
+ // Serializes ICE parameters to SDP.
+ SDPUtils.writeIceParameters = function(params) {
+ return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
+ 'a=ice-pwd:' + params.password + '\r\n';
+ };
+
+ // Parses the SDP media section and returns RTCRtpParameters.
+ SDPUtils.parseRtpParameters = function(mediaSection) {
+ var description = {
+ codecs: [],
+ headerExtensions: [],
+ fecMechanisms: [],
+ rtcp: []
+ };
+ var lines = SDPUtils.splitLines(mediaSection);
+ var mline = lines[0].split(' ');
+ for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
+ var pt = mline[i];
+ var rtpmapline = SDPUtils.matchPrefix(
+ mediaSection, 'a=rtpmap:' + pt + ' ')[0];
+ if (rtpmapline) {
+ var codec = SDPUtils.parseRtpMap(rtpmapline);
+ var fmtps = SDPUtils.matchPrefix(
+ mediaSection, 'a=fmtp:' + pt + ' ');
+ // Only the first a=fmtp: is considered.
+ codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
+ codec.rtcpFeedback = SDPUtils.matchPrefix(
+ mediaSection, 'a=rtcp-fb:' + pt + ' ')
+ .map(SDPUtils.parseRtcpFb);
+ description.codecs.push(codec);
+ }
+ }
+ // FIXME: parse headerExtensions, fecMechanisms and rtcp.
+ return description;
+ };
+
+ // Generates parts of the SDP media section describing the capabilities / parameters.
+ SDPUtils.writeRtpDescription = function(kind, caps) {
+ var sdp = '';
+
+ // Build the mline.
+ sdp += 'm=' + kind + ' ';
+ sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
+ sdp += ' UDP/TLS/RTP/SAVPF ';
+ sdp += caps.codecs.map(function(codec) {
+ if (codec.preferredPayloadType !== undefined) {
+ return codec.preferredPayloadType;
+ }
+ return codec.payloadType;
+ }).join(' ') + '\r\n';
+
+ sdp += 'c=IN IP4 0.0.0.0\r\n';
+ sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
+
+ // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
+ caps.codecs.forEach(function(codec) {
+ sdp += SDPUtils.writeRtpMap(codec);
+ sdp += SDPUtils.writeFtmp(codec);
+ sdp += SDPUtils.writeRtcpFb(codec);
+ });
+ // FIXME: add headerExtensions, fecMechanismÅŸ and rtcp.
+ sdp += 'a=rtcp-mux\r\n';
+ return sdp;
+ };
+
+ SDPUtils.writeSessionBoilerplate = function() {
+ // FIXME: sess-id should be an NTP timestamp.
+ return 'v=0\r\n' +
+ 'o=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\n' +
+ 's=-\r\n' +
+ 't=0 0\r\n';
+ };
+
+ SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
+ var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
+
+ // Map ICE parameters (ufrag, pwd) to SDP.
+ sdp += SDPUtils.writeIceParameters(
+ transceiver.iceGatherer.getLocalParameters());
+
+ // Map DTLS parameters to SDP.
+ sdp += SDPUtils.writeDtlsParameters(
+ transceiver.dtlsTransport.getLocalParameters(),
+ type === 'offer' ? 'actpass' : 'active');
+
+ sdp += 'a=mid:' + transceiver.mid + '\r\n';
+
+ if (transceiver.rtpSender && transceiver.rtpReceiver) {
+ sdp += 'a=sendrecv\r\n';
+ } else if (transceiver.rtpSender) {
+ sdp += 'a=sendonly\r\n';
+ } else if (transceiver.rtpReceiver) {
+ sdp += 'a=recvonly\r\n';
+ } else {
+ sdp += 'a=inactive\r\n';
+ }
+
+ // FIXME: for RTX there might be multiple SSRCs. Not implemented in Edge yet.
+ if (transceiver.rtpSender) {
+ var msid = 'msid:' + stream.id + ' ' +
+ transceiver.rtpSender.track.id + '\r\n';
+ sdp += 'a=' + msid;
+ sdp += 'a=ssrc:' + transceiver.sendSsrc + ' ' + msid;
+ }
+ // FIXME: this should be written by writeRtpDescription.
+ sdp += 'a=ssrc:' + transceiver.sendSsrc + ' cname:' +
+ localCName + '\r\n';
+ return sdp;
+ };
+
+ // Gets the direction from the mediaSection or the sessionpart.
+ SDPUtils.getDirection = function(mediaSection, sessionpart) {
+ // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
+ var lines = SDPUtils.splitLines(mediaSection);
+ for (var i = 0; i < lines.length; i++) {
+ switch (lines[i]) {
+ case 'a=sendrecv':
+ case 'a=sendonly':
+ case 'a=recvonly':
+ case 'a=inactive':
+ return lines[i].substr(2);
+ }
+ }
+ if (sessionpart) {
+ return SDPUtils.getDirection(sessionpart);
+ }
+ return 'sendrecv';
+ };
+
+ // ORTC defines an RTCIceCandidate object but no constructor.
+ // Not implemented in Edge.
+ if (!window.RTCIceCandidate) {
+ window.RTCIceCandidate = function(args) {
+ return args;
+ };
+ }
+ // ORTC does not have a session description object but
+ // other browsers (i.e. Chrome) that will support both PC and ORTC
+ // in the future might have this defined already.
+ if (!window.RTCSessionDescription) {
+ window.RTCSessionDescription = function(args) {
+ return args;
+ };
+ }
+
+ window.RTCPeerConnection = function(config) {
+ var self = this;
+
+ this.onicecandidate = null;
+ this.onaddstream = null;
+ this.onremovestream = null;
+ this.onsignalingstatechange = null;
+ this.oniceconnectionstatechange = null;
+ this.onnegotiationneeded = null;
+ this.ondatachannel = null;
+
+ this.localStreams = [];
+ this.remoteStreams = [];
+ this.getLocalStreams = function() { return self.localStreams; };
+ this.getRemoteStreams = function() { return self.remoteStreams; };
+
+ this.localDescription = new RTCSessionDescription({
+ type: '',
+ sdp: ''
+ });
+ this.remoteDescription = new RTCSessionDescription({
+ type: '',
+ sdp: ''
+ });
+ this.signalingState = 'stable';
+ this.iceConnectionState = 'new';
+
+ this.iceOptions = {
+ gatherPolicy: 'all',
+ iceServers: []
+ };
+ if (config && config.iceTransportPolicy) {
+ switch (config.iceTransportPolicy) {
+ case 'all':
+ case 'relay':
+ this.iceOptions.gatherPolicy = config.iceTransportPolicy;
+ break;
+ case 'none':
+ // FIXME: remove once implementation and spec have added this.
+ throw new TypeError('iceTransportPolicy "none" not supported');
+ }
+ }
+ if (config && config.iceServers) {
+ // Edge does not like
+ // 1) stun:
+ // 2) turn: that does not have all of turn:host:port?transport=udp
+ // 3) an array of urls
+ config.iceServers.forEach(function(server) {
+ if (server.urls) {
+ var url;
+ if (typeof(server.urls) === 'string') {
+ url = server.urls;
+ } else {
+ url = server.urls[0];
+ }
+ if (url.indexOf('transport=udp') !== -1) {
+ self.iceServers.push({
+ username: server.username,
+ credential: server.credential,
+ urls: url
+ });
+ }
+ }
+ });
+ }
+
+ // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
+ // everything that is needed to describe a SDP m-line.
+ this.transceivers = [];
+
+ // since the iceGatherer is currently created in createOffer but we
+ // must not emit candidates until after setLocalDescription we buffer
+ // them in this array.
+ this._localIceCandidatesBuffer = [];
+ };
+
+ window.RTCPeerConnection.prototype._emitBufferedCandidates = function() {
+ var self = this;
+ // FIXME: need to apply ice candidates in a way which is async but in-order
+ this._localIceCandidatesBuffer.forEach(function(event) {
+ if (self.onicecandidate !== null) {
+ self.onicecandidate(event);
+ }
+ });
+ this._localIceCandidatesBuffer = [];
+ };
+
+ window.RTCPeerConnection.prototype.addStream = function(stream) {
+ // Clone is necessary for local demos mostly, attaching directly
+ // to two different senders does not work (build 10547).
+ this.localStreams.push(stream.clone());
+ this._maybeFireNegotiationNeeded();
+ };
+
+ window.RTCPeerConnection.prototype.removeStream = function(stream) {
+ var idx = this.localStreams.indexOf(stream);
+ if (idx > -1) {
+ this.localStreams.splice(idx, 1);
+ this._maybeFireNegotiationNeeded();
+ }
+ };
+
+ // Determines the intersection of local and remote capabilities.
+ window.RTCPeerConnection.prototype._getCommonCapabilities =
+ function(localCapabilities, remoteCapabilities) {
+ var commonCapabilities = {
+ codecs: [],
+ headerExtensions: [],
+ fecMechanisms: []
+ };
+ localCapabilities.codecs.forEach(function(lCodec) {
+ for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
+ var rCodec = remoteCapabilities.codecs[i];
+ if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
+ lCodec.clockRate === rCodec.clockRate &&
+ lCodec.numChannels === rCodec.numChannels) {
+ // push rCodec so we reply with offerer payload type
+ commonCapabilities.codecs.push(rCodec);
+
+ // FIXME: also need to determine intersection between
+ // .rtcpFeedback and .parameters
+ break;
+ }
+ }
+ });
+
+ localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
+ for (var i = 0; i < remoteCapabilities.headerExtensions.length; i++) {
+ var rHeaderExtension = remoteCapabilities.headerExtensions[i];
+ if (lHeaderExtension.uri === rHeaderExtension.uri) {
+ commonCapabilities.headerExtensions.push(rHeaderExtension);
+ break;
+ }
+ }
+ });
+
+ // FIXME: fecMechanisms
+ return commonCapabilities;
+ };
+
+ // Create ICE gatherer, ICE transport and DTLS transport.
+ window.RTCPeerConnection.prototype._createIceAndDtlsTransports =
+ function(mid, sdpMLineIndex) {
+ var self = this;
+ var iceGatherer = new RTCIceGatherer(self.iceOptions);
+ var iceTransport = new RTCIceTransport(iceGatherer);
+ iceGatherer.onlocalcandidate = function(evt) {
+ var event = {};
+ event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
+
+ var cand = evt.candidate;
+ // Edge emits an empty object for RTCIceCandidateComplete‥
+ if (!cand || Object.keys(cand).length === 0) {
+ // polyfill since RTCIceGatherer.state is not implemented in Edge 10547 yet.
+ if (iceGatherer.state === undefined) {
+ iceGatherer.state = 'completed';
+ }
+
+ // Emit a candidate with type endOfCandidates to make the samples work.
+ // Edge requires addIceCandidate with this empty candidate to start checking.
+ // The real solution is to signal end-of-candidates to the other side when
+ // getting the null candidate but some apps (like the samples) don't do that.
+ event.candidate.candidate =
+ 'candidate:1 1 udp 1 0.0.0.0 9 typ endOfCandidates';
+ } else {
+ // RTCIceCandidate doesn't have a component, needs to be added
+ cand.component = iceTransport.component === 'RTCP' ? 2 : 1;
+ event.candidate.candidate = SDPUtils.writeCandidate(cand);
+ }
+
+ var complete = self.transceivers.every(function(transceiver) {
+ return transceiver.iceGatherer &&
+ transceiver.iceGatherer.state === 'completed';
+ });
+ // FIXME: update .localDescription with candidate and (potentially) end-of-candidates.
+ // To make this harder, the gatherer might emit candidates before localdescription
+ // is set. To make things worse, gather.getLocalCandidates still errors in
+ // Edge 10547 when no candidates have been gathered yet.
+
+ if (self.onicecandidate !== null) {
+ // Emit candidate if localDescription is set.
+ // Also emits null candidate when all gatherers are complete.
+ if (self.localDescription && self.localDescription.type === '') {
+ self._localIceCandidatesBuffer.push(event);
+ if (complete) {
+ self._localIceCandidatesBuffer.push({});
+ }
+ } else {
+ self.onicecandidate(event);
+ if (complete) {
+ self.onicecandidate({});
+ }
+ }
+ }
+ };
+ iceTransport.onicestatechange = function() {
+ self._updateConnectionState();
+ };
+
+ var dtlsTransport = new RTCDtlsTransport(iceTransport);
+ dtlsTransport.ondtlsstatechange = function() {
+ self._updateConnectionState();
+ };
+ dtlsTransport.onerror = function() {
+ // onerror does not set state to failed by itself.
+ dtlsTransport.state = 'failed';
+ self._updateConnectionState();
+ };
+
+ return {
+ iceGatherer: iceGatherer,
+ iceTransport: iceTransport,
+ dtlsTransport: dtlsTransport
+ };
+ };
+
+ // Start the RTP Sender and Receiver for a transceiver.
+ window.RTCPeerConnection.prototype._transceive = function(transceiver,
+ send, recv) {
+ var params = this._getCommonCapabilities(transceiver.localCapabilities,
+ transceiver.remoteCapabilities);
+ if (send && transceiver.rtpSender) {
+ params.encodings = [{
+ ssrc: transceiver.sendSsrc
+ }];
+ params.rtcp = {
+ cname: localCName,
+ ssrc: transceiver.recvSsrc
+ };
+ transceiver.rtpSender.send(params);
+ }
+ if (recv && transceiver.rtpReceiver) {
+ params.encodings = [{
+ ssrc: transceiver.recvSsrc
+ }];
+ params.rtcp = {
+ cname: transceiver.cname,
+ ssrc: transceiver.sendSsrc
+ };
+ transceiver.rtpReceiver.receive(params);
+ }
+ };
+
+ window.RTCPeerConnection.prototype.setLocalDescription =
+ function(description) {
+ var self = this;
+ if (description.type === 'offer') {
+ if (!this._pendingOffer) {
+ } else {
+ this.transceivers = this._pendingOffer;
+ delete this._pendingOffer;
+ }
+ } else if (description.type === 'answer') {
+ var sections = SDPUtils.splitSections(self.remoteDescription.sdp);
+ var sessionpart = sections.shift();
+ sections.forEach(function(mediaSection, sdpMLineIndex) {
+ var transceiver = self.transceivers[sdpMLineIndex];
+ var iceGatherer = transceiver.iceGatherer;
+ var iceTransport = transceiver.iceTransport;
+ var dtlsTransport = transceiver.dtlsTransport;
+ var localCapabilities = transceiver.localCapabilities;
+ var remoteCapabilities = transceiver.remoteCapabilities;
+ var rejected = mediaSection.split('\n', 1)[0]
+ .split(' ', 2)[1] === '0';
+
+ if (!rejected) {
+ var remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
+ sessionpart);
+ iceTransport.start(iceGatherer, remoteIceParameters, 'controlled');
+
+ var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
+ sessionpart);
+ dtlsTransport.start(remoteDtlsParameters);
+
+ // Calculate intersection of capabilities.
+ var params = self._getCommonCapabilities(localCapabilities,
+ remoteCapabilities);
+
+ // Start the RTCRtpSender. The RTCRtpReceiver for this transceiver
+ // has already been started in setRemoteDescription.
+ self._transceive(transceiver,
+ params.codecs.length > 0,
+ false);
+ }
+ });
+ }
+
+ this.localDescription = description;
+ switch (description.type) {
+ case 'offer':
+ this._updateSignalingState('have-local-offer');
+ break;
+ case 'answer':
+ this._updateSignalingState('stable');
+ break;
+ default:
+ throw new TypeError('unsupported type "' + description.type + '"');
+ }
+
+ // If a success callback was provided, emit ICE candidates after it has been
+ // executed. Otherwise, emit callback after the Promise is resolved.
+ var hasCallback = arguments.length > 1 &&
+ typeof arguments[1] === 'function';
+ if (hasCallback) {
+ var cb = arguments[1];
+ window.setTimeout(function() {
+ cb();
+ self._emitBufferedCandidates();
+ }, 0);
+ }
+ var p = Promise.resolve();
+ p.then(function() {
+ if (!hasCallback) {
+ window.setTimeout(self._emitBufferedCandidates.bind(self), 0);
+ }
+ });
+ return p;
+ };
+
+ window.RTCPeerConnection.prototype.setRemoteDescription =
+ function(description) {
+ var self = this;
+ var stream = new MediaStream();
+ var sections = SDPUtils.splitSections(description.sdp);
+ var sessionpart = sections.shift();
+ sections.forEach(function(mediaSection, sdpMLineIndex) {
+ var lines = SDPUtils.splitLines(mediaSection);
+ var mline = lines[0].substr(2).split(' ');
+ var kind = mline[0];
+ var rejected = mline[1] === '0';
+ var direction = SDPUtils.getDirection(mediaSection, sessionpart);
+
+ var transceiver;
+ var iceGatherer;
+ var iceTransport;
+ var dtlsTransport;
+ var rtpSender;
+ var rtpReceiver;
+ var sendSsrc;
+ var recvSsrc;
+ var localCapabilities;
+
+ // FIXME: ensure the mediaSection has rtcp-mux set.
+ var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
+ var remoteIceParameters;
+ var remoteDtlsParameters;
+ if (!rejected) {
+ remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
+ sessionpart);
+ remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
+ sessionpart);
+ }
+ var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0].substr(6);
+
+ var cname;
+ // Gets the first SSRC. Note that with RTX there might be multiple SSRCs.
+ var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
+ .map(function(line) {
+ return SDPUtils.parseSsrcMedia(line);
+ })
+ .filter(function(obj) {
+ return obj.attribute === 'cname';
+ })[0];
+ if (remoteSsrc) {
+ recvSsrc = parseInt(remoteSsrc.ssrc, 10);
+ cname = remoteSsrc.value;
+ }
+
+ if (description.type === 'offer') {
+ var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);
+
+ localCapabilities = RTCRtpReceiver.getCapabilities(kind);
+ sendSsrc = (2 * sdpMLineIndex + 2) * 1001;
+
+ rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
+
+ // FIXME: not correct when there are multiple streams but that is
+ // not currently supported in this shim.
+ stream.addTrack(rtpReceiver.track);
+
+ // FIXME: look at direction.
+ if (self.localStreams.length > 0 &&
+ self.localStreams[0].getTracks().length >= sdpMLineIndex) {
+ // FIXME: actually more complicated, needs to match types etc
+ var localtrack = self.localStreams[0].getTracks()[sdpMLineIndex];
+ rtpSender = new RTCRtpSender(localtrack, transports.dtlsTransport);
+ }
+
+ self.transceivers[sdpMLineIndex] = {
+ iceGatherer: transports.iceGatherer,
+ iceTransport: transports.iceTransport,
+ dtlsTransport: transports.dtlsTransport,
+ localCapabilities: localCapabilities,
+ remoteCapabilities: remoteCapabilities,
+ rtpSender: rtpSender,
+ rtpReceiver: rtpReceiver,
+ kind: kind,
+ mid: mid,
+ cname: cname,
+ sendSsrc: sendSsrc,
+ recvSsrc: recvSsrc
+ };
+ // Start the RTCRtpReceiver now. The RTPSender is started in setLocalDescription.
+ self._transceive(self.transceivers[sdpMLineIndex],
+ false,
+ direction === 'sendrecv' || direction === 'sendonly');
+ } else if (description.type === 'answer' && !rejected) {
+ transceiver = self.transceivers[sdpMLineIndex];
+ iceGatherer = transceiver.iceGatherer;
+ iceTransport = transceiver.iceTransport;
+ dtlsTransport = transceiver.dtlsTransport;
+ rtpSender = transceiver.rtpSender;
+ rtpReceiver = transceiver.rtpReceiver;
+ sendSsrc = transceiver.sendSsrc;
+ //recvSsrc = transceiver.recvSsrc;
+ localCapabilities = transceiver.localCapabilities;
+
+ self.transceivers[sdpMLineIndex].recvSsrc = recvSsrc;
+ self.transceivers[sdpMLineIndex].remoteCapabilities =
+ remoteCapabilities;
+ self.transceivers[sdpMLineIndex].cname = cname;
+
+ iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
+ dtlsTransport.start(remoteDtlsParameters);
+
+ self._transceive(transceiver,
+ direction === 'sendrecv' || direction === 'recvonly',
+ direction === 'sendrecv' || direction === 'sendonly');
+
+ if (rtpReceiver &&
+ (direction === 'sendrecv' || direction === 'sendonly')) {
+ stream.addTrack(rtpReceiver.track);
+ } else {
+ // FIXME: actually the receiver should be created later.
+ delete transceiver.rtpReceiver;
+ }
+ }
+ });
+
+ this.remoteDescription = description;
+ switch (description.type) {
+ case 'offer':
+ this._updateSignalingState('have-remote-offer');
+ break;
+ case 'answer':
+ this._updateSignalingState('stable');
+ break;
+ default:
+ throw new TypeError('unsupported type "' + description.type + '"');
+ }
+ window.setTimeout(function() {
+ if (self.onaddstream !== null && stream.getTracks().length) {
+ self.remoteStreams.push(stream);
+ window.setTimeout(function() {
+ self.onaddstream({stream: stream});
+ }, 0);
+ }
+ }, 0);
+ if (arguments.length > 1 && typeof arguments[1] === 'function') {
+ window.setTimeout(arguments[1], 0);
+ }
+ return Promise.resolve();
+ };
+
+ window.RTCPeerConnection.prototype.close = function() {
+ this.transceivers.forEach(function(transceiver) {
+ /* not yet
+ if (transceiver.iceGatherer) {
+ transceiver.iceGatherer.close();
+ }
+ */
+ if (transceiver.iceTransport) {
+ transceiver.iceTransport.stop();
+ }
+ if (transceiver.dtlsTransport) {
+ transceiver.dtlsTransport.stop();
+ }
+ if (transceiver.rtpSender) {
+ transceiver.rtpSender.stop();
+ }
+ if (transceiver.rtpReceiver) {
+ transceiver.rtpReceiver.stop();
+ }
+ });
+ // FIXME: clean up tracks, local streams, remote streams, etc
+ this._updateSignalingState('closed');
+ };
+
+ // Update the signaling state.
+ window.RTCPeerConnection.prototype._updateSignalingState =
+ function(newState) {
+ this.signalingState = newState;
+ if (this.onsignalingstatechange !== null) {
+ this.onsignalingstatechange();
+ }
+ };
+
+ // Determine whether to fire the negotiationneeded event.
+ window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded =
+ function() {
+ // Fire away (for now).
+ if (this.onnegotiationneeded !== null) {
+ this.onnegotiationneeded();
+ }
+ };
+
+ // Update the connection state.
+ window.RTCPeerConnection.prototype._updateConnectionState =
+ function() {
+ var self = this;
+ var newState;
+ var states = {
+ 'new': 0,
+ closed: 0,
+ connecting: 0,
+ checking: 0,
+ connected: 0,
+ completed: 0,
+ failed: 0
+ };
+ this.transceivers.forEach(function(transceiver) {
+ states[transceiver.iceTransport.state]++;
+ states[transceiver.dtlsTransport.state]++;
+ });
+ // ICETransport.completed and connected are the same for this purpose.
+ states.connected += states.completed;
+
+ newState = 'new';
+ if (states.failed > 0) {
+ newState = 'failed';
+ } else if (states.connecting > 0 || states.checking > 0) {
+ newState = 'connecting';
+ } else if (states.disconnected > 0) {
+ newState = 'disconnected';
+ } else if (states.new > 0) {
+ newState = 'new';
+ } else if (states.connecting > 0 || states.completed > 0) {
+ newState = 'connected';
+ }
+
+ if (newState !== self.iceConnectionState) {
+ self.iceConnectionState = newState;
+ if (this.oniceconnectionstatechange !== null) {
+ this.oniceconnectionstatechange();
+ }
+ }
+ };
+
+ window.RTCPeerConnection.prototype.createOffer = function() {
+ var self = this;
+ if (this._pendingOffer) {
+ throw new Error('createOffer called while there is a pending offer.');
+ }
+ var offerOptions;
+ if (arguments.length === 1 && typeof arguments[0] !== 'function') {
+ offerOptions = arguments[0];
+ } else if (arguments.length === 3) {
+ offerOptions = arguments[2];
+ }
+
+ var tracks = [];
+ var numAudioTracks = 0;
+ var numVideoTracks = 0;
+ // Default to sendrecv.
+ if (this.localStreams.length) {
+ numAudioTracks = this.localStreams[0].getAudioTracks().length;
+ numVideoTracks = this.localStreams[0].getVideoTracks().length;
+ }
+ // Determine number of audio and video tracks we need to send/recv.
+ if (offerOptions) {
+ // Reject Chrome legacy constraints.
+ if (offerOptions.mandatory || offerOptions.optional) {
+ throw new TypeError(
+ 'Legacy mandatory/optional constraints not supported.');
+ }
+ if (offerOptions.offerToReceiveAudio !== undefined) {
+ numAudioTracks = offerOptions.offerToReceiveAudio;
+ }
+ if (offerOptions.offerToReceiveVideo !== undefined) {
+ numVideoTracks = offerOptions.offerToReceiveVideo;
+ }
+ }
+ if (this.localStreams.length) {
+ // Push local streams.
+ this.localStreams[0].getTracks().forEach(function(track) {
+ tracks.push({
+ kind: track.kind,
+ track: track,
+ wantReceive: track.kind === 'audio' ?
+ numAudioTracks > 0 : numVideoTracks > 0
+ });
+ if (track.kind === 'audio') {
+ numAudioTracks--;
+ } else if (track.kind === 'video') {
+ numVideoTracks--;
+ }
+ });
+ }
+ // Create M-lines for recvonly streams.
+ while (numAudioTracks > 0 || numVideoTracks > 0) {
+ if (numAudioTracks > 0) {
+ tracks.push({
+ kind: 'audio',
+ wantReceive: true
+ });
+ numAudioTracks--;
+ }
+ if (numVideoTracks > 0) {
+ tracks.push({
+ kind: 'video',
+ wantReceive: true
+ });
+ numVideoTracks--;
+ }
+ }
+
+ var sdp = SDPUtils.writeSessionBoilerplate();
+ var transceivers = [];
+ tracks.forEach(function(mline, sdpMLineIndex) {
+ // For each track, create an ice gatherer, ice transport, dtls transport,
+ // potentially rtpsender and rtpreceiver.
+ var track = mline.track;
+ var kind = mline.kind;
+ var mid = generateIdentifier();
+
+ var transports = self._createIceAndDtlsTransports(mid, sdpMLineIndex);
+
+ var localCapabilities = RTCRtpSender.getCapabilities(kind);
+ var rtpSender;
+ var rtpReceiver;
+
+ // generate an ssrc now, to be used later in rtpSender.send
+ var sendSsrc = (2 * sdpMLineIndex + 1) * 1001;
+ if (track) {
+ rtpSender = new RTCRtpSender(track, transports.dtlsTransport);
+ }
+
+ if (mline.wantReceive) {
+ rtpReceiver = new RTCRtpReceiver(transports.dtlsTransport, kind);
+ }
+
+ transceivers[sdpMLineIndex] = {
+ iceGatherer: transports.iceGatherer,
+ iceTransport: transports.iceTransport,
+ dtlsTransport: transports.dtlsTransport,
+ localCapabilities: localCapabilities,
+ remoteCapabilities: null,
+ rtpSender: rtpSender,
+ rtpReceiver: rtpReceiver,
+ kind: kind,
+ mid: mid,
+ sendSsrc: sendSsrc,
+ recvSsrc: null
+ };
+ var transceiver = transceivers[sdpMLineIndex];
+ sdp += SDPUtils.writeMediaSection(transceiver,
+ transceiver.localCapabilities, 'offer', self.localStreams[0]);
+ });
+
+ this._pendingOffer = transceivers;
+ var desc = new RTCSessionDescription({
+ type: 'offer',
+ sdp: sdp
+ });
+ if (arguments.length && typeof arguments[0] === 'function') {
+ window.setTimeout(arguments[0], 0, desc);
+ }
+ return Promise.resolve(desc);
+ };
+
+ window.RTCPeerConnection.prototype.createAnswer = function() {
+ var self = this;
+ var answerOptions;
+ if (arguments.length === 1 && typeof arguments[0] !== 'function') {
+ answerOptions = arguments[0];
+ } else if (arguments.length === 3) {
+ answerOptions = arguments[2];
+ }
+
+ var sdp = SDPUtils.writeSessionBoilerplate();
+ this.transceivers.forEach(function(transceiver) {
+ // Calculate intersection of capabilities.
+ var commonCapabilities = self._getCommonCapabilities(
+ transceiver.localCapabilities,
+ transceiver.remoteCapabilities);
+
+ sdp += SDPUtils.writeMediaSection(transceiver, commonCapabilities,
+ 'answer', self.localStreams[0]);
+ });
+
+ var desc = new RTCSessionDescription({
+ type: 'answer',
+ sdp: sdp
+ });
+ if (arguments.length && typeof arguments[0] === 'function') {
+ window.setTimeout(arguments[0], 0, desc);
+ }
+ return Promise.resolve(desc);
+ };
+
+ window.RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
+ var mLineIndex = candidate.sdpMLineIndex;
+ if (candidate.sdpMid) {
+ for (var i = 0; i < this.transceivers.length; i++) {
+ if (this.transceivers[i].mid === candidate.sdpMid) {
+ mLineIndex = i;
+ break;
+ }
+ }
+ }
+ var transceiver = this.transceivers[mLineIndex];
+ if (transceiver) {
+ var cand = Object.keys(candidate.candidate).length > 0 ?
+ SDPUtils.parseCandidate(candidate.candidate) : {};
+ // Ignore Chrome's invalid candidates since Edge does not like them.
+ if (cand.protocol === 'tcp' && cand.port === 0) {
+ return;
+ }
+ // Ignore RTCP candidates, we assume RTCP-MUX.
+ if (cand.component !== '1') {
+ return;
+ }
+ // A dirty hack to make samples work.
+ if (cand.type === 'endOfCandidates') {
+ cand = {};
+ }
+ transceiver.iceTransport.addRemoteCandidate(cand);
+ }
+ if (arguments.length > 1 && typeof arguments[1] === 'function') {
+ window.setTimeout(arguments[1], 0);
+ }
+ return Promise.resolve();
+ };
+
+ window.RTCPeerConnection.prototype.getStats = function() {
+ var promises = [];
+ this.transceivers.forEach(function(transceiver) {
+ ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
+ 'dtlsTransport'].forEach(function(method) {
+ if (transceiver[method]) {
+ promises.push(transceiver[method].getStats());
+ }
+ });
+ });
+ var cb = arguments.length > 1 && typeof arguments[1] === 'function' &&
+ arguments[1];
+ return new Promise(function(resolve) {
+ var results = {};
+ Promise.all(promises).then(function(res) {
+ res.forEach(function(result) {
+ Object.keys(result).forEach(function(id) {
+ results[id] = result[id];
+ });
+ });
+ if (cb) {
+ window.setTimeout(cb, 0, results);
+ }
+ resolve(results);
+ });
+ });
+ };
+ }
} else {
webrtcUtils.log('Browser does not appear to be WebRTC-capable');
}
@@ -1074,11 +2256,17 @@ if ( navigator.mozGetUserMedia
/* Orginal exports removed in favor of AdapterJS custom export.
if (typeof module !== 'undefined') {
var RTCPeerConnection;
+ var RTCIceCandidate;
+ var RTCSessionDescription;
if (typeof window !== 'undefined') {
RTCPeerConnection = window.RTCPeerConnection;
+ RTCIceCandidate = window.RTCIceCandidate;
+ RTCSessionDescription = window.RTCSessionDescription;
}
module.exports = {
RTCPeerConnection: RTCPeerConnection,
+ RTCIceCandidate: RTCIceCandidate,
+ RTCSessionDescription: RTCSessionDescription,
getUserMedia: getUserMedia,
attachMediaStream: attachMediaStream,
reattachMediaStream: reattachMediaStream,
@@ -1095,6 +2283,8 @@ if ( navigator.mozGetUserMedia
define([], function() {
return {
RTCPeerConnection: window.RTCPeerConnection,
+ RTCIceCandidate: window.RTCIceCandidate,
+ RTCSessionDescription: window.RTCSessionDescription,
getUserMedia: getUserMedia,
attachMediaStream: attachMediaStream,
reattachMediaStream: reattachMediaStream,
@@ -1110,13 +2300,16 @@ if ( navigator.mozGetUserMedia
}
*/
+/* jshint ignore:end */
// END OF INJECTION OF GOOGLE'S ADAPTER.JS CONTENT
///////////////////////////////////////////////////////////////////
+ AdapterJS.parseWebrtcDetectedBrowser();
+
///////////////////////////////////////////////////////////////////
// EXTENSION FOR CHROME, FIREFOX AND EDGE
- // Includes legacy functions
+ // Includes legacy functions
// -- createIceServer
// -- createIceServers
// -- MediaStreamTrack.getSources
@@ -1142,25 +2335,26 @@ if ( navigator.mozGetUserMedia
createIceServer = function (url, username, password) {
console.warn('createIceServer is deprecated. It should be replaced with an application level implementation.');
-
+ // Note: Google's import of AJS will auto-reverse to 'url': '...' for FF < 38
+
var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
- iceServer = { url : url };
- } else if (url_parts[0].indexOf('turn') === 0) {
+ var urlParts = url.split(':');
+ if (urlParts[0].indexOf('stun') === 0) {
+ iceServer = { urls : [url] };
+ } else if (urlParts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 27) {
- var turn_url_parts = url.split('?');
- if (turn_url_parts.length === 1 ||
- turn_url_parts[1].indexOf('transport=udp') === 0) {
+ var turnUrlParts = url.split('?');
+ if (turnUrlParts.length === 1 ||
+ turnUrlParts[1].indexOf('transport=udp') === 0) {
iceServer = {
- url : turn_url_parts[0],
+ urls : [turnUrlParts[0]],
credential : password,
username : username
};
}
} else {
iceServer = {
- url : url,
+ urls : [url],
credential : password,
username : username
};
@@ -1184,12 +2378,12 @@ if ( navigator.mozGetUserMedia
} else if ( navigator.webkitGetUserMedia ) {
createIceServer = function (url, username, password) {
console.warn('createIceServer is deprecated. It should be replaced with an application level implementation.');
-
+
var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
+ var urlParts = url.split(':');
+ if (urlParts[0].indexOf('stun') === 0) {
iceServer = { 'url' : url };
- } else if (url_parts[0].indexOf('turn') === 0) {
+ } else if (urlParts[0].indexOf('turn') === 0) {
iceServer = {
'url' : url,
'credential' : password,
@@ -1225,7 +2419,7 @@ if ( navigator.mozGetUserMedia
// attachMediaStream and reattachMediaStream for Egde
if (navigator.mediaDevices && navigator.userAgent.match(
/Edge\/(\d+).(\d+)$/)) {
- window.getUserMedia = navigator.getUserMedia.bind(navigator);
+ getUserMedia = window.getUserMedia = navigator.getUserMedia.bind(navigator);
attachMediaStream = function(element, stream) {
element.srcObject = stream;
return element;
@@ -1236,11 +2430,18 @@ if ( navigator.mozGetUserMedia
};
}
- // Need to override attachMediaStream and reattachMediaStream
+ // Need to override attachMediaStream and reattachMediaStream
// to support the plugin's logic
attachMediaStream_base = attachMediaStream;
attachMediaStream = function (element, stream) {
- attachMediaStream_base(element, stream);
+ if ((webrtcDetectedBrowser === 'chrome' ||
+ webrtcDetectedBrowser === 'opera') &&
+ !stream) {
+ // Chrome does not support "src = null"
+ element.src = '';
+ } else {
+ attachMediaStream_base(element, stream);
+ }
return element;
};
reattachMediaStream_base = reattachMediaStream;
@@ -1249,6 +2450,14 @@ if ( navigator.mozGetUserMedia
return to;
};
+ // Propagate attachMediaStream and gUM in window and AdapterJS
+ window.attachMediaStream = attachMediaStream;
+ window.reattachMediaStream = reattachMediaStream;
+ window.getUserMedia = getUserMedia;
+ AdapterJS.attachMediaStream = attachMediaStream;
+ AdapterJS.reattachMediaStream = reattachMediaStream;
+ AdapterJS.getUserMedia = getUserMedia;
+
// Removed Google defined promises when promise is not defined
if (typeof Promise === 'undefined') {
requestUserMedia = null;
@@ -1305,7 +2514,6 @@ if ( navigator.mozGetUserMedia
console.groupEnd = function (arg) {};
/* jshint +W020 */
}
- webrtcDetectedType = 'plugin';
AdapterJS.parseWebrtcDetectedBrowser();
isIE = webrtcDetectedBrowser === 'IE';
@@ -1431,7 +2639,7 @@ if ( navigator.mozGetUserMedia
AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
if (AdapterJS.WebRTCPlugin.pluginState ===
AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
- console.error("AdapterJS - WebRTC interface has already been defined");
+ console.error('AdapterJS - WebRTC interface has already been defined');
return;
}
@@ -1443,13 +2651,13 @@ if ( navigator.mozGetUserMedia
createIceServer = function (url, username, password) {
var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
+ var urlParts = url.split(':');
+ if (urlParts[0].indexOf('stun') === 0) {
iceServer = {
'url' : url,
'hasCredentials' : false
};
- } else if (url_parts[0].indexOf('turn') === 0) {
+ } else if (urlParts[0].indexOf('turn') === 0) {
iceServer = {
'url' : url,
'hasCredentials' : true,
@@ -1475,27 +2683,58 @@ if ( navigator.mozGetUserMedia
};
RTCPeerConnection = function (servers, constraints) {
- var iceServers = null;
- if (servers) {
- iceServers = servers.iceServers;
- for (var i = 0; i < iceServers.length; i++) {
- if (iceServers[i].urls && !iceServers[i].url) {
- iceServers[i].url = iceServers[i].urls;
- }
- iceServers[i].hasCredentials = AdapterJS.
- isDefined(iceServers[i].username) &&
- AdapterJS.isDefined(iceServers[i].credential);
+ // Validate server argumenr
+ if (!(servers === undefined ||
+ servers === null ||
+ Array.isArray(servers.iceServers))) {
+ throw new Error('Failed to construct \'RTCPeerConnection\': Malformed RTCConfiguration');
+ }
+
+ // Validate constraints argument
+ if (typeof constraints !== 'undefined' && constraints !== null) {
+ var invalidConstraits = false;
+ invalidConstraits |= typeof constraints !== 'object';
+ invalidConstraits |= constraints.hasOwnProperty('mandatory') &&
+ constraints.mandatory !== undefined &&
+ constraints.mandatory !== null &&
+ constraints.mandatory.constructor !== Object;
+ invalidConstraits |= constraints.hasOwnProperty('optional') &&
+ constraints.optional !== undefined &&
+ constraints.optional !== null &&
+ !Array.isArray(constraints.optional);
+ if (invalidConstraits) {
+ throw new Error('Failed to construct \'RTCPeerConnection\': Malformed constraints object');
}
}
- var mandatory = (constraints && constraints.mandatory) ?
- constraints.mandatory : null;
- var optional = (constraints && constraints.optional) ?
- constraints.optional : null;
+ // Call relevant PeerConnection constructor according to plugin version
AdapterJS.WebRTCPlugin.WaitForPluginReady();
- return AdapterJS.WebRTCPlugin.plugin.
- PeerConnection(AdapterJS.WebRTCPlugin.pageId,
- iceServers, mandatory, optional);
+ if (AdapterJS.WebRTCPlugin.plugin.PEER_CONNECTION_VERSION &&
+ AdapterJS.WebRTCPlugin.plugin.PEER_CONNECTION_VERSION > 1) {
+ // RTCPeerConnection prototype from the new spec
+ return AdapterJS.WebRTCPlugin.plugin.PeerConnection(servers);
+ } else {
+ // RTCPeerConnection prototype from the old spec
+ var iceServers = null;
+ if (servers && Array.isArray(servers.iceServers)) {
+ iceServers = servers.iceServers;
+ for (var i = 0; i < iceServers.length; i++) {
+ if (iceServers[i].urls && !iceServers[i].url) {
+ iceServers[i].url = iceServers[i].urls;
+ }
+ iceServers[i].hasCredentials = AdapterJS.
+ isDefined(iceServers[i].username) &&
+ AdapterJS.isDefined(iceServers[i].credential);
+ }
+ }
+ var mandatory = (constraints && constraints.mandatory) ?
+ constraints.mandatory : null;
+ var optional = (constraints && constraints.optional) ?
+ constraints.optional : null;
+ return AdapterJS.WebRTCPlugin.plugin.
+ PeerConnection(AdapterJS.WebRTCPlugin.pageId,
+ iceServers, mandatory, optional);
+ }
};
MediaStreamTrack = {};
@@ -1505,7 +2744,7 @@ if ( navigator.mozGetUserMedia
});
};
- window.getUserMedia = function (constraints, successCallback, failureCallback) {
+ getUserMedia = function (constraints, successCallback, failureCallback) {
constraints.audio = constraints.audio || false;
constraints.video = constraints.video || false;
@@ -1514,11 +2753,16 @@ if ( navigator.mozGetUserMedia
getUserMedia(constraints, successCallback, failureCallback);
});
};
- window.navigator.getUserMedia = window.getUserMedia;
+ window.navigator.getUserMedia = getUserMedia;
// Defined mediaDevices when promises are available
- if ( !navigator.mediaDevices
- && typeof Promise !== 'undefined') {
+ if ( !navigator.mediaDevices &&
+ typeof Promise !== 'undefined') {
+ requestUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ getUserMedia(constraints, resolve, reject);
+ });
+ };
navigator.mediaDevices = {getUserMedia: requestUserMedia,
enumerateDevices: function() {
return new Promise(function(resolve) {
@@ -1527,6 +2771,7 @@ if ( navigator.mozGetUserMedia
resolve(devices.map(function(device) {
return {label: device.label,
kind: kinds[device.kind],
+ id: device.id,
deviceId: device.id,
groupId: ''};
}));
@@ -1636,31 +2881,32 @@ if ( navigator.mozGetUserMedia
}
};
- AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
+ // Propagate attachMediaStream and gUM in window and AdapterJS
+ window.attachMediaStream = attachMediaStream;
+ window.reattachMediaStream = reattachMediaStream;
+ window.getUserMedia = getUserMedia;
+ AdapterJS.attachMediaStream = attachMediaStream;
+ AdapterJS.reattachMediaStream = reattachMediaStream;
+ AdapterJS.getUserMedia = getUserMedia;
+ AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
properties = Object.getOwnPropertyNames( prototype );
-
- for(prop in properties) {
- propName = properties[prop];
-
- if (typeof(propName.slice) === 'function') {
- if (propName.slice(0,2) == 'on' && srcElem[propName] != null) {
- if (isIE) {
- destElem.attachEvent(propName,srcElem[propName]);
- } else {
- destElem.addEventListener(propName.slice(2), srcElem[propName], false)
- }
- } else {
- //TODO (http://jira.temasys.com.sg/browse/TWP-328) Forward non-event properties ?
+ for(var prop in properties) {
+ if (prop) {
+ propName = properties[prop];
+
+ if (typeof propName.slice === 'function' &&
+ propName.slice(0,2) === 'on' &&
+ typeof srcElem[propName] === 'function') {
+ AdapterJS.addEvent(destElem, propName.slice(2), srcElem[propName]);
}
}
}
-
- var subPrototype = Object.getPrototypeOf(prototype)
- if(subPrototype != null) {
+ var subPrototype = Object.getPrototypeOf(prototype);
+ if(!!subPrototype) {
AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype);
}
- }
+ };
RTCIceCandidate = function (candidate) {
if (!candidate.sdpMid) {
@@ -1738,10 +2984,14 @@ if ( navigator.mozGetUserMedia
};
var clone = function(obj) {
- if (null == obj || "object" != typeof obj) return obj;
+ if (null === obj || 'object' !== typeof obj) {
+ return obj;
+ }
var copy = obj.constructor();
for (var attr in obj) {
- if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
+ if (obj.hasOwnProperty(attr)) {
+ copy[attr] = obj[attr];
+ }
}
return copy;
};
@@ -1772,11 +3022,10 @@ if ( navigator.mozGetUserMedia
clearInterval(checkIfReady);
baseGetUserMedia(updatedConstraints, successCb, function (error) {
- if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') {
+ if (['PermissionDeniedError', 'SecurityError'].indexOf(error.name) > -1 && window.parent.location.protocol === 'https:') {
AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,
AdapterJS.TEXT.EXTENSION.BUTTON_FF,
- 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true);
- //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname;
+ 'https://addons.mozilla.org/en-US/firefox/addon/skylink-webrtc-tools/', true, true);
} else {
failureCb(error);
}
@@ -1789,7 +3038,12 @@ if ( navigator.mozGetUserMedia
}
};
- getUserMedia = navigator.getUserMedia;
+ AdapterJS.getUserMedia = window.getUserMedia = navigator.getUserMedia;
+ navigator.mediaDevices.getUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ window.getUserMedia(constraints, resolve, reject);
+ });
+ };
} else if (window.navigator.webkitGetUserMedia) {
baseGetUserMedia = window.navigator.getUserMedia;
@@ -1869,7 +3123,12 @@ if ( navigator.mozGetUserMedia
}
};
- getUserMedia = navigator.getUserMedia;
+ AdapterJS.getUserMedia = window.getUserMedia = navigator.getUserMedia;
+ navigator.mediaDevices.getUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ window.getUserMedia(constraints, resolve, reject);
+ });
+ };
} else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
// nothing here because edge does not support screensharing
@@ -1906,7 +3165,9 @@ if ( navigator.mozGetUserMedia
}
};
- getUserMedia = window.navigator.getUserMedia;
+ AdapterJS.getUserMedia = getUserMedia =
+ window.getUserMedia = navigator.getUserMedia;
+ navigator.mediaDevices.getUserMedia = requestUserMedia;
}
// For chrome, use an iframe to load the screensharing extension
@@ -1924,7 +3185,7 @@ if ( navigator.mozGetUserMedia
(document.body || document.documentElement).appendChild(iframe);
- var postFrameMessage = function (object) {
+ var postFrameMessage = function (object) { // jshint ignore:line
object = object || {};
if (!iframe.isLoaded) {
diff --git a/publish/adapter.screenshare.min.js b/publish/adapter.screenshare.min.js
index 3b90802..26ace6a 100644
--- a/publish/adapter.screenshare.min.js
+++ b/publish/adapter.screenshare.min.js
@@ -1,3 +1,3 @@
-/*! adapterjs - v0.13.0 - 2016-01-08 */
-function trace(text){if("\n"===text[text.length-1]&&(text=text.substring(0,text.length-1)),window.performance){var now=(window.performance.now()/1e3).toFixed(3);webrtcUtils.log(now+": "+text)}else webrtcUtils.log(text)}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}var AdapterJS=AdapterJS||{};if("undefined"!=typeof exports&&(module.exports=AdapterJS),AdapterJS.options=AdapterJS.options||{},AdapterJS.VERSION="0.13.0",AdapterJS.onwebrtcready=AdapterJS.onwebrtcready||function(isUsingPlugin){},AdapterJS._onwebrtcreadies=[],AdapterJS.webRTCReady=function(callback){if("function"!=typeof callback)throw new Error("Callback provided is not a function");!0===AdapterJS.onwebrtcreadyDone?callback(null!==AdapterJS.WebRTCPlugin.plugin):AdapterJS._onwebrtcreadies.push(callback)},AdapterJS.WebRTCPlugin=AdapterJS.WebRTCPlugin||{},AdapterJS.WebRTCPlugin.pluginInfo={prefix:"Tem",plugName:"TemWebRTCPlugin",pluginId:"plugin0",type:"application/x-temwebrtcplugin",onload:"__TemWebRTCReady0",portalLink:"http://skylink.io/plugin/",downloadLink:null,companyName:"Temasys"},navigator.platform.match(/^Mac/i)?AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1n77hco":navigator.platform.match(/^Win/i)&&(AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1kkS4FN"),AdapterJS.WebRTCPlugin.TAGS={NONE:"none",AUDIO:"audio",VIDEO:"video"},AdapterJS.WebRTCPlugin.pageId=Math.random().toString(36).slice(2),AdapterJS.WebRTCPlugin.plugin=null,AdapterJS.WebRTCPlugin.setLogLevel=null,AdapterJS.WebRTCPlugin.defineWebRTCInterface=null,AdapterJS.WebRTCPlugin.isPluginInstalled=null,AdapterJS.WebRTCPlugin.pluginInjectionInterval=null,AdapterJS.WebRTCPlugin.injectPlugin=null,AdapterJS.WebRTCPlugin.PLUGIN_STATES={NONE:0,INITIALIZING:1,INJECTING:2,INJECTED:3,READY:4},AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE,AdapterJS.onwebrtcreadyDone=!1,AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS={NONE:"NONE",ERROR:"ERROR",WARNING:"WARNING",INFO:"INFO",VERBOSE:"VERBOSE",SENSITIVE:"SENSITIVE"},AdapterJS.WebRTCPlugin.WaitForPluginReady=null,AdapterJS.WebRTCPlugin.callWhenPluginReady=null,__TemWebRTCReady0=function(){webrtcDetectedVersion=AdapterJS.WebRTCPlugin.plugin.version,"complete"===document.readyState?(AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady()):AdapterJS.WebRTCPlugin.documentReadyInterval=setInterval(function(){"complete"===document.readyState&&(clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval),AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady())},100)},AdapterJS.maybeThroughWebRTCReady=function(){AdapterJS.onwebrtcreadyDone||(AdapterJS.onwebrtcreadyDone=!0,AdapterJS._onwebrtcreadies.length?AdapterJS._onwebrtcreadies.forEach(function(callback){"function"==typeof callback&&callback(null!==AdapterJS.WebRTCPlugin.plugin)}):"function"==typeof AdapterJS.onwebrtcready&&AdapterJS.onwebrtcready(null!==AdapterJS.WebRTCPlugin.plugin))},AdapterJS.TEXT={PLUGIN:{REQUIRE_INSTALLATION:"This website requires you to install a WebRTC-enabling plugin to work on this browser.",NOT_SUPPORTED:"Your browser does not support WebRTC.",BUTTON:"Install Now"},REFRESH:{REQUIRE_REFRESH:"Please refresh page",BUTTON:"Refresh Page"}},AdapterJS._iceConnectionStates={starting:"starting",checking:"checking",connected:"connected",completed:"connected",done:"completed",disconnected:"disconnected",failed:"failed",closed:"closed"},AdapterJS._iceConnectionFiredStates=[],AdapterJS.isDefined=null,AdapterJS.parseWebrtcDetectedBrowser=function(){var hasMatch,checkMatch=navigator.userAgent.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i)||[];if(/trident/i.test(checkMatch[1])?(hasMatch=/\brv[ :]+(\d+)/g.exec(navigator.userAgent)||[],webrtcDetectedBrowser="IE",webrtcDetectedVersion=parseInt(hasMatch[1]||"0",10)):"Chrome"===checkMatch[1]&&(hasMatch=navigator.userAgent.match(/\bOPR\/(\d+)/),null!==hasMatch&&(webrtcDetectedBrowser="opera",webrtcDetectedVersion=parseInt(hasMatch[1],10))),navigator.userAgent.indexOf("Safari")&&("undefined"!=typeof InstallTrigger?webrtcDetectedBrowser="firefox":document.documentMode?webrtcDetectedBrowser="IE":Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0?webrtcDetectedBrowser="safari":window.opera||navigator.userAgent.indexOf(" OPR/")>=0?webrtcDetectedBrowser="opera":window.chrome&&(webrtcDetectedBrowser="chrome")),webrtcDetectedBrowser||(webrtcDetectedVersion=checkMatch[1]),!webrtcDetectedVersion)try{checkMatch=checkMatch[2]?[checkMatch[1],checkMatch[2]]:[navigator.appName,navigator.appVersion,"-?"],null!==(hasMatch=navigator.userAgent.match(/version\/(\d+)/i))&&checkMatch.splice(1,1,hasMatch[1]),webrtcDetectedVersion=parseInt(checkMatch[1],10)}catch(error){}},AdapterJS.maybeFixConfiguration=function(pcConfig){if(null!==pcConfig)for(var i=0;i'+text+""),buttonText&&buttonLink?(c.document.write(''),c.document.close(),AdapterJS.addEvent(c.document.getElementById("okay"),"click",function(e){displayRefreshBar&&AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION?AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH:AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH,AdapterJS.TEXT.REFRESH.BUTTON,"javascript:location.reload()"),window.open(buttonLink,openNewTab?"_blank":"_top"),e.preventDefault();try{event.cancelBubble=!0}catch(error){}var pluginInstallInterval=setInterval(function(){isIE||navigator.plugins.refresh(!1),AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,function(){clearInterval(pluginInstallInterval),AdapterJS.WebRTCPlugin.defineWebRTCInterface()},function(){})},500)}),AdapterJS.addEvent(c.document.getElementById("cancel"),"click",function(e){w.document.body.removeChild(i)})):c.document.close(),setTimeout(function(){"string"==typeof i.style.webkitTransform?i.style.webkitTransform="translateY(40px)":"string"==typeof i.style.transform?i.style.transform="translateY(40px)":i.style.top="0px"},300)}},webrtcDetectedType=null,checkMediaDataChannelSettings=function(peerBrowserAgent,peerBrowserVersion,callback,constraints){if("function"==typeof callback){var beOfferer=!0,isLocalFirefox="firefox"===webrtcDetectedBrowser,isLocalFirefoxInterop="moz"===webrtcDetectedType&&webrtcDetectedVersion>30,isPeerFirefox="firefox"===peerBrowserAgent;if(isLocalFirefox&&isPeerFirefox||isLocalFirefoxInterop)try{delete constraints.mandatory.MozDontOfferDataChannel}catch(error){}else isLocalFirefox&&!isPeerFirefox&&(constraints.mandatory.MozDontOfferDataChannel=!0);if(!isLocalFirefox)for(var prop in constraints.mandatory)constraints.mandatory.hasOwnProperty(prop)&&-1!==prop.indexOf("Moz")&&delete constraints.mandatory[prop];!isLocalFirefox||isPeerFirefox||isLocalFirefoxInterop||(beOfferer=!1),callback(beOfferer,constraints)}},checkIceConnectionState=function(peerId,iceConnectionState,callback){"function"==typeof callback&&(peerId=peerId?peerId:"peer",AdapterJS._iceConnectionFiredStates[peerId]&&iceConnectionState!==AdapterJS._iceConnectionStates.disconnected&&iceConnectionState!==AdapterJS._iceConnectionStates.failed&&iceConnectionState!==AdapterJS._iceConnectionStates.closed||(AdapterJS._iceConnectionFiredStates[peerId]=[]),iceConnectionState=AdapterJS._iceConnectionStates[iceConnectionState],AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState)<0&&(AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState),iceConnectionState===AdapterJS._iceConnectionStates.connected&&setTimeout(function(){AdapterJS._iceConnectionFiredStates[peerId].push(AdapterJS._iceConnectionStates.done),callback(AdapterJS._iceConnectionStates.done)},1e3),callback(iceConnectionState)))},createIceServer=null,createIceServers=null,RTCPeerConnection=null,RTCSessionDescription="function"==typeof RTCSessionDescription?RTCSessionDescription:null,RTCIceCandidate="function"==typeof RTCIceCandidate?RTCIceCandidate:null,getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,navigator.mozGetUserMedia||navigator.webkitGetUserMedia||navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)){var getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils={log:function(){"undefined"!=typeof module||"function"==typeof require&&"function"==typeof define},extractVersion:function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos])}};if("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=URL.createObjectURL(stream))}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),attachMediaStream=function(element,stream){element.srcObject=stream},reattachMediaStream=function(to,from){to.srcObject=from.srcObject},"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia&&window.mozRTCPeerConnection){if(webrtcUtils.log("This appears to be Firefox"),webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,window.RTCPeerConnection=function(pcConfig,pcConstraints){if(38>webrtcDetectedVersion&&pcConfig&&pcConfig.iceServers){for(var newIceServers=[],i=0;iwebrtcDetectedVersion&&(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){webrtcUtils.log("This appears to be Chrome"),webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38,window.RTCPeerConnection=function(pcConfig,pcConstraints){pcConfig&&pcConfig.iceTransportPolicy&&(pcConfig.iceTransports=pcConfig.iceTransportPolicy);var pc=new webkitRTCPeerConnection(pcConfig,pcConstraints),origGetStats=pc.getStats.bind(pc);return pc.getStats=function(selector,successCallback,errorCallback){var self=this,args=arguments;if(arguments.length>0&&"function"==typeof selector)return origGetStats(selector,successCallback);var fixChromeStats=function(response){var standardReport={},reports=response.result();return reports.forEach(function(report){var standardStats={id:report.id,timestamp:report.timestamp,type:report.type};report.names().forEach(function(name){standardStats[name]=report.stat(name)}),standardReport[standardStats.id]=standardStats}),standardReport};if(arguments.length>=2){var successCallbackWrapper=function(response){args[1](fixChromeStats(response))};return origGetStats.apply(this,[successCallbackWrapper,arguments[0]])}return new Promise(function(resolve,reject){1===args.length&&null===selector?origGetStats.apply(self,[function(response){resolve.apply(null,[fixChromeStats(response)])},reject]):origGetStats.apply(self,[resolve,reject])})},pc},["createOffer","createAnswer"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var self=this;if(arguments.length<1||1===arguments.length&&"object"==typeof arguments[0]){var opts=1===arguments.length?arguments[0]:void 0;return new Promise(function(resolve,reject){nativeMethod.apply(self,[resolve,reject,opts])})}return nativeMethod.apply(this,arguments)}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var args=arguments,self=this;return new Promise(function(resolve,reject){nativeMethod.apply(self,[args[0],function(){resolve(),args.length>=2&&args[1].apply(null,[])},function(err){reject(err),args.length>=3&&args[2].apply(null,[err])}])})}});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,enumerateDevices:function(){return new Promise(function(resolve){var kinds={audio:"audioinput",video:"videoinput"};return MediaStreamTrack.getSources(function(devices){resolve(devices.map(function(device){return{label:device.label,kind:kinds[device.kind],deviceId:device.id,groupId:""}}))})})}}),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.")}),attachMediaStream=function(element,stream){webrtcDetectedVersion>=43?element.srcObject=stream:"undefined"!=typeof element.src?element.src=URL.createObjectURL(stream):webrtcUtils.log("Error attaching stream to element.")},reattachMediaStream=function(to,from){webrtcDetectedVersion>=43?to.srcObject=from.srcObject:to.src=from.src}}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 webrtcUtils.log("This does not appear to be a browser"),webrtcDetectedBrowser="not a browser";var webrtcTesting={};try{Object.defineProperty(webrtcTesting,"version",{set:function(version){webrtcDetectedVersion=version}})}catch(e){}navigator.mozGetUserMedia?(MediaStreamTrack.getSources=function(successCb){setTimeout(function(){var infos=[{kind:"audio",id:"default",label:"",facing:""},{kind:"video",id:"default",label:"",facing:""}];successCb(infos)},0)},createIceServer=function(url,username,password){var iceServer=null,url_parts=url.split(":");if(0===url_parts[0].indexOf("stun"))iceServer={url:url};else if(0===url_parts[0].indexOf("turn"))if(27>webrtcDetectedVersion){var turn_url_parts=url.split("?");(1===turn_url_parts.length||0===turn_url_parts[1].indexOf("transport=udp"))&&(iceServer={url:turn_url_parts[0],credential:password,username:username})}else iceServer={url:url,credential:password,username:username};return iceServer},createIceServers=function(urls,username,password){var iceServers=[];for(i=0;i=34)iceServers={urls:urls,credential:password,username:username};else for(i=0;i=webrtcDetectedVersion){var frag=document.createDocumentFragment();for(AdapterJS.WebRTCPlugin.plugin=document.createElement("div"),AdapterJS.WebRTCPlugin.plugin.innerHTML='";AdapterJS.WebRTCPlugin.plugin.firstChild;)frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);document.body.appendChild(frag),AdapterJS.WebRTCPlugin.plugin=document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId)}else AdapterJS.WebRTCPlugin.plugin=document.createElement("object"),AdapterJS.WebRTCPlugin.plugin.id=AdapterJS.WebRTCPlugin.pluginInfo.pluginId,isIE?(AdapterJS.WebRTCPlugin.plugin.width="1px",AdapterJS.WebRTCPlugin.plugin.height="1px"):(AdapterJS.WebRTCPlugin.plugin.width="0px",AdapterJS.WebRTCPlugin.plugin.height="0px"),AdapterJS.WebRTCPlugin.plugin.type=AdapterJS.WebRTCPlugin.pluginInfo.type,AdapterJS.WebRTCPlugin.plugin.innerHTML=' '+(AdapterJS.options.getAllCams?'':"")+'',document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED}},AdapterJS.WebRTCPlugin.isPluginInstalled=function(comName,plugName,installedCb,notInstalledCb){if(isIE){try{new ActiveXObject(comName+"."+plugName)}catch(e){return void notInstalledCb()}installedCb()}else{for(var pluginArray=navigator.plugins,i=0;i=0)return void installedCb();notInstalledCb()}},AdapterJS.WebRTCPlugin.defineWebRTCInterface=function(){AdapterJS.WebRTCPlugin.pluginState!==AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY&&(AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING,AdapterJS.isDefined=function(variable){return null!==variable&&void 0!==variable},createIceServer=function(url,username,password){var iceServer=null,url_parts=url.split(":");return 0===url_parts[0].indexOf("stun")?iceServer={url:url,hasCredentials:!1}:0===url_parts[0].indexOf("turn")&&(iceServer={url:url,hasCredentials:!0,credential:password,username:username}),iceServer},createIceServers=function(urls,username,password){for(var iceServers=[],i=0;i ';temp.firstChild;)frag.appendChild(temp.firstChild);var height="",width="";element.clientWidth||element.clientHeight?(width=element.clientWidth,height=element.clientHeight):(element.width||element.height)&&(width=element.width,height=element.height),element.parentNode.insertBefore(frag,element),frag=document.getElementById(elementId),frag.width=width,frag.height=height,element.parentNode.removeChild(element)}else{for(var children=element.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){children[i].value=streamId;break}element.setStreamId(streamId)}var newElement=document.getElementById(elementId);return AdapterJS.forwardEventHandlers(newElement,element,Object.getPrototypeOf(element)),newElement}},reattachMediaStream=function(to,from){for(var stream=null,children=from.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){AdapterJS.WebRTCPlugin.WaitForPluginReady(),stream=AdapterJS.WebRTCPlugin.plugin.getStreamWithId(AdapterJS.WebRTCPlugin.pageId,children[i].value);break}return null!==stream?attachMediaStream(to,stream):void 0},AdapterJS.forwardEventHandlers=function(destElem,srcElem,prototype){properties=Object.getOwnPropertyNames(prototype);for(prop in properties)propName=properties[prop],"function"==typeof propName.slice&&"on"==propName.slice(0,2)&&null!=srcElem[propName]&&(isIE?destElem.attachEvent(propName,srcElem[propName]):destElem.addEventListener(propName.slice(2),srcElem[propName],!1));var subPrototype=Object.getPrototypeOf(prototype);null!=subPrototype&&AdapterJS.forwardEventHandlers(destElem,srcElem,subPrototype)},RTCIceCandidate=function(candidate){return candidate.sdpMid||(candidate.sdpMid=""),AdapterJS.WebRTCPlugin.WaitForPluginReady(),AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(candidate.sdpMid,candidate.sdpMLineIndex,candidate.candidate)},AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.injectPlugin),AdapterJS.WebRTCPlugin.injectPlugin())},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb=AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb||function(){AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv),AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv()},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv=function(){if(!AdapterJS.options.hidePluginInstallPrompt){var downloadLink=AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;if(downloadLink){var popupString;popupString=AdapterJS.WebRTCPlugin.pluginInfo.portalLink?'This website requires you to install the '+AdapterJS.WebRTCPlugin.pluginInfo.companyName+" WebRTC Plugin to work on this browser.":AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION,AdapterJS.renderNotificationBar(popupString,AdapterJS.TEXT.PLUGIN.BUTTON,downloadLink)}else AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED)}},AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,AdapterJS.WebRTCPlugin.defineWebRTCInterface,AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
-!function(){"use strict";var baseGetUserMedia=null;AdapterJS.TEXT.EXTENSION={REQUIRE_INSTALLATION_FF:"To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.",REQUIRE_INSTALLATION_CHROME:"To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.",REQUIRE_REFRESH:"Please refresh this page after the Skylink WebRTC tools extension has been installed.",BUTTON_FF:"Install Now",BUTTON_CHROME:"Go to Chrome Web Store"};var clone=function(obj){if(null==obj||"object"!=typeof obj)return obj;var copy=obj.constructor();for(var attr in obj)obj.hasOwnProperty(attr)&&(copy[attr]=obj[attr]);return copy};if(window.navigator.mozGetUserMedia?(baseGetUserMedia=window.navigator.getUserMedia,navigator.getUserMedia=function(constraints,successCb,failureCb){if(constraints&&constraints.video&&constraints.video.mediaSource){if("screen"!==constraints.video.mediaSource&&"window"!==constraints.video.mediaSource)return void failureCb(new Error('GetUserMedia: Only "screen" and "window" are supported as mediaSource constraints'));var updatedConstraints=clone(constraints);updatedConstraints.video.mozMediaSource=updatedConstraints.video.mediaSource;var checkIfReady=setInterval(function(){"complete"===document.readyState&&(clearInterval(checkIfReady),baseGetUserMedia(updatedConstraints,successCb,function(error){"PermissionDeniedError"===error.name&&"https:"===window.parent.location.protocol?AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,AdapterJS.TEXT.EXTENSION.BUTTON_FF,"http://skylink.io/screensharing/ff_addon.php?domain="+window.location.hostname,!1,!0):failureCb(error)}))},1)}else baseGetUserMedia(constraints,successCb,failureCb)},getUserMedia=navigator.getUserMedia):window.navigator.webkitGetUserMedia?(baseGetUserMedia=window.navigator.getUserMedia,navigator.getUserMedia=function(constraints,successCb,failureCb){if(constraints&&constraints.video&&constraints.video.mediaSource){if("chrome"!==window.webrtcDetectedBrowser)return void failureCb(new Error("Current browser does not support screensharing"));var updatedConstraints=clone(constraints),chromeCallback=function(error,sourceId){error?failureCb("permission-denied"===error?new Error("Permission denied for screen retrieval"):new Error("Failed retrieving selected screen")):(updatedConstraints.video.mandatory=updatedConstraints.video.mandatory||{},updatedConstraints.video.mandatory.chromeMediaSource="desktop",updatedConstraints.video.mandatory.maxWidth=window.screen.width>1920?window.screen.width:1920,updatedConstraints.video.mandatory.maxHeight=window.screen.height>1080?window.screen.height:1080,sourceId&&(updatedConstraints.video.mandatory.chromeMediaSourceId=sourceId),delete updatedConstraints.video.mediaSource,baseGetUserMedia(updatedConstraints,successCb,failureCb))},onIFrameCallback=function(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?chromeCallback("permission-denied"):chromeCallback(null,event.data.chromeMediaSourceId)),event.data.chromeExtensionStatus&&("not-installed"===event.data.chromeExtensionStatus?AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,AdapterJS.TEXT.EXTENSION.BUTTON_CHROME,event.data.data,!0,!0):chromeCallback(event.data.chromeExtensionStatus,null)),window.removeEventListener("message",onIFrameCallback))};window.addEventListener("message",onIFrameCallback),postFrameMessage({captureSourceId:!0})}else baseGetUserMedia(constraints,successCb,failureCb)},getUserMedia=navigator.getUserMedia):navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)||(baseGetUserMedia=window.navigator.getUserMedia,navigator.getUserMedia=function(constraints,successCb,failureCb){if(constraints&&constraints.video&&constraints.video.mediaSource){var updatedConstraints=clone(constraints);AdapterJS.WebRTCPlugin.callWhenPluginReady(function(){return AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature&&AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable?(updatedConstraints.video.optional=updatedConstraints.video.optional||[],updatedConstraints.video.optional.push({sourceId:AdapterJS.WebRTCPlugin.plugin.screensharingKey||"Screensharing"}),delete updatedConstraints.video.mediaSource,void baseGetUserMedia(updatedConstraints,successCb,failureCb)):void failureCb(new Error("Your version of the WebRTC plugin does not support screensharing"))})}else baseGetUserMedia(constraints,successCb,failureCb)},getUserMedia=window.navigator.getUserMedia),"chrome"===window.webrtcDetectedBrowser){var iframe=document.createElement("iframe");iframe.onload=function(){iframe.isLoaded=!0},iframe.src="https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html",iframe.style.display="none",(document.body||document.documentElement).appendChild(iframe);var postFrameMessage=function(object){return object=object||{},iframe.isLoaded?void iframe.contentWindow.postMessage(object,"*"):void setTimeout(function(){iframe.contentWindow.postMessage(object,"*")},100)}}else"opera"===window.webrtcDetectedBrowser}();
+/*! adapterjs - v0.13.1 - 2016-03-15 */
+function trace(text){if("\n"===text[text.length-1]&&(text=text.substring(0,text.length-1)),window.performance){var now=(window.performance.now()/1e3).toFixed(3);webrtcUtils.log(now+": "+text)}else webrtcUtils.log(text)}function requestUserMedia(constraints){return new Promise(function(resolve,reject){getUserMedia(constraints,resolve,reject)})}var AdapterJS=AdapterJS||{};if("undefined"!=typeof exports&&(module.exports=AdapterJS),AdapterJS.options=AdapterJS.options||{},AdapterJS.VERSION="0.13.1",AdapterJS.onwebrtcready=AdapterJS.onwebrtcready||function(isUsingPlugin){},AdapterJS._onwebrtcreadies=[],AdapterJS.webRTCReady=function(callback){if("function"!=typeof callback)throw new Error("Callback provided is not a function");!0===AdapterJS.onwebrtcreadyDone?callback(null!==AdapterJS.WebRTCPlugin.plugin):AdapterJS._onwebrtcreadies.push(callback)},AdapterJS.WebRTCPlugin=AdapterJS.WebRTCPlugin||{},AdapterJS.WebRTCPlugin.pluginInfo={prefix:"Tem",plugName:"TemWebRTCPlugin",pluginId:"plugin0",type:"application/x-temwebrtcplugin",onload:"__TemWebRTCReady0",portalLink:"http://skylink.io/plugin/",downloadLink:null,companyName:"Temasys"},navigator.platform.match(/^Mac/i)?AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1n77hco":navigator.platform.match(/^Win/i)&&(AdapterJS.WebRTCPlugin.pluginInfo.downloadLink="http://bit.ly/1kkS4FN"),AdapterJS.WebRTCPlugin.TAGS={NONE:"none",AUDIO:"audio",VIDEO:"video"},AdapterJS.WebRTCPlugin.pageId=Math.random().toString(36).slice(2),AdapterJS.WebRTCPlugin.plugin=null,AdapterJS.WebRTCPlugin.setLogLevel=null,AdapterJS.WebRTCPlugin.defineWebRTCInterface=null,AdapterJS.WebRTCPlugin.isPluginInstalled=null,AdapterJS.WebRTCPlugin.pluginInjectionInterval=null,AdapterJS.WebRTCPlugin.injectPlugin=null,AdapterJS.WebRTCPlugin.PLUGIN_STATES={NONE:0,INITIALIZING:1,INJECTING:2,INJECTED:3,READY:4},AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE,AdapterJS.onwebrtcreadyDone=!1,AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS={NONE:"NONE",ERROR:"ERROR",WARNING:"WARNING",INFO:"INFO",VERBOSE:"VERBOSE",SENSITIVE:"SENSITIVE"},AdapterJS.WebRTCPlugin.WaitForPluginReady=null,AdapterJS.WebRTCPlugin.callWhenPluginReady=null,__TemWebRTCReady0=function(){if("complete"===document.readyState)AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady();else var timer=setInterval(function(){"complete"===document.readyState&&(clearInterval(timer),AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,AdapterJS.maybeThroughWebRTCReady())},100)},AdapterJS.maybeThroughWebRTCReady=function(){AdapterJS.onwebrtcreadyDone||(AdapterJS.onwebrtcreadyDone=!0,AdapterJS._onwebrtcreadies.length?AdapterJS._onwebrtcreadies.forEach(function(callback){"function"==typeof callback&&callback(null!==AdapterJS.WebRTCPlugin.plugin)}):"function"==typeof AdapterJS.onwebrtcready&&AdapterJS.onwebrtcready(null!==AdapterJS.WebRTCPlugin.plugin))},AdapterJS.TEXT={PLUGIN:{REQUIRE_INSTALLATION:"This website requires you to install a WebRTC-enabling plugin to work on this browser.",NOT_SUPPORTED:"Your browser does not support WebRTC.",BUTTON:"Install Now"},REFRESH:{REQUIRE_REFRESH:"Please refresh page",BUTTON:"Refresh Page"}},AdapterJS._iceConnectionStates={starting:"starting",checking:"checking",connected:"connected",completed:"connected",done:"completed",disconnected:"disconnected",failed:"failed",closed:"closed"},AdapterJS._iceConnectionFiredStates=[],AdapterJS.isDefined=null,AdapterJS.parseWebrtcDetectedBrowser=function(){var hasMatch=null;window.opr&&opr.addons||window.opera||navigator.userAgent.indexOf(" OPR/")>=0?(webrtcDetectedBrowser="opera",webrtcDetectedType="webkit",webrtcMinimumVersion=26,hasMatch=/OPR\/(\d+)/i.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1],10)):"undefined"!=typeof InstallTrigger?webrtcDetectedType="moz":Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor")>0?(webrtcDetectedBrowser="safari",webrtcDetectedType="plugin",webrtcMinimumVersion=7,hasMatch=/version\/(\d+)/i.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1],10)):document.documentMode?(webrtcDetectedBrowser="IE",webrtcDetectedType="plugin",webrtcMinimumVersion=9,hasMatch=/\brv[ :]+(\d+)/g.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1]||"0",10),webrtcDetectedVersion||(hasMatch=/\bMSIE[ :]+(\d+)/g.exec(navigator.userAgent)||[],webrtcDetectedVersion=parseInt(hasMatch[1]||"0",10))):window.StyleMedia?webrtcDetectedType="":window.chrome&&window.chrome.webstore?webrtcDetectedType="webkit":"chrome"!==webrtcDetectedBrowser&&"opera"!==webrtcDetectedBrowser||!window.CSS||(webrtcDetectedBrowser="blink"),window.webrtcDetectedBrowser=webrtcDetectedBrowser,window.webrtcDetectedVersion=webrtcDetectedVersion,window.webrtcMinimumVersion=webrtcMinimumVersion},AdapterJS.addEvent=function(elem,evnt,func){elem.addEventListener?elem.addEventListener(evnt,func,!1):elem.attachEvent?elem.attachEvent("on"+evnt,func):elem[evnt]=func},AdapterJS.renderNotificationBar=function(text,buttonText,buttonLink,openNewTab,displayRefreshBar){if("complete"===document.readyState){var w=window,i=document.createElement("iframe");i.name="adapterjs-alert",i.style.position="fixed",i.style.top="-41px",i.style.left=0,i.style.right=0,i.style.width="100%",i.style.height="40px",i.style.backgroundColor="#ffffe1",i.style.border="none",i.style.borderBottom="1px solid #888888",i.style.zIndex="9999999","string"==typeof i.style.webkitTransition?i.style.webkitTransition="all .5s ease-out":"string"==typeof i.style.transition&&(i.style.transition="all .5s ease-out"),document.body.appendChild(i);var c=i.contentWindow?i.contentWindow:i.contentDocument.document?i.contentDocument.document:i.contentDocument;c.document.open(),c.document.write(''+text+""),buttonText&&buttonLink?(c.document.write(''),c.document.close(),AdapterJS.addEvent(c.document.getElementById("okay"),"click",function(e){displayRefreshBar&&AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION?AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH:AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH,AdapterJS.TEXT.REFRESH.BUTTON,"javascript:location.reload()"),window.open(buttonLink,openNewTab?"_blank":"_top"),e.preventDefault();try{e.cancelBubble=!0}catch(error){}var pluginInstallInterval=setInterval(function(){isIE||navigator.plugins.refresh(!1),AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,function(){clearInterval(pluginInstallInterval),AdapterJS.WebRTCPlugin.defineWebRTCInterface()},function(){})},500)}),AdapterJS.addEvent(c.document.getElementById("cancel"),"click",function(e){w.document.body.removeChild(i)})):c.document.close(),setTimeout(function(){"string"==typeof i.style.webkitTransform?i.style.webkitTransform="translateY(40px)":"string"==typeof i.style.transform?i.style.transform="translateY(40px)":i.style.top="0px"},300)}},webrtcDetectedType=null,checkMediaDataChannelSettings=function(peerBrowserAgent,peerBrowserVersion,callback,constraints){if("function"==typeof callback){var beOfferer=!0,isLocalFirefox="firefox"===webrtcDetectedBrowser,isLocalFirefoxInterop="moz"===webrtcDetectedType&&webrtcDetectedVersion>30,isPeerFirefox="firefox"===peerBrowserAgent;if(isLocalFirefox&&isPeerFirefox||isLocalFirefoxInterop)try{delete constraints.mandatory.MozDontOfferDataChannel}catch(error){}else isLocalFirefox&&!isPeerFirefox&&(constraints.mandatory.MozDontOfferDataChannel=!0);if(!isLocalFirefox)for(var prop in constraints.mandatory)constraints.mandatory.hasOwnProperty(prop)&&-1!==prop.indexOf("Moz")&&delete constraints.mandatory[prop];!isLocalFirefox||isPeerFirefox||isLocalFirefoxInterop||(beOfferer=!1),callback(beOfferer,constraints)}},checkIceConnectionState=function(peerId,iceConnectionState,callback){"function"==typeof callback&&(peerId=peerId?peerId:"peer",AdapterJS._iceConnectionFiredStates[peerId]&&iceConnectionState!==AdapterJS._iceConnectionStates.disconnected&&iceConnectionState!==AdapterJS._iceConnectionStates.failed&&iceConnectionState!==AdapterJS._iceConnectionStates.closed||(AdapterJS._iceConnectionFiredStates[peerId]=[]),iceConnectionState=AdapterJS._iceConnectionStates[iceConnectionState],AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState)<0&&(AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState),iceConnectionState===AdapterJS._iceConnectionStates.connected&&setTimeout(function(){AdapterJS._iceConnectionFiredStates[peerId].push(AdapterJS._iceConnectionStates.done),callback(AdapterJS._iceConnectionStates.done)},1e3),callback(iceConnectionState)))},createIceServer=null,createIceServers=null,RTCPeerConnection=null,RTCSessionDescription="function"==typeof RTCSessionDescription?RTCSessionDescription:null,RTCIceCandidate="function"==typeof RTCIceCandidate?RTCIceCandidate:null,getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,navigator.mozGetUserMedia||navigator.webkitGetUserMedia||navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)){var getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,webrtcUtils={log:function(){"undefined"!=typeof module||"function"==typeof require&&"function"==typeof define},extractVersion:function(uastring,expr,pos){var match=uastring.match(expr);return match&&match.length>=pos&&parseInt(match[pos],10)}};if("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=URL.createObjectURL(stream))}}),getUserMedia=window.navigator&&window.navigator.getUserMedia),attachMediaStream=function(element,stream){element.srcObject=stream},reattachMediaStream=function(to,from){to.srcObject=from.srcObject},"undefined"!=typeof window&&window.navigator)if(navigator.mozGetUserMedia){if(webrtcUtils.log("This appears to be Firefox"),webrtcDetectedBrowser="firefox",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1),webrtcMinimumVersion=31,window.RTCPeerConnection||(window.RTCPeerConnection=function(pcConfig,pcConstraints){if(38>webrtcDetectedVersion&&pcConfig&&pcConfig.iceServers){for(var newIceServers=[],i=0;iwebrtcDetectedVersion&&(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){webrtcUtils.log("This appears to be Chrome"),webrtcDetectedBrowser="chrome",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2),webrtcMinimumVersion=38,window.RTCPeerConnection=function(pcConfig,pcConstraints){pcConfig&&pcConfig.iceTransportPolicy&&(pcConfig.iceTransports=pcConfig.iceTransportPolicy);var pc=new webkitRTCPeerConnection(pcConfig,pcConstraints),origGetStats=pc.getStats.bind(pc);return pc.getStats=function(selector,successCallback,errorCallback){var self=this,args=arguments;if(arguments.length>0&&"function"==typeof selector)return origGetStats(selector,successCallback);var fixChromeStats=function(response){var standardReport={},reports=response.result();return reports.forEach(function(report){var standardStats={id:report.id,timestamp:report.timestamp,type:report.type};report.names().forEach(function(name){standardStats[name]=report.stat(name)}),standardReport[standardStats.id]=standardStats}),standardReport};if(arguments.length>=2){var successCallbackWrapper=function(response){args[1](fixChromeStats(response))};return origGetStats.apply(this,[successCallbackWrapper,arguments[0]])}return new Promise(function(resolve,reject){1===args.length&&null===selector?origGetStats.apply(self,[function(response){resolve.apply(null,[fixChromeStats(response)])},reject]):origGetStats.apply(self,[resolve,reject])})},pc},webkitRTCPeerConnection.generateCertificate&&Object.defineProperty(window.RTCPeerConnection,"generateCertificate",{get:function(){return arguments.length?webkitRTCPeerConnection.generateCertificate.apply(null,arguments):webkitRTCPeerConnection.generateCertificate}}),["createOffer","createAnswer"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var self=this;if(arguments.length<1||1===arguments.length&&"object"==typeof arguments[0]){var opts=1===arguments.length?arguments[0]:void 0;return new Promise(function(resolve,reject){nativeMethod.apply(self,[resolve,reject,opts])})}return nativeMethod.apply(this,arguments)}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(method){var nativeMethod=webkitRTCPeerConnection.prototype[method];webkitRTCPeerConnection.prototype[method]=function(){var args=arguments,self=this;return new Promise(function(resolve,reject){nativeMethod.apply(self,[args[0],function(){resolve(),args.length>=2&&args[1].apply(null,[])},function(err){reject(err),args.length>=3&&args[2].apply(null,[err])}])})}});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,enumerateDevices:function(){return new Promise(function(resolve){var kinds={audio:"audioinput",video:"videoinput"};return MediaStreamTrack.getSources(function(devices){resolve(devices.map(function(device){return{label:device.label,kind:kinds[device.kind],deviceId:device.id,groupId:""}}))})})}}),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.")}),attachMediaStream=function(element,stream){webrtcDetectedVersion>=43?element.srcObject=stream:"undefined"!=typeof element.src?element.src=URL.createObjectURL(stream):webrtcUtils.log("Error attaching stream to element.")},reattachMediaStream=function(to,from){webrtcDetectedVersion>=43?to.srcObject=from.srcObject:to.src=from.src}}else if(navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)){if(webrtcUtils.log("This appears to be Edge"),webrtcDetectedBrowser="edge",webrtcDetectedVersion=webrtcUtils.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2),webrtcMinimumVersion=10547,window.RTCIceGatherer){var generateIdentifier=function(){return Math.random().toString(36).substr(2,10)},localCName=generateIdentifier(),SDPUtils={};SDPUtils.splitLines=function(blob){return blob.trim().split("\n").map(function(line){return line.trim()})},SDPUtils.splitSections=function(blob){var parts=blob.split("\r\nm=");return parts.map(function(part,index){return(index>0?"m="+part:part).trim()+"\r\n"})},SDPUtils.matchPrefix=function(blob,prefix){return SDPUtils.splitLines(blob).filter(function(line){return 0===line.indexOf(prefix)})},SDPUtils.parseCandidate=function(line){var parts;parts=0===line.indexOf("a=candidate:")?line.substring(12).split(" "):line.substring(10).split(" ");for(var candidate={foundation:parts[0],component:parts[1],protocol:parts[2].toLowerCase(),priority:parseInt(parts[3],10),ip:parts[4],port:parseInt(parts[5],10),type:parts[7]},i=8;i-1?(parts.attribute=line.substr(sp+1,colon-sp-1),parts.value=line.substr(colon+1)):parts.attribute=line.substr(sp+1),parts},SDPUtils.getDtlsParameters=function(mediaSection,sessionpart){var lines=SDPUtils.splitLines(mediaSection);lines=lines.concat(SDPUtils.splitLines(sessionpart));var fpLine=lines.filter(function(line){return 0===line.indexOf("a=fingerprint:")})[0].substr(14),dtlsParameters={role:"auto",fingerprints:[{algorithm:fpLine.split(" ")[0],value:fpLine.split(" ")[1]}]};return dtlsParameters},SDPUtils.writeDtlsParameters=function(params,setupType){var sdp="a=setup:"+setupType+"\r\n";return params.fingerprints.forEach(function(fp){sdp+="a=fingerprint:"+fp.algorithm+" "+fp.value+"\r\n"}),sdp},SDPUtils.getIceParameters=function(mediaSection,sessionpart){var lines=SDPUtils.splitLines(mediaSection);lines=lines.concat(SDPUtils.splitLines(sessionpart));var iceParameters={usernameFragment:lines.filter(function(line){return 0===line.indexOf("a=ice-ufrag:")})[0].substr(12),password:lines.filter(function(line){return 0===line.indexOf("a=ice-pwd:")})[0].substr(10)};return iceParameters},SDPUtils.writeIceParameters=function(params){return"a=ice-ufrag:"+params.usernameFragment+"\r\na=ice-pwd:"+params.password+"\r\n"},SDPUtils.parseRtpParameters=function(mediaSection){for(var description={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},lines=SDPUtils.splitLines(mediaSection),mline=lines[0].split(" "),i=3;i0?"9":"0",sdp+=" UDP/TLS/RTP/SAVPF ",sdp+=caps.codecs.map(function(codec){return void 0!==codec.preferredPayloadType?codec.preferredPayloadType:codec.payloadType}).join(" ")+"\r\n",sdp+="c=IN IP4 0.0.0.0\r\n",sdp+="a=rtcp:9 IN IP4 0.0.0.0\r\n",caps.codecs.forEach(function(codec){sdp+=SDPUtils.writeRtpMap(codec),sdp+=SDPUtils.writeFtmp(codec),sdp+=SDPUtils.writeRtcpFb(codec)}),sdp+="a=rtcp-mux\r\n"},SDPUtils.writeSessionBoilerplate=function(){return"v=0\r\no=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},SDPUtils.writeMediaSection=function(transceiver,caps,type,stream){var sdp=SDPUtils.writeRtpDescription(transceiver.kind,caps);if(sdp+=SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters()),sdp+=SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(),"offer"===type?"actpass":"active"),sdp+="a=mid:"+transceiver.mid+"\r\n",sdp+=transceiver.rtpSender&&transceiver.rtpReceiver?"a=sendrecv\r\n":transceiver.rtpSender?"a=sendonly\r\n":transceiver.rtpReceiver?"a=recvonly\r\n":"a=inactive\r\n",transceiver.rtpSender){var msid="msid:"+stream.id+" "+transceiver.rtpSender.track.id+"\r\n";sdp+="a="+msid,sdp+="a=ssrc:"+transceiver.sendSsrc+" "+msid}return sdp+="a=ssrc:"+transceiver.sendSsrc+" cname:"+localCName+"\r\n"},SDPUtils.getDirection=function(mediaSection,sessionpart){for(var lines=SDPUtils.splitLines(mediaSection),i=0;i-1&&(this.localStreams.splice(idx,1),this._maybeFireNegotiationNeeded())},window.RTCPeerConnection.prototype._getCommonCapabilities=function(localCapabilities,remoteCapabilities){var commonCapabilities={codecs:[],headerExtensions:[],fecMechanisms:[]};return localCapabilities.codecs.forEach(function(lCodec){for(var i=0;i0,!1)}})}switch(this.localDescription=description,
+description.type){case"offer":this._updateSignalingState("have-local-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+description.type+'"')}var hasCallback=arguments.length>1&&"function"==typeof arguments[1];if(hasCallback){var cb=arguments[1];window.setTimeout(function(){cb(),self._emitBufferedCandidates()},0)}var p=Promise.resolve();return p.then(function(){hasCallback||window.setTimeout(self._emitBufferedCandidates.bind(self),0)}),p},window.RTCPeerConnection.prototype.setRemoteDescription=function(description){var self=this,stream=new MediaStream,sections=SDPUtils.splitSections(description.sdp),sessionpart=sections.shift();switch(sections.forEach(function(mediaSection,sdpMLineIndex){var transceiver,iceGatherer,iceTransport,dtlsTransport,rtpSender,rtpReceiver,sendSsrc,recvSsrc,localCapabilities,remoteIceParameters,remoteDtlsParameters,lines=SDPUtils.splitLines(mediaSection),mline=lines[0].substr(2).split(" "),kind=mline[0],rejected="0"===mline[1],direction=SDPUtils.getDirection(mediaSection,sessionpart),remoteCapabilities=SDPUtils.parseRtpParameters(mediaSection);rejected||(remoteIceParameters=SDPUtils.getIceParameters(mediaSection,sessionpart),remoteDtlsParameters=SDPUtils.getDtlsParameters(mediaSection,sessionpart));var cname,mid=SDPUtils.matchPrefix(mediaSection,"a=mid:")[0].substr(6),remoteSsrc=SDPUtils.matchPrefix(mediaSection,"a=ssrc:").map(function(line){return SDPUtils.parseSsrcMedia(line)}).filter(function(obj){return"cname"===obj.attribute})[0];if(remoteSsrc&&(recvSsrc=parseInt(remoteSsrc.ssrc,10),cname=remoteSsrc.value),"offer"===description.type){var transports=self._createIceAndDtlsTransports(mid,sdpMLineIndex);if(localCapabilities=RTCRtpReceiver.getCapabilities(kind),sendSsrc=1001*(2*sdpMLineIndex+2),rtpReceiver=new RTCRtpReceiver(transports.dtlsTransport,kind),stream.addTrack(rtpReceiver.track),self.localStreams.length>0&&self.localStreams[0].getTracks().length>=sdpMLineIndex){var localtrack=self.localStreams[0].getTracks()[sdpMLineIndex];rtpSender=new RTCRtpSender(localtrack,transports.dtlsTransport)}self.transceivers[sdpMLineIndex]={iceGatherer:transports.iceGatherer,iceTransport:transports.iceTransport,dtlsTransport:transports.dtlsTransport,localCapabilities:localCapabilities,remoteCapabilities:remoteCapabilities,rtpSender:rtpSender,rtpReceiver:rtpReceiver,kind:kind,mid:mid,cname:cname,sendSsrc:sendSsrc,recvSsrc:recvSsrc},self._transceive(self.transceivers[sdpMLineIndex],!1,"sendrecv"===direction||"sendonly"===direction)}else"answer"!==description.type||rejected||(transceiver=self.transceivers[sdpMLineIndex],iceGatherer=transceiver.iceGatherer,iceTransport=transceiver.iceTransport,dtlsTransport=transceiver.dtlsTransport,rtpSender=transceiver.rtpSender,rtpReceiver=transceiver.rtpReceiver,sendSsrc=transceiver.sendSsrc,localCapabilities=transceiver.localCapabilities,self.transceivers[sdpMLineIndex].recvSsrc=recvSsrc,self.transceivers[sdpMLineIndex].remoteCapabilities=remoteCapabilities,self.transceivers[sdpMLineIndex].cname=cname,iceTransport.start(iceGatherer,remoteIceParameters,"controlling"),dtlsTransport.start(remoteDtlsParameters),self._transceive(transceiver,"sendrecv"===direction||"recvonly"===direction,"sendrecv"===direction||"sendonly"===direction),!rtpReceiver||"sendrecv"!==direction&&"sendonly"!==direction?delete transceiver.rtpReceiver:stream.addTrack(rtpReceiver.track))}),this.remoteDescription=description,description.type){case"offer":this._updateSignalingState("have-remote-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+description.type+'"')}return window.setTimeout(function(){null!==self.onaddstream&&stream.getTracks().length&&(self.remoteStreams.push(stream),window.setTimeout(function(){self.onaddstream({stream:stream})},0))},0),arguments.length>1&&"function"==typeof arguments[1]&&window.setTimeout(arguments[1],0),Promise.resolve()},window.RTCPeerConnection.prototype.close=function(){this.transceivers.forEach(function(transceiver){transceiver.iceTransport&&transceiver.iceTransport.stop(),transceiver.dtlsTransport&&transceiver.dtlsTransport.stop(),transceiver.rtpSender&&transceiver.rtpSender.stop(),transceiver.rtpReceiver&&transceiver.rtpReceiver.stop()}),this._updateSignalingState("closed")},window.RTCPeerConnection.prototype._updateSignalingState=function(newState){this.signalingState=newState,null!==this.onsignalingstatechange&&this.onsignalingstatechange()},window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded=function(){null!==this.onnegotiationneeded&&this.onnegotiationneeded()},window.RTCPeerConnection.prototype._updateConnectionState=function(){var newState,self=this,states={"new":0,closed:0,connecting:0,checking:0,connected:0,completed:0,failed:0};this.transceivers.forEach(function(transceiver){states[transceiver.iceTransport.state]++,states[transceiver.dtlsTransport.state]++}),states.connected+=states.completed,newState="new",states.failed>0?newState="failed":states.connecting>0||states.checking>0?newState="connecting":states.disconnected>0?newState="disconnected":states["new"]>0?newState="new":(states.connecting>0||states.completed>0)&&(newState="connected"),newState!==self.iceConnectionState&&(self.iceConnectionState=newState,null!==this.oniceconnectionstatechange&&this.oniceconnectionstatechange())},window.RTCPeerConnection.prototype.createOffer=function(){var self=this;if(this._pendingOffer)throw new Error("createOffer called while there is a pending offer.");var offerOptions;1===arguments.length&&"function"!=typeof arguments[0]?offerOptions=arguments[0]:3===arguments.length&&(offerOptions=arguments[2]);var tracks=[],numAudioTracks=0,numVideoTracks=0;if(this.localStreams.length&&(numAudioTracks=this.localStreams[0].getAudioTracks().length,numVideoTracks=this.localStreams[0].getVideoTracks().length),offerOptions){if(offerOptions.mandatory||offerOptions.optional)throw new TypeError("Legacy mandatory/optional constraints not supported.");void 0!==offerOptions.offerToReceiveAudio&&(numAudioTracks=offerOptions.offerToReceiveAudio),void 0!==offerOptions.offerToReceiveVideo&&(numVideoTracks=offerOptions.offerToReceiveVideo)}for(this.localStreams.length&&this.localStreams[0].getTracks().forEach(function(track){tracks.push({kind:track.kind,track:track,wantReceive:"audio"===track.kind?numAudioTracks>0:numVideoTracks>0}),"audio"===track.kind?numAudioTracks--:"video"===track.kind&&numVideoTracks--});numAudioTracks>0||numVideoTracks>0;)numAudioTracks>0&&(tracks.push({kind:"audio",wantReceive:!0}),numAudioTracks--),numVideoTracks>0&&(tracks.push({kind:"video",wantReceive:!0}),numVideoTracks--);var sdp=SDPUtils.writeSessionBoilerplate(),transceivers=[];tracks.forEach(function(mline,sdpMLineIndex){var rtpSender,rtpReceiver,track=mline.track,kind=mline.kind,mid=generateIdentifier(),transports=self._createIceAndDtlsTransports(mid,sdpMLineIndex),localCapabilities=RTCRtpSender.getCapabilities(kind),sendSsrc=1001*(2*sdpMLineIndex+1);track&&(rtpSender=new RTCRtpSender(track,transports.dtlsTransport)),mline.wantReceive&&(rtpReceiver=new RTCRtpReceiver(transports.dtlsTransport,kind)),transceivers[sdpMLineIndex]={iceGatherer:transports.iceGatherer,iceTransport:transports.iceTransport,dtlsTransport:transports.dtlsTransport,localCapabilities:localCapabilities,remoteCapabilities:null,rtpSender:rtpSender,rtpReceiver:rtpReceiver,kind:kind,mid:mid,sendSsrc:sendSsrc,recvSsrc:null};var transceiver=transceivers[sdpMLineIndex];sdp+=SDPUtils.writeMediaSection(transceiver,transceiver.localCapabilities,"offer",self.localStreams[0])}),this._pendingOffer=transceivers;var desc=new RTCSessionDescription({type:"offer",sdp:sdp});return arguments.length&&"function"==typeof arguments[0]&&window.setTimeout(arguments[0],0,desc),Promise.resolve(desc)},window.RTCPeerConnection.prototype.createAnswer=function(){var answerOptions,self=this;1===arguments.length&&"function"!=typeof arguments[0]?answerOptions=arguments[0]:3===arguments.length&&(answerOptions=arguments[2]);var sdp=SDPUtils.writeSessionBoilerplate();this.transceivers.forEach(function(transceiver){var commonCapabilities=self._getCommonCapabilities(transceiver.localCapabilities,transceiver.remoteCapabilities);sdp+=SDPUtils.writeMediaSection(transceiver,commonCapabilities,"answer",self.localStreams[0])});var desc=new RTCSessionDescription({type:"answer",sdp:sdp});return arguments.length&&"function"==typeof arguments[0]&&window.setTimeout(arguments[0],0,desc),Promise.resolve(desc)},window.RTCPeerConnection.prototype.addIceCandidate=function(candidate){var mLineIndex=candidate.sdpMLineIndex;if(candidate.sdpMid)for(var i=0;i0?SDPUtils.parseCandidate(candidate.candidate):{};if("tcp"===cand.protocol&&0===cand.port)return;if("1"!==cand.component)return;"endOfCandidates"===cand.type&&(cand={}),transceiver.iceTransport.addRemoteCandidate(cand)}return arguments.length>1&&"function"==typeof arguments[1]&&window.setTimeout(arguments[1],0),Promise.resolve()},window.RTCPeerConnection.prototype.getStats=function(){var promises=[];this.transceivers.forEach(function(transceiver){["rtpSender","rtpReceiver","iceGatherer","iceTransport","dtlsTransport"].forEach(function(method){transceiver[method]&&promises.push(transceiver[method].getStats())})});var cb=arguments.length>1&&"function"==typeof arguments[1]&&arguments[1];return new Promise(function(resolve){var results={};Promise.all(promises).then(function(res){res.forEach(function(result){Object.keys(result).forEach(function(id){results[id]=result[id]})}),cb&&window.setTimeout(cb,0,results),resolve(results)})})}}}else webrtcUtils.log("Browser does not appear to be WebRTC-capable");else webrtcUtils.log("This does not appear to be a browser"),webrtcDetectedBrowser="not a browser";var webrtcTesting={};try{Object.defineProperty(webrtcTesting,"version",{set:function(version){webrtcDetectedVersion=version}})}catch(e){}AdapterJS.parseWebrtcDetectedBrowser(),navigator.mozGetUserMedia?(MediaStreamTrack.getSources=function(successCb){setTimeout(function(){var infos=[{kind:"audio",id:"default",label:"",facing:""},{kind:"video",id:"default",label:"",facing:""}];successCb(infos)},0)},createIceServer=function(url,username,password){var iceServer=null,urlParts=url.split(":");if(0===urlParts[0].indexOf("stun"))iceServer={urls:[url]};else if(0===urlParts[0].indexOf("turn"))if(27>webrtcDetectedVersion){var turnUrlParts=url.split("?");(1===turnUrlParts.length||0===turnUrlParts[1].indexOf("transport=udp"))&&(iceServer={urls:[turnUrlParts[0]],credential:password,username:username})}else iceServer={urls:[url],credential:password,username:username};return iceServer},createIceServers=function(urls,username,password){var iceServers=[];for(i=0;i=34)iceServers={urls:urls,credential:password,username:username};else for(i=0;i=webrtcDetectedVersion){var frag=document.createDocumentFragment();for(AdapterJS.WebRTCPlugin.plugin=document.createElement("div"),AdapterJS.WebRTCPlugin.plugin.innerHTML='";AdapterJS.WebRTCPlugin.plugin.firstChild;)frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);document.body.appendChild(frag),AdapterJS.WebRTCPlugin.plugin=document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId)}else AdapterJS.WebRTCPlugin.plugin=document.createElement("object"),AdapterJS.WebRTCPlugin.plugin.id=AdapterJS.WebRTCPlugin.pluginInfo.pluginId,isIE?(AdapterJS.WebRTCPlugin.plugin.width="1px",AdapterJS.WebRTCPlugin.plugin.height="1px"):(AdapterJS.WebRTCPlugin.plugin.width="0px",AdapterJS.WebRTCPlugin.plugin.height="0px"),AdapterJS.WebRTCPlugin.plugin.type=AdapterJS.WebRTCPlugin.pluginInfo.type,AdapterJS.WebRTCPlugin.plugin.innerHTML=' '+(AdapterJS.options.getAllCams?'':"")+'',document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED}},AdapterJS.WebRTCPlugin.isPluginInstalled=function(comName,plugName,installedCb,notInstalledCb){if(isIE){try{new ActiveXObject(comName+"."+plugName)}catch(e){return void notInstalledCb()}installedCb()}else{for(var pluginArray=navigator.plugins,i=0;i=0)return void installedCb();notInstalledCb()}},AdapterJS.WebRTCPlugin.defineWebRTCInterface=function(){AdapterJS.WebRTCPlugin.pluginState!==AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY&&(AdapterJS.WebRTCPlugin.pluginState=AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING,AdapterJS.isDefined=function(variable){return null!==variable&&void 0!==variable},createIceServer=function(url,username,password){var iceServer=null,urlParts=url.split(":");return 0===urlParts[0].indexOf("stun")?iceServer={url:url,hasCredentials:!1}:0===urlParts[0].indexOf("turn")&&(iceServer={url:url,hasCredentials:!0,credential:password,username:username}),iceServer},createIceServers=function(urls,username,password){for(var iceServers=[],i=0;i1)return AdapterJS.WebRTCPlugin.plugin.PeerConnection(servers);var iceServers=null;if(servers&&Array.isArray(servers.iceServers)){iceServers=servers.iceServers;for(var i=0;i ';temp.firstChild;)frag.appendChild(temp.firstChild);var height="",width="";element.clientWidth||element.clientHeight?(width=element.clientWidth,height=element.clientHeight):(element.width||element.height)&&(width=element.width,height=element.height),element.parentNode.insertBefore(frag,element),frag=document.getElementById(elementId),frag.width=width,frag.height=height,element.parentNode.removeChild(element)}else{for(var children=element.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){children[i].value=streamId;break}element.setStreamId(streamId)}var newElement=document.getElementById(elementId);return AdapterJS.forwardEventHandlers(newElement,element,Object.getPrototypeOf(element)),newElement}},reattachMediaStream=function(to,from){for(var stream=null,children=from.children,i=0;i!==children.length;++i)if("streamId"===children[i].name){AdapterJS.WebRTCPlugin.WaitForPluginReady(),stream=AdapterJS.WebRTCPlugin.plugin.getStreamWithId(AdapterJS.WebRTCPlugin.pageId,children[i].value);break}return null!==stream?attachMediaStream(to,stream):void 0},window.attachMediaStream=attachMediaStream,window.reattachMediaStream=reattachMediaStream,window.getUserMedia=getUserMedia,AdapterJS.attachMediaStream=attachMediaStream,AdapterJS.reattachMediaStream=reattachMediaStream,AdapterJS.getUserMedia=getUserMedia,AdapterJS.forwardEventHandlers=function(destElem,srcElem,prototype){properties=Object.getOwnPropertyNames(prototype);for(var prop in properties)prop&&(propName=properties[prop],"function"==typeof propName.slice&&"on"===propName.slice(0,2)&&"function"==typeof srcElem[propName]&&AdapterJS.addEvent(destElem,propName.slice(2),srcElem[propName]));var subPrototype=Object.getPrototypeOf(prototype);subPrototype&&AdapterJS.forwardEventHandlers(destElem,srcElem,subPrototype)},RTCIceCandidate=function(candidate){return candidate.sdpMid||(candidate.sdpMid=""),AdapterJS.WebRTCPlugin.WaitForPluginReady(),AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(candidate.sdpMid,candidate.sdpMLineIndex,candidate.candidate)},AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.injectPlugin),AdapterJS.WebRTCPlugin.injectPlugin())},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb=AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb||function(){AdapterJS.addEvent(document,"readystatechange",AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv),AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv()},AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv=function(){if(!AdapterJS.options.hidePluginInstallPrompt){var downloadLink=AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;if(downloadLink){var popupString;popupString=AdapterJS.WebRTCPlugin.pluginInfo.portalLink?'This website requires you to install the '+AdapterJS.WebRTCPlugin.pluginInfo.companyName+" WebRTC Plugin to work on this browser.":AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION,AdapterJS.renderNotificationBar(popupString,AdapterJS.TEXT.PLUGIN.BUTTON,downloadLink)}else AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED)}},AdapterJS.WebRTCPlugin.isPluginInstalled(AdapterJS.WebRTCPlugin.pluginInfo.prefix,AdapterJS.WebRTCPlugin.pluginInfo.plugName,AdapterJS.WebRTCPlugin.defineWebRTCInterface,AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);!function(){"use strict";var baseGetUserMedia=null;AdapterJS.TEXT.EXTENSION={REQUIRE_INSTALLATION_FF:"To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.",REQUIRE_INSTALLATION_CHROME:"To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.",REQUIRE_REFRESH:"Please refresh this page after the Skylink WebRTC tools extension has been installed.",BUTTON_FF:"Install Now",BUTTON_CHROME:"Go to Chrome Web Store"};var clone=function(obj){if(null===obj||"object"!=typeof obj)return obj;var copy=obj.constructor();for(var attr in obj)obj.hasOwnProperty(attr)&&(copy[attr]=obj[attr]);return copy};if(window.navigator.mozGetUserMedia?(baseGetUserMedia=window.navigator.getUserMedia,navigator.getUserMedia=function(constraints,successCb,failureCb){if(constraints&&constraints.video&&constraints.video.mediaSource){if("screen"!==constraints.video.mediaSource&&"window"!==constraints.video.mediaSource)return void failureCb(new Error('GetUserMedia: Only "screen" and "window" are supported as mediaSource constraints'));var updatedConstraints=clone(constraints);updatedConstraints.video.mozMediaSource=updatedConstraints.video.mediaSource;var checkIfReady=setInterval(function(){"complete"===document.readyState&&(clearInterval(checkIfReady),baseGetUserMedia(updatedConstraints,successCb,function(error){["PermissionDeniedError","SecurityError"].indexOf(error.name)>-1&&"https:"===window.parent.location.protocol?AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,AdapterJS.TEXT.EXTENSION.BUTTON_FF,"https://addons.mozilla.org/en-US/firefox/addon/skylink-webrtc-tools/",!0,!0):failureCb(error)}))},1)}else baseGetUserMedia(constraints,successCb,failureCb)},AdapterJS.getUserMedia=window.getUserMedia=navigator.getUserMedia,navigator.mediaDevices.getUserMedia=function(constraints){return new Promise(function(resolve,reject){window.getUserMedia(constraints,resolve,reject)})}):window.navigator.webkitGetUserMedia?(baseGetUserMedia=window.navigator.getUserMedia,navigator.getUserMedia=function(constraints,successCb,failureCb){if(constraints&&constraints.video&&constraints.video.mediaSource){if("chrome"!==window.webrtcDetectedBrowser)return void failureCb(new Error("Current browser does not support screensharing"));var updatedConstraints=clone(constraints),chromeCallback=function(error,sourceId){error?failureCb("permission-denied"===error?new Error("Permission denied for screen retrieval"):new Error("Failed retrieving selected screen")):(updatedConstraints.video.mandatory=updatedConstraints.video.mandatory||{},updatedConstraints.video.mandatory.chromeMediaSource="desktop",updatedConstraints.video.mandatory.maxWidth=window.screen.width>1920?window.screen.width:1920,updatedConstraints.video.mandatory.maxHeight=window.screen.height>1080?window.screen.height:1080,sourceId&&(updatedConstraints.video.mandatory.chromeMediaSourceId=sourceId),delete updatedConstraints.video.mediaSource,baseGetUserMedia(updatedConstraints,successCb,failureCb))},onIFrameCallback=function(event){event.data&&(event.data.chromeMediaSourceId&&("PermissionDeniedError"===event.data.chromeMediaSourceId?chromeCallback("permission-denied"):chromeCallback(null,event.data.chromeMediaSourceId)),event.data.chromeExtensionStatus&&("not-installed"===event.data.chromeExtensionStatus?AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,AdapterJS.TEXT.EXTENSION.BUTTON_CHROME,event.data.data,!0,!0):chromeCallback(event.data.chromeExtensionStatus,null)),window.removeEventListener("message",onIFrameCallback))};window.addEventListener("message",onIFrameCallback),postFrameMessage({captureSourceId:!0})}else baseGetUserMedia(constraints,successCb,failureCb)},AdapterJS.getUserMedia=window.getUserMedia=navigator.getUserMedia,navigator.mediaDevices.getUserMedia=function(constraints){return new Promise(function(resolve,reject){window.getUserMedia(constraints,resolve,reject)})}):navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)||(baseGetUserMedia=window.navigator.getUserMedia,navigator.getUserMedia=function(constraints,successCb,failureCb){if(constraints&&constraints.video&&constraints.video.mediaSource){var updatedConstraints=clone(constraints);AdapterJS.WebRTCPlugin.callWhenPluginReady(function(){return AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature&&AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable?(updatedConstraints.video.optional=updatedConstraints.video.optional||[],updatedConstraints.video.optional.push({sourceId:AdapterJS.WebRTCPlugin.plugin.screensharingKey||"Screensharing"}),delete updatedConstraints.video.mediaSource,void baseGetUserMedia(updatedConstraints,successCb,failureCb)):void failureCb(new Error("Your version of the WebRTC plugin does not support screensharing"))})}else baseGetUserMedia(constraints,successCb,failureCb)},AdapterJS.getUserMedia=getUserMedia=window.getUserMedia=navigator.getUserMedia,navigator.mediaDevices.getUserMedia=requestUserMedia),"chrome"===window.webrtcDetectedBrowser){var iframe=document.createElement("iframe");iframe.onload=function(){iframe.isLoaded=!0},iframe.src="https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html",iframe.style.display="none",(document.body||document.documentElement).appendChild(iframe);var postFrameMessage=function(object){return object=object||{},iframe.isLoaded?void iframe.contentWindow.postMessage(object,"*"):void setTimeout(function(){iframe.contentWindow.postMessage(object,"*")},100)}}else"opera"===window.webrtcDetectedBrowser}();
\ No newline at end of file
diff --git a/source/adapter.MediaStream.js b/source/adapter.MediaStream.js
deleted file mode 100644
index 56fb138..0000000
--- a/source/adapter.MediaStream.js
+++ /dev/null
@@ -1,696 +0,0 @@
-// Polyfill all MediaStream objects
-var polyfillMediaStream = null;
-
-// Firefox MediaStream
-if (navigator.mozGetUserMedia) {
-
- /**
- * The polyfilled MediaStream class.
- * @class MediaStream
- * @since 0.10.5
- */
- polyfillMediaStream = function (stream) {
-
- /**
- * The MediaStream object id.
- * @attribute id
- * @type String
- * @readOnly
- * @for MediaStream
- * @since 0.10.6
- */
- try {
- stream.id = stream.id || (new Date()).getTime().toString();
- } catch (error) {
- console.warn('Unable to polyfill MediaStream.id');
- }
-
- /**
- * The flag that indicates if a MediaStream object has ended.
- * @attribute ended
- * @type Boolean
- * @readOnly
- * @for MediaStream
- * @since 0.10.6
- */
- stream.ended = typeof stream.ended === 'boolean' ? stream.ended : false;
-
- /**
- * Event triggered when MediaStream has ended streaming.
- * @event onended
- * @param {String} type The type of event: "ended"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onended = null;
-
- /**
- * Event triggered when MediaStream has added a new track.
- * @event onaddtrack
- * @param {String} type The type of event: "addtrack"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onaddtrack = null;
-
- /**
- * Event triggered when MediaStream has removed an existing track.
- * @event onremovetrack
- * @param {String} type The type of event: "removetrack"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onremovetrack = null;
-
-
- var polyEndedEmitter = function () {
- // set the ended as true
- stream.ended = true;
-
- // trigger that it has ended
- if (typeof stream.onended === 'function') {
- stream.onended({
- type: 'ended',
- bubbles: false,
- cancelBubble: false,
- cancelable: false,
- currentTarget: stream,
- defaultPrevented: false,
- eventPhase: 0,
- returnValue: true,
- srcElement: stream,
- target: stream,
- timeStamp: stream.currentTime || (new Date()).getTime()
- });
- }
- };
-
- var polyTrackEndedEmitter = function (track) {
- // set the ended as true
- track.ended = true;
-
- // trigger that it has ended
- if (typeof track.onended === 'function') {
- track.onended({
- type: 'ended',
- bubbles: false,
- cancelBubble: false,
- cancelable: false,
- currentTarget: track,
- defaultPrevented: false,
- eventPhase: 0,
- returnValue: true,
- srcElement: track,
- target: track,
- timeStamp: stream.currentTime || (new Date()).getTime()
- });
- }
- };
-
-
- (function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- polyfillMediaStreamTrack( audioTracks[i] );
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- polyfillMediaStreamTrack( videoTracks[j] );
- }
- })();
-
- /**
- * Stops a MediaStream streaming.
- * @method polystop
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polystop = function () {
- if (stream instanceof LocalMediaStream) {
- stream.stop();
-
- var i, j;
-
- var outputAudioTracks = stream.polygetAudioTracks();
- var outputVideoTracks = stream.polygetVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < outputAudioTracks.length; i += 1) {
- outputAudioTracks[i].ended = true;
- }
-
- for (j = 0; j < outputVideoTracks.length; j += 1) {
- outputVideoTracks[j].ended = true;
- }
-
- } else {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- for (i = 0; i < audioTracks.length; i += 1) {
- audioTracks[i].polystop();
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- videoTracks[j].polystop();
- }
- }
- };
-
- /**
- * Adds a MediaStreamTrack to an object.
- * @method polyaddTrack
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polyaddTrack = function (track) {
- try {
- stream.addTrack(track);
- } catch (error) {
- throw error;
- }
- };
-
- /**
- * Gets a MediaStreamTrack from a MediaStreamTrack based on the object id provided.
- * @method polygetTrackById
- * @param {String} trackId The MediaStreamTrack object id.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetTrackById = function (trackId) {
- try {
- return stream.getTrackById(trackId);
-
- } catch (error) {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- if (audioTracks[i].id === trackId) {
- return audioTracks[i];
- }
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- if (videoTracks[i].id === trackId) {
- return videoTracks[i];
- }
- }
-
- return null;
- }
- };
-
- /**
- * Gets all MediaStreamTracks from a MediaStreamTrack.
- * @method polygetTracks
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetTracks = function (trackId) {
- try {
- return stream.getTracks();
-
- } catch (error) {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- return audioTracks.concat(videoTracks);
- }
- };
-
- /**
- * Removes a MediaStreamTrack from an object.
- * @method polyremoveTrack
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polyremoveTrack = function (track) {
- try {
- stream.removeTrack(track);
- } catch (error) {
- throw error;
- }
- };
-
- /**
- * Gets the list of audio MediaStreamTracks of a MediaStream.
- * @method polygetAudioTracks
- * @return {Array} Returns a list of the audio MediaStreamTracks
- * available for the MediaStream.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetAudioTracks = stream.getAudioTracks;
-
- /**
- * Gets the list of video MediaStreamTracks of a MediaStream.
- * @method polygetVideoTracks
- * @return {Array} Returns a list of the video MediaStreamTracks
- * available for the MediaStream.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetVideoTracks = stream.getVideoTracks;
-
- /**
- * Listens and waits to check if all MediaStreamTracks of a MediaStream
- * has ended. Once ended, this invokes the ended flag of the MediaStream.
- * This loops every second.
- * @method _polyOnTracksEndedListener
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- stream._polyOnTracksEndedListener = setInterval(function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- var audioEnded = true;
- var videoEnded = true;
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- if (audioTracks[i].ended !== true) {
- audioEnded = false;
- break;
- }
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- if (videoTracks[j].ended !== true) {
- videoEnded = false;
- break;
- }
- }
-
- if (audioEnded && videoEnded) {
- clearInterval(stream._polyOnTracksEndedListener);
- stream.ended = true;
- }
- }, 1000);
-
- /**
- * Listens and waits to check if all MediaStream has ended.
- * This loops every second.
- * @method _polyOnEndedListener
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- if (stream instanceof LocalMediaStream) {
- stream._polyOnEndedListener = setInterval(function () {
- // If stream has flag ended because of media tracks being stopped
- if (stream.ended) {
- clearInterval(stream._polyOnEndedListener);
-
- polyEndedEmitter();
-
- return;
- }
-
- if (typeof stream.recordedTime === 'undefined') {
- stream.recordedTime = 0;
- }
-
- if (stream.recordedTime === stream.currentTime) {
- clearInterval(stream._polyOnEndedListener);
-
- polyEndedEmitter();
-
- return;
-
- } else {
- stream.recordedTime = stream.currentTime;
- }
- }, 1000);
-
- } else {
- /**
- * Stores the attached video element with the existing MediaStream
- * This loops every second.
- * - This only exists in Firefox browsers.
- * @attribute _polyOnEndedListenerObj
- * @type DOM
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- // Use a video to attach to check if stream has ended
- var video = document.createElement('video');
-
- video._polyOnEndedListener = setInterval(function () {
- // If stream has flag ended because of media tracks being stopped
- if (stream.ended) {
- clearInterval(video._polyOnEndedListener);
-
- polyEndedEmitter();
-
- return;
- }
-
- // Check if mozSrcObject is not empty
- if (typeof video.mozSrcObject === 'object' &&
- video.mozSrcObject !== null) {
-
- if (video.mozSrcObject.ended === true) {
- clearInterval(video._polyOnEndedListener);
-
- polyEndedEmitter();
-
- return;
- }
- }
- }, 1000);
-
- // Bind the video element to MediaStream object
- stream._polyOnEndedListenerObj = video;
-
- window.attachMediaStream(video, stream);
- }
- };
-
- window.navigator.getUserMedia = function (constraints, successCb, failureCb) {
-
- window.navigator.mozGetUserMedia(constraints, function (stream) {
- polyfillMediaStream(stream);
-
- successCb(stream);
-
- }, failureCb);
- };
-
- window.getUserMedia = window.navigator.getUserMedia;
-
- window.attachMediaStream = function (element, stream) {
- // If there's an element used for checking stream stop
- // for an instance remote MediaStream for firefox
- // reattachmediastream instead
- if (typeof stream._polyOnEndedListenerObj !== 'undefined' &&
- stream instanceof LocalMediaStream === false) {
- window.reattachMediaStream(element, bind._polyOnEndedListenerObj);
-
- // LocalMediaStream
- } else {
- console.log('Attaching media stream');
- element.mozSrcObject = stream;
- }
- };
-
-// Chrome / Opera MediaStream
-} else if (navigator.webkitGetUserMedia) {
-
- polyfillMediaStream = function (stream) {
-
- stream.id = stream.id || (new Date()).getTime().toString();
-
- stream.ended = typeof stream.ended === 'boolean' ? stream.ended : false;
-
- stream.onended = null;
-
- stream.onaddtrack = null;
-
- stream.onremovetrack = null;
-
-
- (function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- polyfillMediaStreamTrack( audioTracks[i] );
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- polyfillMediaStreamTrack( videoTracks[j] );
- }
- })();
-
- stream.polystop = function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- try {
- stream.stop();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- if (audioTracks[i].readyState !== 'ended') {
- audioTracks[i].polystop();
- }
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- if (videoTracks[j].readyState !== 'ended') {
- videoTracks[j].polystop();
- }
- }
-
- } catch (error) {
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- audioTracks[i].polystop();
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- videoTracks[j].polystop();
- }
- }
- };
-
- stream.polyaddTrack = function (track) {
- try {
- stream.addTrack(track);
- } catch (error) {
- throw error;
- }
- };
-
- stream.polygetTrackById = function (trackId) {
- try {
- return stream.getTrackById(trackId);
-
- } catch (err) {
-
- console.log(err);
-
- var i, j;
-
- var outputAudioTracks = polyStoreMediaTracks.audio;
- var outputVideoTracks = polyStoreMediaTracks.video;
-
- // Check for all tracks if ended
- for (i = 0; i < outputAudioTracks.length; i += 1) {
- if (outputAudioTracks[i].id === trackId) {
- return outputAudioTracks[i];
- }
- }
-
- for (j = 0; j < outputVideoTracks.length; j += 1) {
- if (outputVideoTracks[j].id === trackId) {
- return outputVideoTracks[j];
- }
- }
-
- return null;
- }
- };
-
- stream.polygetTracks = function (trackId) {
- try {
- return stream.getTracks();
-
- } catch (error) {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- return audioTracks.concat(videoTracks);
- }
- };
-
- stream.polyremoveTrack = function (track) {
- try {
- stream.removeTrack(track);
- } catch (error) {
- throw error;
- }
- };
-
- stream.polygetAudioTracks = stream.getAudioTracks;
-
- stream.polygetVideoTracks = stream.getVideoTracks;
- };
-
- window.navigator.getUserMedia = function (constraints, successCb, failureCb) {
- navigator.webkitGetUserMedia(constraints, function (stream) {
-
- polyfillMediaStream(stream);
-
- successCb(stream);
- }, failureCb);
-
- };
-
- window.getUserMedia = window.navigator.getUserMedia;
-
-// Safari MediaStream
-} else {
-
- polyfillMediaStream = function (stream) {
-
- stream.id = stream.id || (new Date()).getTime().toString();
-
- stream.ended = typeof stream.ended === 'boolean' ? stream.ended : false;
-
- stream.onended = null;
-
- stream.onaddtrack = null;
-
- stream.onremovetrack = null;
-
- // MediaStreamTracks Polyfilled
- var polyStoreMediaTracks = {
- audio: [],
- video: []
- };
-
- (function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- var outputAudioTracks = [];
- var outputVideoTracks = [];
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- var audioTrack = polyfillMediaStreamTrack( audioTracks[i] );
- outputAudioTracks.push(audioTrack);
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- var videoTrack = polyfillMediaStreamTrack( videoTracks[j] );
- outputVideoTracks.push(videoTrack);
- }
-
- polyStoreMediaTracks.audio = outputAudioTracks;
- polyStoreMediaTracks.video = outputVideoTracks;
- })();
-
- stream.polystop = function () {
- stream.stop();
-
- stream.ended = true;
-
- var i, j;
-
- var outputAudioTracks = polyStoreMediaTracks.audio;
- var outputVideoTracks = polyStoreMediaTracks.video;
-
- // Check for all tracks if ended
- for (i = 0; i < outputAudioTracks.length; i += 1) {
- outputAudioTracks[i].ended = true;
- }
-
- for (j = 0; j < outputVideoTracks.length; j += 1) {
- outputVideoTracks[j].ended = true;
- }
- };
-
- stream.polyaddTrack = function (track) {
- try {
- stream.addTrack(track);
- } catch (error) {
- throw error;
- }
- };
-
- stream.polygetTrackById = function (trackId) {
- // return stream.getTrackById(trackId);
- // for right now, because MediaStreamTrack does not allow overwrites,
- // we shall implement the polyfill to return the overwrite-able track.
- var i, j;
-
- var outputAudioTracks = polyStoreMediaTracks.audio;
- var outputVideoTracks = polyStoreMediaTracks.video;
-
- // Check for all tracks if ended
- for (i = 0; i < outputAudioTracks.length; i += 1) {
- if (outputAudioTracks[i].id === trackId) {
- return outputAudioTracks[i];
- }
- }
-
- for (j = 0; j < outputVideoTracks.length; j += 1) {
- if (outputVideoTracks[j].id === trackId) {
- return outputVideoTracks[j];
- }
- }
-
- return null;
- };
-
- stream.polygetTracks = function (trackId) {
- var outputAudioTracks = polyStoreMediaTracks.audio;
- var outputVideoTracks = polyStoreMediaTracks.video;
-
- return outputAudioTracks.concat(outputVideoTracks);
- };
-
- stream.polyremoveTrack = function (track) {
- try {
- stream.removeTrack(track);
- } catch (error) {
- throw error;
- }
- };
-
- stream.polygetAudioTracks = function () {
- return polyStoreMediaTracks.audio;
- };
-
- stream.polygetVideoTracks = function () {
- return polyStoreMediaTracks.video;
- };
- };
-
- var originalGUM = navigator.getUserMedia;
-
- window.navigator.getUserMedia = function (constraints, successCb, failureCb) {
- originalGUM(constraints, function(stream) {
-
- polyfillMediaStream(stream);
-
- successCb(stream);
- }, failureCb);
- };
-
- window.getUserMedia = window.navigator.getUserMedia;
-}
\ No newline at end of file
diff --git a/source/adapter.MediaStreamTrack.js b/source/adapter.MediaStreamTrack.js
deleted file mode 100644
index 2c8c059..0000000
--- a/source/adapter.MediaStreamTrack.js
+++ /dev/null
@@ -1,322 +0,0 @@
-// Polyfill all MediaStream objects
-var polyfillMediaStreamTrack = null;
-
-
-if (navigator.mozGetUserMedia) {
-
- /**
- * The polyfilled MediaStreamTrack class.
- * @class MediaStreamTrack
- * @since 0.10.5
- */
- polyfillMediaStreamTrack = function (track) {
-
- /**
- * The MediaStreamTrack object id.
- * @attribute id
- * @type String
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- //track.id = track.id || (new Date()).getTime().toString();
-
- /**
- * The MediaStreamTrack object label.
- * @attribute label
- * @type String
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- //track.label = track.label || track.kind + '-' + track.id;
-
- /**
- * The flag that indicates if a MediaStreamTrack object has ended.
- * @attribute ended
- * @type Boolean
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.ended = typeof track.ended === 'boolean' ? track.ended : false;
-
- /**
- * The flag that indicates if a MediaStreamTrack object is a remote stream.
- * @attribute remote
- * @type Boolean
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.remote = typeof track.remote === 'boolean' ? track.remote : false;
-
- /**
- * The flag that indicates if a MediaStreamTrack object is enabled.
- * - Set it to true
for enabled track stream or set it to
- * false
for disable track stream.
- * @attribute enabled
- * @type Boolean
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.enabled = true;
-
- /**
- * The flag that indicates if a MediaStreamTrack object is muted.
- * @attribute muted
- * @type Boolean
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.muted = typeof track.muted === 'boolean' ? track.muted : false;
-
- /**
- * The ready state status of a MediaStreamTrack object.
- * @attribute readyState
- * @type String
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.readyState = typeof track.readyState === 'string' ? track.readyState : 'live';
-
- /**
- * The MediaStreamTrack object type.
- * - "audio"
: The MediaStreamTrack object type is an audio track.
- * - "video"
: The MediaStreamTrack object type is an video track.
- * @attribute kind
- * @type String
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- //track.kind = track.kind;
-
- /**
- * The status if a MediaStreamTrack object is read only and cannot to be overwritten.
- * @attribute readOnly
- * @type Boolean
- * @readOnly
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.readOnly = typeof track.readOnly === 'boolean' ? track.readOnly : false;
-
- /**
- * Event triggered when MediaStreamTrack has ended streaming.
- * @event onended
- * @param {String} type The type of event: "ended"
.
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.onended = null;
-
- /**
- * Event triggered when MediaStreamTrack has started streaming.
- * @event onstarted
- * @param {String} type The type of event: "started"
.
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.onstarted = null;
-
- /**
- * Event triggered when MediaStreamTrack has been muted.
- * @event onmute
- * @param {String} type The type of event: "mute"
.
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.onmute = null;
-
- /**
- * Event triggered when MediaStreamTrack has been unmuted.
- * @event onunmute
- * @param {String} type The type of event: "unmute"
.
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.onunmute = null;
-
- /**
- * Event triggered when MediaStreamTrack is over constrained.
- * @event onoverconstrained
- * @param {String} type The type of event: "overconstrained"
.
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.onoverconstrained = null;
-
- /**
- * Listens and waits to check if all MediaStreamTracks of a MediaStream
- * has ended. Once ended, this invokes the ended flag of the MediaStream.
- * This loops every second.
- * @method _polyOnTracksEndedListener
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- track._polyOnEndedListener = setInterval(function () {
- if (track.ended) {
-
- clearInterval(track._polyOnEndedListener);
-
- // set the readyState to 'ended'
- track.readyState = 'ended';
-
- // trigger that it has ended
- if (typeof track.onended === 'function') {
- track.onended({
- type: 'ended',
- bubbles: false,
- cancelBubble: false,
- cancelable: false,
- currentTarget: track,
- defaultPrevented: false,
- eventPhase: 0,
- returnValue: true,
- srcElement: track,
- target: track,
- timeStamp: (new Date()).getTime()
- });
- }
- }
- }, 1000);
-
- /**
- * Stops a MediaStreamTrack streaming.
- * @method polystop
- * @for MediaStreamTrack
- * @since 0.10.6
- */
- track.polystop = function () {
- track.stop();
-
- // set the ended as true
- track.ended = true;
- };
- };
-
-
-} else if (navigator.webkitGetUserMedia) {
-
- polyfillMediaStreamTrack = function (track) {
-
- //track.id = track.id || (new Date()).getTime().toString();
-
- track.label = track.label || track.kind + '-' + track.id;
-
- track.ended = false;
-
- track.remote = typeof track.remote === 'boolean' ? track.remote : false;
-
- track.enabled = true;
-
- track.muted = typeof track.muted === 'boolean' ? track.muted : false;
-
- track.readyState = typeof track.readyState === 'string' ? track.readyState : 'live';
-
- //track.kind = track.kind;
-
- track.readOnly = typeof track.readOnly === 'boolean' ? track.readOnly : false;
-
- track.onended = null;
-
- track.onstarted = null;
-
- track.onmute = null;
-
- track.onunmute = null;
-
- track.onoverconstrained = null;
-
- track.polystop = function () {
- try {
- track.stop();
-
- // set the ended state to true
- track.ended = true;
-
- } catch (error) {
- throw error;
- }
- };
- };
-
-} else {
-
- polyfillMediaStreamTrack = function (track) {
-
- track.id = track.id || (new Date()).getTime().toString();
-
- track.label = typeof track.label === 'undefined' ? track.kind + '-' + track.id : track.label;
-
- track.ended = false;
-
- track.remote = typeof track.remote === 'boolean' ? track.remote : false;
-
- track.enabled = true;
-
- track.muted = typeof track.muted === 'boolean' ? track.muted : false;
-
- track.readyState = typeof track.readyState === 'string' ? track.readyState : 'live';
-
- //track.kind = track.kind;
-
- track.readOnly = typeof track.readOnly === 'boolean' ? track.readOnly : false;
-
- track.onended = null;
-
- track.onstarted = null;
-
- track.onmute = null;
-
- track.onunmute = null;
-
- track.onoverconstrained = null;
-
- track._polyOnEndedListener = setInterval(function () {
- if (track.ended) {
-
- clearInterval(track._polyOnEndedListener);
-
- // set the readyState to 'ended'
- track.readyState = 'ended';
-
- // trigger that it has ended
- if (typeof track.onended === 'function') {
- track.onended({
- type: 'ended',
- bubbles: false,
- cancelBubble: false,
- cancelable: false,
- currentTarget: track,
- defaultPrevented: false,
- eventPhase: 0,
- returnValue: true,
- srcElement: track,
- target: track,
- timeStamp: (new Date()).getTime()
- });
- }
- }
- }, 1000);
-
- track.polystop = function () {
- try {
- track.stop();
-
- // set the ended as true
- track.ended = true;
-
- } catch (error) {
- throw error;
- }
- };
-
- return track;
- };
-}
\ No newline at end of file
diff --git a/source/adapter.RTCPeerConnection.js b/source/adapter.RTCPeerConnection.js
deleted file mode 100644
index 2d9a5c3..0000000
--- a/source/adapter.RTCPeerConnection.js
+++ /dev/null
@@ -1,630 +0,0 @@
-// Polyfill all MediaStream objects
-var polyfillRTCPeerConnection = null;
-
-// Return the event payload
-var returnEventPayloadFn = function (stream) {
- return {
- bubbles: false,
- cancelBubble: false,
- cancelable: false,
- currentTarget: stream,
- defaultPrevented: false,
- eventPhase: 0,
- returnValue: true,
- srcElement: stream,
- target: stream,
- timeStamp: stream.currentTime || (new Date()).getTime()
- };
-};
-
-// MediaStreamTracks Polyfilled
-var storePolyfillMediaStreamTracks = {};
-
-// Firefox MediaStream
-if (navigator.mozGetUserMedia) {
-
- /**
- * The polyfilled RTCPeerConnection class.
- * @class RTCPeerConnection
- * @since 0.10.5
- */
- polyfillRTCPeerConnection = function (stream) {
-
- /**
- * The MediaStream object id.
- * @attribute id
- * @type String
- * @readOnly
- * @for MediaStream
- * @since 0.10.6
- */
- stream.id = stream.id || (new Date()).getTime().toString();
-
- /**
- * The flag that indicates if a MediaStream object has ended.
- * @attribute ended
- * @type Boolean
- * @readOnly
- * @for MediaStream
- * @since 0.10.6
- */
- stream.ended = false;
-
- /**
- * Event triggered when MediaStream has ended streaming.
- * @event onended
- * @param {String} type The type of event: "ended"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onended = null;
-
- /**
- * Event triggered when MediaStream has added a new track.
- * @event onaddtrack
- * @param {String} type The type of event: "addtrack"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onaddtrack = null;
-
- /**
- * Event triggered when MediaStream has removed an existing track.
- * @event onremovetrack
- * @param {String} type The type of event: "removetrack"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onremovetrack = null;
-
- /**
- * Event triggered when a feature in the MediaStream is not supported
- * but used.
- * @event onunsupported
- * @param {String} feature The feature that is not supported. Eg. "addTrack"
.
- * @param {Object} error The error received natively.
- * @param {String} type The type of event: "unsupported"
.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.onunsupported = null;
-
-
- (function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- polyfillMediaStreamTrack( audioTracks[i] );
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- polyfillMediaStreamTrack( videoTracks[j] );
- }
- })();
-
- /**
- * Stops a MediaStream streaming.
- * @method polystop
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polystop = function () {
- if (stream instanceof LocalMediaStream) {
- stream.stop();
-
- } else {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- for (i = 0; i < audioTracks.length; i += 1) {
- audioTracks[i].polystop();
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- videoTracks[j].polystop();
- }
- }
- };
-
- /**
- * Adds a MediaStreamTrack to an object.
- * @method polyaddTrack
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polyaddTrack = function (track) {
- try {
- stream.addTrack(track);
- } catch (error) {
- // trigger that it has ended
- if (typeof stream.onunsupported === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'unsupported';
- eventPayload.error = error;
- eventPayload.feature = 'addTrack';
- stream.onunsupported(eventPayload);
- }
- }
- };
-
- /**
- * Gets a MediaStreamTrack from a MediaStreamTrack based on the object id provided.
- * @method polygetTrackById
- * @param {String} trackId The MediaStreamTrack object id.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetTrackById = function (trackId) {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- if (audioTracks[i].id === trackId) {
- return audioTracks[i];
- }
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- if (videoTracks[i].id === trackId) {
- return videoTracks[i];
- }
- }
-
- return null;
- };
-
- /**
- * Removes a MediaStreamTrack from an object.
- * @method polyremoveTrack
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polyremoveTrack = function (track) {
- try {
- stream.removeTrack(track);
- } catch (error) {
- // trigger that it has ended
- if (typeof stream.onunsupported === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'unsupported';
- eventPayload.error = error;
- eventPayload.feature = 'removeTrack';
- stream.onunsupported(eventPayload);
- }
- }
- };
-
- /**
- * Gets the list of audio MediaStreamTracks of a MediaStream.
- * @method polygetAudioTracks
- * @return {Array} Returns a list of the audio MediaStreamTracks
- * available for the MediaStream.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetAudioTracks = stream.getAudioTracks;
-
- /**
- * Gets the list of video MediaStreamTracks of a MediaStream.
- * @method polygetVideoTracks
- * @return {Array} Returns a list of the video MediaStreamTracks
- * available for the MediaStream.
- * @for MediaStream
- * @since 0.10.6
- */
- stream.polygetVideoTracks = stream.getVideoTracks;
-
- /**
- * Listens and waits to check if all MediaStreamTracks of a MediaStream
- * has ended. Once ended, this invokes the ended flag of the MediaStream.
- * This loops every second.
- * @method _polyOnTracksEndedListener
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- stream._polyOnTracksEndedListener = setInterval(function () {
- var i, j;
-
- var audios = stream.getAudioTracks();
- var videos = stream.getVideoTracks();
-
- var audioEnded = true;
- var videoEnded = true;
-
- // Check for all tracks if ended
- for (i = 0; i < audios.length; i += 1) {
- if (audios[i].ended !== true) {
- audioEnded = false;
- break;
- }
- }
-
- for (j = 0; j < videos.length; j += 1) {
- if (videos[j].ended !== true) {
- videoEnded = false;
- break;
- }
- }
-
- if (audioEnded && videoEnded) {
- clearInterval(stream._polyOnTracksEndedListener);
- stream.ended = true;
- }
- }, 1000);
-
- /**
- * Listens and waits to check if all MediaStream has ended.
- * This loops every second.
- * @method _polyOnEndedListener
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- if (stream instanceof LocalMediaStream) {
- stream._polyOnEndedListener = setInterval(function () {
- // If stream has flag ended because of media tracks being stopped
- if (stream.ended) {
- clearInterval(stream._polyOnEndedListener);
-
- // trigger that it has ended
- if (typeof stream.onended === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'ended';
- stream.onended(eventPayload);
- }
- }
-
- if (typeof stream.recordedTime === 'undefined') {
- stream.recordedTime = 0;
- }
-
- if (stream.recordedTime === stream.currentTime) {
- clearInterval(stream._polyOnEndedListener);
-
- stream.ended = true;
-
- // trigger that it has ended
- if (typeof stream.onended === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'ended';
- stream.onended(eventPayload);
- }
-
- } else {
- stream.recordedTime = stream.currentTime;
- }
- }, 1000);
-
- } else {
- /**
- * Stores the attached video element with the existing MediaStream
- * This loops every second.
- * - This only exists in Firefox browsers.
- * @attribute _polyOnEndedListenerObj
- * @type DOM
- * @private
- * @optional
- * @for MediaStream
- * @since 0.10.6
- */
- // Use a video to attach to check if stream has ended
- var video = document.createElement('video');
-
- video._polyOnEndedListener = setInterval(function () {
- // If stream has flag ended because of media tracks being stopped
- if (stream.ended) {
- clearInterval(video._polyOnEndedListener);
-
- // trigger that it has ended
- if (typeof stream.onended === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'ended';
- stream.onended(eventPayload);
- }
- }
-
- // Check if mozSrcObject is not empty
- if (typeof video.mozSrcObject === 'object' &&
- video.mozSrcObject !== null) {
-
- if (video.mozSrcObject.ended === true) {
- clearInterval(video._polyOnEndedListener);
-
- stream.ended = true;
-
- // trigger that it has ended
- if (typeof stream.onended === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'ended';
- stream.onended(eventPayload);
- }
- }
- }
- }, 1000);
-
- // Bind the video element to MediaStream object
- stream._polyOnEndedListenerObj = video;
-
- window.attachMediaStream(video, stream);
- }
- };
-
- window.getUserMedia = function (constraints, successCb, failureCb) {
-
- navigator.mozGetUserMedia(constraints, function (stream) {
- polyfillMediaStream(stream);
-
- successCb(stream);
-
- }, failureCb);
- };
-
- window.attachMediaStream = function (element, stream) {
- // If there's an element used for checking stream stop
- // for an instance remote MediaStream for firefox
- // reattachmediastream instead
- if (typeof stream._polyOnEndedListenerObj !== 'undefined' &&
- stream instanceof LocalMediaStream === false) {
- window.reattachMediaStream(element, bind._polyOnEndedListenerObj);
-
- // LocalMediaStream
- } else {
- console.log('Attaching media stream');
- element.mozSrcObject = stream;
- }
- };
-
-// Chrome / Opera MediaStream
-} else if (navigator.webkitGetUserMedia) {
-
- polyfillRTCPeerConnection = function (stream) {
-
- stream.onended = null;
-
- stream.onaddtrack = null;
-
- stream.onremovetrack = null;
-
- stream.onunsupported = null;
-
-
- (function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- polyfillMediaStreamTrack( audioTracks[i] );
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- polyfillMediaStreamTrack( videoTracks[j] );
- }
- })();
-
- stream.polystop = function () {
- stream.stop();
- };
-
- stream.polyaddTrack = function (track) {
- try {
- stream.addTrack(track);
- } catch (error) {
- // trigger that it has ended
- if (typeof stream.onunsupported === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'unsupported';
- eventPayload.error = error;
- eventPayload.feature = 'addTrack';
- stream.onunsupported(eventPayload);
- }
- }
- };
-
- stream.polygetTrackById = stream.getTrackById;
-
- stream.polyremoveTrack = function (track) {
- try {
- stream.removeTrack(track);
- } catch (error) {
- // trigger that it has ended
- if (typeof stream.onunsupported === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'unsupported';
- eventPayload.error = error;
- eventPayload.feature = 'removeTrack';
- stream.onunsupported(eventPayload);
- }
- }
- };
-
- stream.polygetAudioTracks = stream.getAudioTracks;
-
- stream.polygetVideoTracks = stream.getVideoTracks;
- };
-
- window.getUserMedia = function (constraints, successCb, failureCb) {
- navigator.webkitGetUserMedia(constraints, function (stream) {
-
- polyfillMediaStream(stream);
-
- successCb(stream);
- }, failureCb);
-
- };
-
-// Safari MediaStream
-} else {
-
- polyfillRTCPeerConnection = function (stream) {
-
- /**
- * Stores the store Id to store MediaStreamTrack functions.
- * - This only exists in Safari / IE (Plugin-enabled) browsers.
- * @attribute _polyStoreId
- * @type String
- * @optional
- * @private
- * @for MediaStream
- * @since 0.10.6
- */
- stream._polyStoreId = (new Date()).getTime().toString();
-
- stream.ended = typeof stream.ended === 'boolean' ? stream.ended : false;
-
- stream.onended = null;
-
- stream.onaddtrack = null;
-
- stream.onremovetrack = null;
-
- stream.onunsupported = null;
-
- (function () {
- var i, j;
-
- var audioTracks = stream.getAudioTracks();
- var videoTracks = stream.getVideoTracks();
-
- var outputAudioTracks = [];
- var outputVideoTracks = [];
-
- // Check for all tracks if ended
- for (i = 0; i < audioTracks.length; i += 1) {
- var track = polyfillMediaStreamTrack( audioTracks[i] );
- outputAudioTracks.push(track);
- }
-
- for (j = 0; j < videoTracks.length; j += 1) {
- var track = polyfillMediaStreamTrack( videoTracks[j] );
- outputVideoTracks.push(track);
- }
-
- storePolyfillMediaStreamTracks[stream._polyStoreId] = {
- audio: outputAudioTracks,
- video: outputVideoTracks
- };
- })();
-
- stream.polystop = function () {
- stream.stop();
-
- var i, j;
-
- var outputAudioTracks = storePolyfillMediaStreamTracks[stream._polyStoreId].audio;
- var outputVideoTracks = storePolyfillMediaStreamTracks[stream._polyStoreId].video;
-
- // Check for all tracks if ended
- for (i = 0; i < outputAudioTracks.length; i += 1) {
- var track = outputAudioTracks[i];
- track.ended = true;
-
- if (typeof track.onended === 'function') {
- var eventPayload = returnEventPayloadFn(track);
- eventPayload.type = 'ended';
-
- if (typeof track.onended === 'function') {
- track.onended(eventPayload);
- }
- }
- }
-
- for (j = 0; j < outputVideoTracks.length; j += 1) {
- var track = outputVideoTracks[j];
- track.ended = true;
-
- if (typeof track.onended === 'function') {
- var eventPayload = returnEventPayloadFn(track);
- eventPayload.type = 'ended';
-
- if (typeof track.onended === 'function') {
- track.onended(eventPayload);
- }
- }
- }
- };
-
- stream.polyaddTrack = function (track) {
- try {
- stream.addTrack(track);
- } catch (error) {
- // trigger that it has ended
- if (typeof stream.onunsupported === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'unsupported';
- eventPayload.error = error;
- eventPayload.feature = 'addTrack';
- stream.onunsupported(eventPayload);
- }
- }
- };
-
- stream.polygetTrackById = function (trackId) {
- var i, j;
-
- var outputAudioTracks = storePolyfillMediaStreamTracks[stream._polyStoreId].audio;
- var outputVideoTracks = storePolyfillMediaStreamTracks[stream._polyStoreId].video;
-
- // Check for all tracks if ended
- for (i = 0; i < outputAudioTracks.length; i += 1) {
- if (outputAudioTracks[i].id === trackId) {
- return outputAudioTracks[i];
- }
- }
-
- for (j = 0; j < outputVideoTracks.length; j += 1) {
- if (outputVideoTracks[j].id === trackId) {
- return outputVideoTracks[j];
- }
- }
-
- return null;
- };
-
- stream.polyremoveTrack = function (track) {
- try {
- stream.removeTrack(track);
- } catch (error) {
- // trigger that it has ended
- if (typeof stream.onunsupported === 'function') {
- var eventPayload = returnEventPayloadFn(stream);
- eventPayload.type = 'unsupported';
- eventPayload.error = error;
- eventPayload.feature = 'removeTrack';
- stream.onunsupported(eventPayload);
- }
- }
- };
-
- stream.polygetAudioTracks = function () {
- return storePolyfillMediaStreamTracks[stream._polyStoreId].audio;
- };
-
- stream.polygetVideoTracks = function () {
- return storePolyfillMediaStreamTracks[stream._polyStoreId].video;
- };
- };
-
- window.getUserMedia = function (constraints, successCb, failureCb) {
- navigator.getUserMedia(constraints, function(stream) {
-
- polyfillMediaStream(stream);
-
- successCb(stream);
- }, failureCb);
- };
-}
\ No newline at end of file
diff --git a/source/adapter.js b/source/adapter.js
index 84c7753..46b8868 100644
--- a/source/adapter.js
+++ b/source/adapter.js
@@ -56,7 +56,9 @@ AdapterJS.webRTCReady = function (callback) {
AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {};
// The object to store plugin information
-@@include('source/pluginInfo.js', {})
+/* jshint ignore:start */
+@Tem@include('pluginInfo.js', {})
+/* jshint ignore:end */
AdapterJS.WebRTCPlugin.TAGS = {
NONE : 'none',
@@ -143,16 +145,14 @@ AdapterJS.WebRTCPlugin.callWhenPluginReady = null;
// This function is the only private function that is not encapsulated to
// allow the plugin method to be called.
__TemWebRTCReady0 = function () {
- webrtcDetectedVersion = AdapterJS.WebRTCPlugin.plugin.version;
-
if (document.readyState === 'complete') {
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
AdapterJS.maybeThroughWebRTCReady();
} else {
- AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () {
+ var timer = setInterval(function () {
if (document.readyState === 'complete') {
// TODO: update comments, we wait for the document to be ready
- clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval);
+ clearInterval(timer);
AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
AdapterJS.maybeThroughWebRTCReady();
}
@@ -223,65 +223,62 @@ AdapterJS.isDefined = null;
// This sets:
// - webrtcDetectedBrowser: The browser agent name.
// - webrtcDetectedVersion: The browser version.
+// - webrtcMinimumVersion: The minimum browser version still supported by AJS.
// - webrtcDetectedType: The types of webRTC support.
// - 'moz': Mozilla implementation of webRTC.
// - 'webkit': WebKit implementation of webRTC.
// - 'plugin': Using the plugin implementation.
AdapterJS.parseWebrtcDetectedBrowser = function () {
- var hasMatch, checkMatch = navigator.userAgent.match(
- /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
- if (/trident/i.test(checkMatch[1])) {
- hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ var hasMatch = null;
+ if ((!!window.opr && !!opr.addons) ||
+ !!window.opera ||
+ navigator.userAgent.indexOf(' OPR/') >= 0) {
+ // Opera 8.0+
+ webrtcDetectedBrowser = 'opera';
+ webrtcDetectedType = 'webkit';
+ webrtcMinimumVersion = 26;
+ hasMatch = /OPR\/(\d+)/i.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ } else if (typeof InstallTrigger !== 'undefined') {
+ // Firefox 1.0+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = 'moz';
+ } else if (Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
+ // Safari
+ webrtcDetectedBrowser = 'safari';
+ webrtcDetectedType = 'plugin';
+ webrtcMinimumVersion = 7;
+ hasMatch = /version\/(\d+)/i.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ } else if (/*@cc_on!@*/false || !!document.documentMode) {
+ // Internet Explorer 6-11
webrtcDetectedBrowser = 'IE';
+ webrtcDetectedType = 'plugin';
+ webrtcMinimumVersion = 9;
+ hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
- } else if (checkMatch[1] === 'Chrome') {
- hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/);
- if (hasMatch !== null) {
- webrtcDetectedBrowser = 'opera';
- webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ if (!webrtcDetectedVersion) {
+ hasMatch = /\bMSIE[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
}
+ } else if (!!window.StyleMedia) {
+ // Edge 20+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = '';
+ } else if (!!window.chrome && !!window.chrome.webstore) {
+ // Chrome 1+
+ // Bowser and Version set in Google's adapter
+ webrtcDetectedType = 'webkit';
+ } else if ((webrtcDetectedBrowser === 'chrome'|| webrtcDetectedBrowser === 'opera') &&
+ !!window.CSS) {
+ // Blink engine detection
+ webrtcDetectedBrowser = 'blink';
+ // TODO: detected WebRTC version
}
- if (navigator.userAgent.indexOf('Safari')) {
- if (typeof InstallTrigger !== 'undefined') {
- webrtcDetectedBrowser = 'firefox';
- } else if (/*@cc_on!@*/ false || !!document.documentMode) {
- webrtcDetectedBrowser = 'IE';
- } else if (
- Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
- webrtcDetectedBrowser = 'safari';
- } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
- webrtcDetectedBrowser = 'opera';
- } else if (!!window.chrome) {
- webrtcDetectedBrowser = 'chrome';
- }
- }
- if (!webrtcDetectedBrowser) {
- webrtcDetectedVersion = checkMatch[1];
- }
- if (!webrtcDetectedVersion) {
- try {
- checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] :
- [navigator.appName, navigator.appVersion, '-?'];
- if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) {
- checkMatch.splice(1, 1, hasMatch[1]);
- }
- webrtcDetectedVersion = parseInt(checkMatch[1], 10);
- } catch (error) { }
- }
-};
-// To fix configuration as some browsers does not support
-// the 'urls' attribute.
-AdapterJS.maybeFixConfiguration = function (pcConfig) {
- if (pcConfig === null) {
- return;
- }
- for (var i = 0; i < pcConfig.iceServers.length; i++) {
- if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
- pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
- delete pcConfig.iceServers[i].urls;
- }
- }
+ window.webrtcDetectedBrowser = webrtcDetectedBrowser;
+ window.webrtcDetectedVersion = webrtcDetectedVersion;
+ window.webrtcMinimumVersion = webrtcMinimumVersion;
};
AdapterJS.addEvent = function(elem, evnt, func) {
@@ -319,7 +316,7 @@ AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNe
i.style.transition = 'all .5s ease-out';
}
document.body.appendChild(i);
- c = (i.contentWindow) ? i.contentWindow :
+ var c = (i.contentWindow) ? i.contentWindow :
(i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
c.document.open();
c.document.write(' 1) {
+ // RTCPeerConnection prototype from the new spec
+ return AdapterJS.WebRTCPlugin.plugin.PeerConnection(servers);
+ } else {
+ // RTCPeerConnection prototype from the old spec
+ var iceServers = null;
+ if (servers && Array.isArray(servers.iceServers)) {
+ iceServers = servers.iceServers;
+ for (var i = 0; i < iceServers.length; i++) {
+ if (iceServers[i].urls && !iceServers[i].url) {
+ iceServers[i].url = iceServers[i].urls;
+ }
+ iceServers[i].hasCredentials = AdapterJS.
+ isDefined(iceServers[i].username) &&
+ AdapterJS.isDefined(iceServers[i].credential);
+ }
+ }
+ var mandatory = (constraints && constraints.mandatory) ?
+ constraints.mandatory : null;
+ var optional = (constraints && constraints.optional) ?
+ constraints.optional : null;
+ return AdapterJS.WebRTCPlugin.plugin.
+ PeerConnection(AdapterJS.WebRTCPlugin.pageId,
+ iceServers, mandatory, optional);
+ }
};
MediaStreamTrack = {};
@@ -938,7 +988,7 @@ if ( navigator.mozGetUserMedia
});
};
- window.getUserMedia = function (constraints, successCallback, failureCallback) {
+ getUserMedia = function (constraints, successCallback, failureCallback) {
constraints.audio = constraints.audio || false;
constraints.video = constraints.video || false;
@@ -947,11 +997,16 @@ if ( navigator.mozGetUserMedia
getUserMedia(constraints, successCallback, failureCallback);
});
};
- window.navigator.getUserMedia = window.getUserMedia;
+ window.navigator.getUserMedia = getUserMedia;
// Defined mediaDevices when promises are available
- if ( !navigator.mediaDevices
- && typeof Promise !== 'undefined') {
+ if ( !navigator.mediaDevices &&
+ typeof Promise !== 'undefined') {
+ requestUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ getUserMedia(constraints, resolve, reject);
+ });
+ };
navigator.mediaDevices = {getUserMedia: requestUserMedia,
enumerateDevices: function() {
return new Promise(function(resolve) {
@@ -960,6 +1015,7 @@ if ( navigator.mozGetUserMedia
resolve(devices.map(function(device) {
return {label: device.label,
kind: kinds[device.kind],
+ id: device.id,
deviceId: device.id,
groupId: ''};
}));
@@ -1069,31 +1125,32 @@ if ( navigator.mozGetUserMedia
}
};
- AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
+ // Propagate attachMediaStream and gUM in window and AdapterJS
+ window.attachMediaStream = attachMediaStream;
+ window.reattachMediaStream = reattachMediaStream;
+ window.getUserMedia = getUserMedia;
+ AdapterJS.attachMediaStream = attachMediaStream;
+ AdapterJS.reattachMediaStream = reattachMediaStream;
+ AdapterJS.getUserMedia = getUserMedia;
+ AdapterJS.forwardEventHandlers = function (destElem, srcElem, prototype) {
properties = Object.getOwnPropertyNames( prototype );
-
- for(prop in properties) {
- propName = properties[prop];
-
- if (typeof(propName.slice) === 'function') {
- if (propName.slice(0,2) == 'on' && srcElem[propName] != null) {
- if (isIE) {
- destElem.attachEvent(propName,srcElem[propName]);
- } else {
- destElem.addEventListener(propName.slice(2), srcElem[propName], false)
- }
- } else {
- //TODO (http://jira.temasys.com.sg/browse/TWP-328) Forward non-event properties ?
+ for(var prop in properties) {
+ if (prop) {
+ propName = properties[prop];
+
+ if (typeof propName.slice === 'function' &&
+ propName.slice(0,2) === 'on' &&
+ typeof srcElem[propName] === 'function') {
+ AdapterJS.addEvent(destElem, propName.slice(2), srcElem[propName]);
}
}
}
-
- var subPrototype = Object.getPrototypeOf(prototype)
- if(subPrototype != null) {
+ var subPrototype = Object.getPrototypeOf(prototype);
+ if(!!subPrototype) {
AdapterJS.forwardEventHandlers(destElem, srcElem, subPrototype);
}
- }
+ };
RTCIceCandidate = function (candidate) {
if (!candidate.sdpMid) {
diff --git a/source/adapter.plugin.js b/source/adapter.plugin.js
deleted file mode 100644
index a9f89cd..0000000
--- a/source/adapter.plugin.js
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * The Temasys AdapterJS Plugin interface.
- * @class WebRTCPlugin
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {};
-
-/**
- * Contains the plugin information.
- * @property pluginInfo
- * @param {String} prefix The plugin prefix name.
- * @param {String} plugName The plugin object name.
- * @param {String} pluginId The plugin object id.
- * @param {String} type The plugin object type.
- * @param {String} onload The Javascript function to trigger when
- * the plugin object has loaded.
- * @param {String} portalLink The plugin website url.
- * @param {String} downloadLink The link to download new versions
- * of the plugin.
- * @param {String} companyName The plugin company name.
- * @type JSON
- * @private
- * @readOnly
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.pluginInfo = {
- prefix : 'Tem',
- plugName : 'TemWebRTCPlugin',
- pluginId : 'plugin0',
- type : 'application/x-temwebrtcplugin',
- onload : '__TemWebRTCReady0',
- portalLink : 'http://skylink.io/plugin/',
- downloadLink : (function () {
- // Placed on-top to return the url string directly instead
- if (!!navigator.platform.match(/^Mac/i)) {
- return 'http://bit.ly/1n77hco';
- } else if (!!navigator.platform.match(/^Win/i)) {
- return 'http://bit.ly/1kkS4FN';
- }
- return null;
- })(),
- companyName: 'Temasys'
-};
-
-/**
- * Contains the unique identifier of each opened page
- * @property pageId
- * @type String
- * @private
- * @readOnly
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.pageId = Math.random().toString(36).slice(2);
-
-/**
- * Use this whenever you want to call the plugin.
- * @property plugin
- * @type Object
- * @private
- * @readOnly
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.plugin = null;
-
-/**
- * Sets log level for the plugin once it is ready.
- * This is an asynchronous function that will run when the plugin is ready
- * @property setLogLevel
- * @type Function
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.setLogLevel = null; //function (logLevel) {};
-
-/**
- * Defines webrtc's JS interface according to the plugin's implementation.
- * Define plugin Browsers as WebRTC Interface.
- * @property defineWebRTCInterface
- * @type Function
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.defineWebRTCInterface = null; //function () { };
-
-/**
- * This function detects whether or not a plugin is installed.
- * we're running IE and do something. If not it is not supported.
- * @property isPluginInstalled
- * @type Function
- * @readOnly
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.isPluginInstalled = null; // function () { };
-
-/**
- * Lets adapter.js wait until the the document is ready before injecting the plugin.
- * @property pluginInjectionInterval
- * @type Object
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.pluginInjectionInterval = null;
-
-/**
- * Injects the HTML DOM object element into the page.
- * @property injectPlugin
- * @type Function
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.injectPlugin = null;
-
-/**
- * States of readiness that the plugin goes through when being injected and stated.
- * @property PLUGIN_STATES
- * @param {Integer} NONE No plugin use
- * @param {Integer} INITIALIZING Detected need for plugin
- * @param {Integer} INJECTING Injecting plugin
- * @param {Integer} INJECTED Plugin element injected but not usable yet
- * @param {Integer} READY Plugin ready to be used
- * @type JSON
- * @readOnly
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.PLUGIN_STATES = {
- NONE : 0,
- INITIALIZING : 1,
- INJECTING : 2,
- INJECTED: 3,
- READY: 4
-};
-
-/**
- * Current state of the plugin. You cannot use the plugin before this is
- * equal to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY.
- * @property pluginState
- * @type Integer
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE;
-
-/**
- * True is AdapterJS.onwebrtcready was already called, false otherwise.
- * Used to make sure AdapterJS.onwebrtcready is only called once.
- * @property onwebrtcreadyDone
- * @type Boolean
- * @readOnly
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.onwebrtcreadyDone = false;
-
-/**
- * Log levels for the plugin.
- * Log outputs are prefixed in some cases.
- * From the least verbose to the most verbose
- * @property PLUGIN_LOG_LEVELS
- * @param {String} NONE No log level.
- * @param {String} ERROR Errors originating from within the plugin.
- * @param {String} INFO Information reported by the plugin.
- * @param {String} VERBOSE Verbose mode.
- * @param {String} SENSITIVE Sensitive mode.
- * @type JSON
- * @readOnly
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS = {
- NONE : 'NONE',
- ERROR : 'ERROR',
- WARNING : 'WARNING',
- INFO: 'INFO',
- VERBOSE: 'VERBOSE',
- SENSITIVE: 'SENSITIVE'
-};
-
-/**
- * Does a waiting check before proceeding to load the plugin.
- * @property WaitForPluginReady
- * @type Function
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.WaitForPluginReady = null;
-
-/**
- * This method will use an interval to wait for the plugin to be ready.
- * @property callWhenPluginReady
- * @type Function
- * @private
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.callWhenPluginReady = null;
-
-/**
- * This function will be called if the plugin is needed (browser different
- * from Chrome or Firefox), but the plugin is not installed.
- * Override it according to your application logic.
- * @property pluginNeededButNotInstalledCb
- * @type Function
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = null;
-
-/**
- * !!!! WARNING: DO NOT OVERRIDE THIS FUNCTION. !!!
- * This function will be called when plugin is ready. It sends necessary
- * details to the plugin.
- * The function will wait for the document to be ready and the set the
- * plugin state to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,
- * indicating that it can start being requested.
- * This function is not in the IE/Safari condition brackets so that
- * TemPluginLoaded function might be called on Chrome/Firefox.
- * This function is the only private function that is not encapsulated to
- * allow the plugin method to be called.
- * @method pluginNeededButNotInstalledCb
- * @private
- * @global true
- * @for WebRTCPlugin
- * @since 0.10.5
- */
-window.__TemWebRTCReady0 = function () {
- if (document.readyState === 'complete') {
- AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
-
- AdapterJS.maybeThroughWebRTCReady();
- } else {
- AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () {
- if (document.readyState === 'complete') {
- // TODO: update comments, we wait for the document to be ready
- clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval);
- AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
-
- AdapterJS.maybeThroughWebRTCReady();
- }
- }, 100);
- }
-};
\ No newline at end of file
diff --git a/source/adapter.plugin.rtc.adapter.js b/source/adapter.plugin.rtc.adapter.js
deleted file mode 100644
index 159edef..0000000
--- a/source/adapter.plugin.rtc.adapter.js
+++ /dev/null
@@ -1,410 +0,0 @@
-if (!navigator.mozGetUserMedia && !navigator.webkitGetUserMedia) {
- // IE 9 is not offering an implementation of console.log until you open a console
- if (typeof console !== 'object' || typeof console.log !== 'function') {
- /* jshint -W020 */
- console = {} || console;
- // Implemented based on console specs from MDN
- // You may override these functions
- console.log = function (arg) {};
- console.info = function (arg) {};
- console.error = function (arg) {};
- console.dir = function (arg) {};
- console.exception = function (arg) {};
- console.trace = function (arg) {};
- console.warn = function (arg) {};
- console.count = function (arg) {};
- console.debug = function (arg) {};
- console.count = function (arg) {};
- console.time = function (arg) {};
- console.timeEnd = function (arg) {};
- console.group = function (arg) {};
- console.groupCollapsed = function (arg) {};
- console.groupEnd = function (arg) {};
- /* jshint +W020 */
- }
- webrtcDetectedType = 'plugin';
- webrtcDetectedDCSupport = 'plugin';
- AdapterJS.parseWebrtcDetectedBrowser();
- var isIE = webrtcDetectedBrowser === 'IE';
-
- /* jshint -W035 */
- AdapterJS.WebRTCPlugin.WaitForPluginReady = function() {
- while (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
- /* empty because it needs to prevent the function from running. */
- }
- };
- /* jshint +W035 */
-
- AdapterJS.WebRTCPlugin.callWhenPluginReady = function (callback) {
- if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
- // Call immediately if possible
- // Once the plugin is set, the code will always take this path
- callback();
- } else {
- // otherwise start a 100ms interval
- var checkPluginReadyState = setInterval(function () {
- if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
- clearInterval(checkPluginReadyState);
- callback();
- }
- }, 100);
- }
- };
-
- AdapterJS.WebRTCPlugin.setLogLevel = function(logLevel) {
- AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
- AdapterJS.WebRTCPlugin.plugin.setLogLevel(logLevel);
- });
- };
-
- AdapterJS.WebRTCPlugin.injectPlugin = function () {
- // only inject once the page is ready
- if (document.readyState !== 'complete') {
- return;
- }
-
- // Prevent multiple injections
- if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) {
- return;
- }
-
- AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING;
-
- if (webrtcDetectedBrowser === 'IE' && webrtcDetectedVersion <= 10) {
- var frag = document.createDocumentFragment();
- AdapterJS.WebRTCPlugin.plugin = document.createElement('div');
- AdapterJS.WebRTCPlugin.plugin.innerHTML = '';
- while (AdapterJS.WebRTCPlugin.plugin.firstChild) {
- frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);
- }
- document.body.appendChild(frag);
-
- // Need to re-fetch the plugin
- AdapterJS.WebRTCPlugin.plugin =
- document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId);
- } else {
- // Load Plugin
- AdapterJS.WebRTCPlugin.plugin = document.createElement('object');
- AdapterJS.WebRTCPlugin.plugin.id =
- AdapterJS.WebRTCPlugin.pluginInfo.pluginId;
- // IE will only start the plugin if it's ACTUALLY visible
- if (isIE) {
- AdapterJS.WebRTCPlugin.plugin.width = '1px';
- AdapterJS.WebRTCPlugin.plugin.height = '1px';
- }
- AdapterJS.WebRTCPlugin.plugin.type = AdapterJS.WebRTCPlugin.pluginInfo.type;
- AdapterJS.WebRTCPlugin.plugin.innerHTML = '' +
- '' +
- ' ' +
- (AdapterJS.options.getAllCams ? '':'') +
- '';
- document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);
- }
-
-
- AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED;
- };
-
- AdapterJS.WebRTCPlugin.isPluginInstalled =
- function (comName, plugName, installedCb, notInstalledCb) {
- if (!isIE) {
- var pluginArray = navigator.plugins;
- for (var i = 0; i < pluginArray.length; i++) {
- if (pluginArray[i].name.indexOf(plugName) >= 0) {
- installedCb();
- return;
- }
- }
- notInstalledCb();
- } else {
- try {
- var axo = new ActiveXObject(comName + '.' + plugName);
- } catch (e) {
- notInstalledCb();
- return;
- }
- installedCb();
- }
- };
-
- AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
- AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING;
-
- AdapterJS.isDefined = function (variable) {
- return variable !== null && variable !== undefined;
- };
-
- window.createIceServer = function (url, username, password) {
- var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
- iceServer = {
- 'url' : url,
- 'hasCredentials' : false
- };
- } else if (url_parts[0].indexOf('turn') === 0) {
- iceServer = {
- 'url' : url,
- 'hasCredentials' : true,
- 'credential' : password,
- 'username' : username
- };
- }
- return iceServer;
- };
-
- window.createIceServers = function (urls, username, password) {
- var iceServers = [];
- for (var i = 0; i < urls.length; ++i) {
- iceServers.push(createIceServer(urls[i], username, password));
- }
- return iceServers;
- };
-
- window.RTCSessionDescription = function (info) {
- AdapterJS.WebRTCPlugin.WaitForPluginReady();
- return AdapterJS.WebRTCPlugin.plugin.
- ConstructSessionDescription(info.type, info.sdp);
- };
-
- RTCPeerConnection = function (servers, constraints) {
- var iceServers = null;
- if (servers) {
- iceServers = servers.iceServers;
- for (var i = 0; i < iceServers.length; i++) {
- if (iceServers[i].urls && !iceServers[i].url) {
- iceServers[i].url = iceServers[i].urls;
- }
- iceServers[i].hasCredentials = AdapterJS.
- isDefined(iceServers[i].username) &&
- AdapterJS.isDefined(iceServers[i].credential);
- }
- }
- var mandatory = (constraints && constraints.mandatory) ?
- constraints.mandatory : null;
- var optional = (constraints && constraints.optional) ?
- constraints.optional : null;
-
- AdapterJS.WebRTCPlugin.WaitForPluginReady();
- return AdapterJS.WebRTCPlugin.plugin.
- PeerConnection(AdapterJS.WebRTCPlugin.pageId,
- iceServers, mandatory, optional);
- };
-
- window.MediaStreamTrack = {};
- MediaStreamTrack.getSources = function (callback) {
- AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
- AdapterJS.WebRTCPlugin.plugin.GetSources(callback);
- });
- };
-
- getUserMedia = function (constraints, successCallback, failureCallback) {
- if (!constraints.audio) {
- constraints.audio = false;
- }
-
- AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
- AdapterJS.WebRTCPlugin.plugin.
- getUserMedia(constraints, successCallback, failureCallback);
- });
- };
- navigator.getUserMedia = getUserMedia;
-
- attachMediaStream = function (element, stream) {
- stream.enableSoundTracks(true);
- if (element.nodeName.toLowerCase() !== 'audio') {
- var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id;
- if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) {
- var frag = document.createDocumentFragment();
- var temp = document.createElement('div');
- var classHTML = (element.className) ? 'class="' + element.className + '" ' : '';
- temp.innerHTML = '';
- while (temp.firstChild) {
- frag.appendChild(temp.firstChild);
- }
- var rectObject = element.getBoundingClientRect();
- element.parentNode.insertBefore(frag, element);
- frag = document.getElementById(elementId);
- frag.width = rectObject.width + 'px';
- frag.height = rectObject.height + 'px';
- element.parentNode.removeChild(element);
- } else {
- var children = element.children;
- for (var i = 0; i !== children.length; ++i) {
- if (children[i].name === 'streamId') {
- children[i].value = stream.id;
- break;
- }
- }
- element.setStreamId(stream.id);
- }
- var newElement = document.getElementById(elementId);
- newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
- if (isIE) { // on IE the event needs to be plugged manually
- newElement.attachEvent('onplaying', newElement.onplaying);
- newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
- newElement._TemOnClick = function (id) {
- var arg = {
- srcElement : document.getElementById(id)
- };
- newElement.onclick(arg);
- };
- }
- return newElement;
- } else {
- return element;
- }
- };
-
- reattachMediaStream = function (to, from) {
- var stream = null;
- var children = from.children;
- for (var i = 0; i !== children.length; ++i) {
- if (children[i].name === 'streamId') {
- AdapterJS.WebRTCPlugin.WaitForPluginReady();
- stream = AdapterJS.WebRTCPlugin.plugin
- .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, children[i].value);
- break;
- }
- }
- if (stream !== null) {
- return attachMediaStream(to, stream);
- } else {
- console.log('Could not find the stream associated with this element');
- }
- };
-
- window.RTCIceCandidate = function (candidate) {
- if (!candidate.sdpMid) {
- candidate.sdpMid = '';
- }
-
- AdapterJS.WebRTCPlugin.WaitForPluginReady();
- return AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(
- candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate
- );
- };
-
- // inject plugin
- AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.injectPlugin);
- AdapterJS.WebRTCPlugin.injectPlugin();
- };
-
- AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb ||
- function() {
- AdapterJS.addEvent(document,
- 'readystatechange',
- AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv);
- AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv();
- };
-
- AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv = function () {
- if (AdapterJS.options.hidePluginInstallPrompt) {
- return;
- }
-
- var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;
- if(downloadLink) { // if download link
- var popupString;
- if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { // is portal link
- popupString = 'This website requires you to install the ' +
- ' ' + AdapterJS.WebRTCPlugin.pluginInfo.companyName +
- ' WebRTC Plugin' +
- ' to work on this browser.';
- } else { // no portal link, just print a generic explanation
- popupString = 'This website requires you to install a WebRTC-enabling plugin ' +
- 'to work on this browser.';
- }
-
- AdapterJS.WebRTCPlugin.renderNotificationBar(popupString, 'Install Now', downloadLink);
- } else { // no download link, just print a generic explanation
- AdapterJS.WebRTCPlugin.renderNotificationBar('Your browser does not support WebRTC.');
- }
- };
-
- AdapterJS.WebRTCPlugin.renderNotificationBar = function (text, buttonText, buttonLink) {
- // only inject once the page is ready
- if (document.readyState !== 'complete') {
- return;
- }
-
- var w = window;
- var i = document.createElement('iframe');
- i.name = 'adapterjs-alert';
- i.style.position = 'fixed';
- i.style.top = '-41px';
- i.style.left = 0;
- i.style.right = 0;
- i.style.width = '100%';
- i.style.height = '40px';
- i.style.backgroundColor = '#ffffe1';
- i.style.border = 'none';
- i.style.borderBottom = '1px solid #888888';
- i.style.zIndex = '9999999';
- if(typeof i.style.webkitTransition === 'string') {
- i.style.webkitTransition = 'all .5s ease-out';
- } else if(typeof i.style.transition === 'string') {
- i.style.transition = 'all .5s ease-out';
- }
- document.body.appendChild(i);
- c = (i.contentWindow) ? i.contentWindow :
- (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
- c.document.open();
- c.document.write('' + text + '');
- if(buttonText && buttonLink) {
- c.document.write('');
- c.document.close();
- AdapterJS.addEvent(c.document.getElementById('okay'), 'click', function(e) {
- window.open(buttonLink, '_top');
- e.preventDefault();
- try {
- event.cancelBubble = true;
- } catch(error) { }
- });
- }
- else {
- c.document.close();
- }
- AdapterJS.addEvent(c.document, 'click', function() {
- w.document.body.removeChild(i);
- });
- setTimeout(function() {
- if(typeof i.style.webkitTransform === 'string') {
- i.style.webkitTransform = 'translateY(40px)';
- } else if(typeof i.style.transform === 'string') {
- i.style.transform = 'translateY(40px)';
- } else {
- i.style.top = '0px';
- }
- }, 300);
- };
- // Try to detect the plugin and act accordingly
- AdapterJS.WebRTCPlugin.isPluginInstalled(
- AdapterJS.WebRTCPlugin.pluginInfo.prefix,
- AdapterJS.WebRTCPlugin.pluginInfo.plugName,
- AdapterJS.WebRTCPlugin.defineWebRTCInterface,
- AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
-}
diff --git a/source/adapter.screenshare.js b/source/adapter.screenshare.js
index 0f80647..4084d79 100644
--- a/source/adapter.screenshare.js
+++ b/source/adapter.screenshare.js
@@ -13,10 +13,14 @@
};
var clone = function(obj) {
- if (null == obj || "object" != typeof obj) return obj;
+ if (null === obj || 'object' !== typeof obj) {
+ return obj;
+ }
var copy = obj.constructor();
for (var attr in obj) {
- if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
+ if (obj.hasOwnProperty(attr)) {
+ copy[attr] = obj[attr];
+ }
}
return copy;
};
@@ -47,11 +51,10 @@
clearInterval(checkIfReady);
baseGetUserMedia(updatedConstraints, successCb, function (error) {
- if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') {
+ if (['PermissionDeniedError', 'SecurityError'].indexOf(error.name) > -1 && window.parent.location.protocol === 'https:') {
AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,
AdapterJS.TEXT.EXTENSION.BUTTON_FF,
- 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true);
- //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname;
+ 'https://addons.mozilla.org/en-US/firefox/addon/skylink-webrtc-tools/', true, true);
} else {
failureCb(error);
}
@@ -64,7 +67,12 @@
}
};
- getUserMedia = navigator.getUserMedia;
+ AdapterJS.getUserMedia = window.getUserMedia = navigator.getUserMedia;
+ navigator.mediaDevices.getUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ window.getUserMedia(constraints, resolve, reject);
+ });
+ };
} else if (window.navigator.webkitGetUserMedia) {
baseGetUserMedia = window.navigator.getUserMedia;
@@ -144,7 +152,12 @@
}
};
- getUserMedia = navigator.getUserMedia;
+ AdapterJS.getUserMedia = window.getUserMedia = navigator.getUserMedia;
+ navigator.mediaDevices.getUserMedia = function(constraints) {
+ return new Promise(function(resolve, reject) {
+ window.getUserMedia(constraints, resolve, reject);
+ });
+ };
} else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
// nothing here because edge does not support screensharing
@@ -181,7 +194,9 @@
}
};
- getUserMedia = window.navigator.getUserMedia;
+ AdapterJS.getUserMedia = getUserMedia =
+ window.getUserMedia = navigator.getUserMedia;
+ navigator.mediaDevices.getUserMedia = requestUserMedia;
}
// For chrome, use an iframe to load the screensharing extension
@@ -199,7 +214,7 @@
(document.body || document.documentElement).appendChild(iframe);
- var postFrameMessage = function (object) {
+ var postFrameMessage = function (object) { // jshint ignore:line
object = object || {};
if (!iframe.isLoaded) {
@@ -214,4 +229,4 @@
} else if (window.webrtcDetectedBrowser === 'opera') {
console.warn('Opera does not support screensharing feature in getUserMedia');
}
-})();
\ No newline at end of file
+})();
diff --git a/source/adapter.utils.js b/source/adapter.utils.js
deleted file mode 100644
index 5f7e386..0000000
--- a/source/adapter.utils.js
+++ /dev/null
@@ -1,341 +0,0 @@
-/**
- * The Temasys AdapterJS interface.
- * @class AdapterJS
- * @since 0.10.5
- */
-window.AdapterJS = typeof window.AdapterJS !== 'undefined' ? window.AdapterJS : {};
-
-/**
- * Contains the options of the Temasys Plugin.
- * @property options
- * @param getAllCams {Boolean} Option to get virtual cameras.
- * Override this value here.
- * @param hidePluginInstallPrompt {Boolean} Option to prevent
- * the install prompt when the plugin in not yet installed.
- * Override this value here.
- * @type JSON
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.options = {
- getAllCams: false,
- hidePluginInstallPrompt: false
-};
-
-/**
- * The current version of the Temasys AdapterJS.
- * @property VERSION.
- * @type String
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.VERSION = '@@version';
-
-/**
- * The event function that will be called when the WebRTC API is
- * ready to be used in cross-browsers.
- * If you decide not to override use this synchronisation, it may result in
- * an extensive CPU usage on the plugin start (once per tab loaded).
- * Override this function to synchronise the start of your application
- * with the WebRTC API being ready.
- * @property onwebrtcready
- * @return {Boolean} Returns a boolean in the event function that
- * indicates if the WebRTC plugin is being used, false otherwise.
- * @type Function
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.onwebrtcready = AdapterJS.onwebrtcready || function (isUsingPlugin) {};
-
-/**
- * Checks if maybe WebRTC is already ready.
- * @property maybeThroughWebRTCReady
- * @type Function
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.maybeThroughWebRTCReady = function () {
- if (!AdapterJS.onwebrtcreadyDone) {
- AdapterJS.onwebrtcreadyDone = true;
-
- if (typeof AdapterJS.onwebrtcready === 'function') {
- AdapterJS.onwebrtcready(AdapterJS.WebRTCPlugin.plugin !== null);
- }
- }
-};
-
-/**
- * The result of ICE connection states.
- * @property _iceConnectionStates
- * @param {String} starting ICE connection is starting.
- * @param {String} checking ICE connection is checking.
- * @param {String} connected ICE connection is connected.
- * @param {String} completed ICE connection is connected.
- * @param {String} done ICE connection has been completed.
- * @param {String} disconnected ICE connection has been disconnected.
- * @param {String} failed ICE connection has failed.
- * @param {String} closed ICE connection is closed.
- * @type JSON
- * @readOnly
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS._iceConnectionStates = {
- starting : 'starting',
- checking : 'checking',
- connected : 'connected',
- completed : 'connected',
- done : 'completed',
- disconnected : 'disconnected',
- failed : 'failed',
- closed : 'closed'
-};
-
-/**
- * The IceConnection states that has been fired for each peer.
- * @property _iceConnectionFiredStates
- * @param {Array} (#peerId) The ICE connection fired states for this peerId.
- * @type Array
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS._iceConnectionFiredStates = [];
-
-/**
- * Check if WebRTC Interface is defined.
- * @property isDefined
- * @type Boolean
- * @readOnly
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.isDefined = null;
-
-/**
- * This function helps to retrieve the webrtc detected browser information.
- * This sets:
- * webrtcDetectedBrowser: The browser agent name.
- * - webrtcDetectedVersion: The browser version.
- * - webrtcDetectedType: The types of webRTC support.
- * - 'moz': Mozilla implementation of webRTC.
- * - 'webkit': WebKit implementation of webRTC.
- * - 'plugin': Using the plugin implementation.
- * @property parseWebrtcDetectedBrowser
- * @type Function
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.parseWebrtcDetectedBrowser = function () {
- var hasMatch, checkMatch = navigator.userAgent.match(
- /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
- if (/trident/i.test(checkMatch[1])) {
- hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
- webrtcDetectedBrowser = 'IE';
- webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
- } else if (checkMatch[1] === 'Chrome') {
- hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/);
- if (hasMatch !== null) {
- webrtcDetectedBrowser = 'opera';
- webrtcDetectedVersion = parseInt(hasMatch[1], 10);
- }
- }
- if (navigator.userAgent.indexOf('Safari')) {
- if (typeof InstallTrigger !== 'undefined') {
- webrtcDetectedBrowser = 'firefox';
- } else if (/*@cc_on!@*/ false || !!document.documentMode) {
- webrtcDetectedBrowser = 'IE';
- } else if (
- Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
- webrtcDetectedBrowser = 'safari';
- } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
- webrtcDetectedBrowser = 'opera';
- } else if (!!window.chrome) {
- webrtcDetectedBrowser = 'chrome';
- }
- }
- if (!webrtcDetectedBrowser) {
- webrtcDetectedVersion = checkMatch[1];
- }
- if (!webrtcDetectedVersion) {
- try {
- checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] :
- [navigator.appName, navigator.appVersion, '-?'];
- if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) {
- checkMatch.splice(1, 1, hasMatch[1]);
- }
- webrtcDetectedVersion = parseInt(checkMatch[1], 10);
- } catch (error) { }
- }
-};
-
-/**
- * To fix configuration as some browsers does not support
- * the 'urls' attribute.
- * @property maybeFixConfiguration
- * @type Function
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.maybeFixConfiguration = function (pcConfig) {
- if (pcConfig === null) {
- return;
- }
- for (var i = 0; i < pcConfig.iceServers.length; i++) {
- if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
- pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
- delete pcConfig.iceServers[i].urls;
- }
- }
-};
-
-/**
- * Adds an event listener for Temasys plugin objects.
- * @property addEvent
- * @type Function
- * @private
- * @for AdapterJS
- * @since 0.10.5
- */
-AdapterJS.addEvent = function(elem, evnt, func) {
- if (elem.addEventListener) { // W3C DOM
- elem.addEventListener(evnt, func, false);
- } else if (elem.attachEvent) {// OLD IE DOM
- elem.attachEvent('on' + evnt, func);
- } else { // No much to do
- elem[evnt] = func;
- }
-};
-
-/**
- * Detected webrtc implementation. Types are:
- * - 'moz': Mozilla implementation of webRTC.
- * - 'webkit': WebKit implementation of webRTC.
- * - 'plugin': Using the plugin implementation.
- * @property webrtcDetectedType
- * @type String
- * @readOnly
- * @for AdapterJS
- * @since 0.10.5
- */
-window.webrtcDetectedType = null;
-
-/**
- * Detected webrtc datachannel support. Types are:
- * - 'SCTP': SCTP datachannel support.
- * - 'RTP': RTP datachannel support.
- * @property webrtcDetectedType
- * @type String
- * @readOnly
- * @for AdapterJS
- * @since 0.10.5
- */
-window.webrtcDetectedDCSupport = null;
-
-/**
- * Set the settings for creating DataChannels, MediaStream for
- * Cross-browser compability. This is only for SCTP based support browsers.
- * @method checkMediaDataChannelSettings
- * @param {String} peerBrowserAgent The browser agent name.
- * @param {Integer} peerBrowserVersion The browser agent version.
- * @param {Function} callback The callback that gets fired once the function is
- * completed.
- * @param {JSON} constraints The RTCOfferOptions.
- * @return {Boolean & JSON} (beOfferer, updatedConstraints)
- * Returns a flag beOfferer if the peer should be the offer and also the updated unified
- * RTCOfferOptions constraints.
- * @readOnly
- * @global true
- * @for AdapterJS
- * @since 0.10.5
- */
-window.checkMediaDataChannelSettings = function (peerBrowserAgent, peerBrowserVersion, callback, constraints) {
- if (typeof callback !== 'function') {
- return;
- }
- var beOfferer = true;
- var isLocalFirefox = webrtcDetectedBrowser === 'firefox';
- // Nightly version does not require MozDontOfferDataChannel for interop
- var isLocalFirefoxInterop = webrtcDetectedType === 'moz' && webrtcDetectedVersion > 30;
- var isPeerFirefox = peerBrowserAgent === 'firefox';
- var isPeerFirefoxInterop = peerBrowserAgent === 'firefox' &&
- ((peerBrowserVersion) ? (peerBrowserVersion > 30) : false);
-
- // Resends an updated version of constraints for MozDataChannel to work
- // If other userAgent is firefox and user is firefox, remove MozDataChannel
- if ((isLocalFirefox && isPeerFirefox) || (isLocalFirefoxInterop)) {
- try {
- delete constraints.mandatory.MozDontOfferDataChannel;
- } catch (error) {
- console.error('Failed deleting MozDontOfferDataChannel');
- console.error(error);
- }
- } else if ((isLocalFirefox && !isPeerFirefox)) {
- constraints.mandatory.MozDontOfferDataChannel = true;
- }
- if (!isLocalFirefox) {
- // temporary measure to remove Moz* constraints in non Firefox browsers
- for (var prop in constraints.mandatory) {
- if (constraints.mandatory.hasOwnProperty(prop)) {
- if (prop.indexOf('Moz') !== -1) {
- delete constraints.mandatory[prop];
- }
- }
- }
- }
- // Firefox (not interopable) cannot offer DataChannel as it will cause problems to the
- // interopability of the media stream
- if (isLocalFirefox && !isPeerFirefox && !isLocalFirefoxInterop) {
- beOfferer = false;
- }
- callback(beOfferer, constraints);
-};
-
-/**
- * Handles the differences for all browsers ice connection state output.
- * Tested outcomes are:
- * - Chrome (offerer) : 'checking' > 'completed' > 'completed'
- * - Chrome (answerer) : 'checking' > 'connected'
- * - Firefox (offerer) : 'checking' > 'connected'
- * - Firefox (answerer): 'checking' > 'connected'
- * @method checkIceConnectionState
- * @param {String} peerId The peerId of the peer to check.
- * @param {String} iceConnectionState The peer's current ICE connection state.
- * @param {String} callback The callback that returns the updated connected state.
- * @return {String} (state)
- * Returns the updated ICE connection state.
- * @for AdapterJS
- * @since 0.10.5
- */
-window.checkIceConnectionState = function (peerId, iceConnectionState, callback) {
- if (typeof callback !== 'function') {
- console.warn('No callback specified in checkIceConnectionState. Aborted.');
- return;
- }
- peerId = (peerId) ? peerId : 'peer';
-
- if (!AdapterJS._iceConnectionFiredStates[peerId] ||
- iceConnectionState === AdapterJS._iceConnectionStates.disconnected ||
- iceConnectionState === AdapterJS._iceConnectionStates.failed ||
- iceConnectionState === AdapterJS._iceConnectionStates.closed) {
- AdapterJS._iceConnectionFiredStates[peerId] = [];
- }
- iceConnectionState = AdapterJS._iceConnectionStates[iceConnectionState];
- if (AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState) < 0) {
- AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState);
- if (iceConnectionState === AdapterJS._iceConnectionStates.connected) {
- setTimeout(function () {
- AdapterJS._iceConnectionFiredStates[peerId]
- .push(AdapterJS._iceConnectionStates.done);
- callback(AdapterJS._iceConnectionStates.done);
- }, 1000);
- }
- callback(iceConnectionState);
- }
- return;
-};
\ No newline at end of file
diff --git a/tests/globals.js b/tests/globals.js
index d9d7ee4..22176c0 100644
--- a/tests/globals.js
+++ b/tests/globals.js
@@ -94,10 +94,8 @@ var printJSON = function (obj, spaces) {
if (typeof val === 'object') {
outputStr += printJSON(val, spaces + 2);
-
} else if (typeof val === 'string') {
outputStr += '"' + val + '"';
-
} else {
outputStr += val;
}
@@ -133,12 +131,13 @@ var printJSON = function (obj, spaces) {
};
var isArrayEqual = function(array1, array2) {
- if (array1.length != array2.length)
- return false
+ if (array1.length !== array2.length) {
+ return false;
+ }
return array1.every(function(element, index) {
return element === array2[index];
- })
+ });
};
// Connect the RTCPeerConnection object
@@ -178,6 +177,17 @@ var connect = function (peer1, peer2, offerConstraints) {
}
};
+ // create answer
+ var peer2AnswerCb = function (a) {
+ answer = a;
+ peer2.setLocalDescription(answer, function() {}, function (error) {
+ throw error;
+ });
+ peer1.setRemoteDescription(answer, function() {}, function (error) {
+ throw error;
+ });
+ };
+
// create offer
var peer1OfferCb = function (o) {
offer = o;
@@ -192,19 +202,12 @@ var connect = function (peer1, peer2, offerConstraints) {
});
};
- // create answer
- var peer2AnswerCb = function (a) {
- answer = a;
- peer2.setLocalDescription(answer, function() {}, function (error) {
- throw error;
- });
- peer1.setRemoteDescription(answer, function() {}, function (error) {
- throw error;
- });
- };
-
// start
peer1.createOffer(peer1OfferCb, function (error) {
throw error;
}, offerConstraints);
-};
\ No newline at end of file
+};
+
+// Plugin functions have different types depending on the interface (NPAPI VS ActiveX)
+FUNCTION_TYPE = webrtcDetectedBrowser === 'IE' ? 'object' : 'function';
+
diff --git a/tests/karma.conf.js b/tests/karma.conf.js
index 7263fe8..115d890 100644
--- a/tests/karma.conf.js
+++ b/tests/karma.conf.js
@@ -21,6 +21,7 @@ module.exports = function(config) {
// tests
{pattern: 'unit/*.spec.js', included: false},
+ {pattern: 'unit/*.no-spec.js', included: false},
],
@@ -94,5 +95,5 @@ module.exports = function(config) {
'karma-ie-launcher',
'karma-opera-launcher',
'karma-requirejs']
- })
-}
+ });
+};
diff --git a/tests/unit/MediaStream.prop.spec.js b/tests/unit/MediaStream.prop.spec.js
index 06d4681..2b4c995 100644
--- a/tests/unit/MediaStream.prop.spec.js
+++ b/tests/unit/MediaStream.prop.spec.js
@@ -78,7 +78,7 @@ describe('MediaStream | Properties', function() {
it('MediaStream.ended :: boolean', function (done) {
this.timeout(testItemTimeout);
- assert.typeOf(stream.ended, 'boolean')
+ assert.typeOf(stream.ended, 'boolean');
done();
});
diff --git a/tests/unit/MediaStreamTrack.prop.spec.js b/tests/unit/MediaStreamTrack.prop.spec.js
index 4ee90f3..d171a0b 100644
--- a/tests/unit/MediaStreamTrack.prop.spec.js
+++ b/tests/unit/MediaStreamTrack.prop.spec.js
@@ -47,7 +47,6 @@ describe('MediaStreamTrack | Properties', function() {
}, function (error) {
throw error;
- done();
});
});
});
@@ -67,23 +66,7 @@ describe('MediaStreamTrack | Properties', function() {
assert.typeOf(source1.facing, 'string');
assert.typeOf(source1.label, 'string');
- var constraints = {};
-
- constraints[source1.kind] = {
- optional: [{ sourceId: source1.id }]
- };
-
- window.navigator.getUserMedia(constraints, function (checkStream) {
-
- var checkTrack = source1.kind === 'audio' ? checkStream.getAudioTracks()[0] :
- checkStream.getVideoTracks()[0];
-
- expect(checkTrack.id).to.equal(source1.id);
- done();
-
- }, function (error) {
- throw error;
- });
+ done();
});
});
@@ -96,7 +79,7 @@ describe('MediaStreamTrack | Properties', function() {
expect(audioTrack.id).to.not.equal(videoTrack.id);
});
- it('MediaStreamTrack.ended :: boolean', function () {
+ it.skip('MediaStreamTrack.ended :: boolean', function () {
this.timeout(testItemTimeout);
assert.typeOf(audioTrack.ended, 'boolean');
diff --git a/tests/unit/Plugin.ScreenSaver.behaviour.spec.js b/tests/unit/Plugin.ScreenSaver.behaviour.spec.js
index c2a82b3..aae6c0d 100644
--- a/tests/unit/Plugin.ScreenSaver.behaviour.spec.js
+++ b/tests/unit/Plugin.ScreenSaver.behaviour.spec.js
@@ -58,7 +58,7 @@ if(webrtcDetectedBrowser === 'safari' || webrtcDetectedBrowser === 'IE') {
document.body.removeChild(video);
stream = null;
- if(interval != null) {
+ if(interval !== null) {
clearInterval(interval);
interval = null;
}
diff --git a/tests/unit/Plugin.features.no-spec.js b/tests/unit/Plugin.features.no-spec.js
new file mode 100644
index 0000000..ebe6796
--- /dev/null
+++ b/tests/unit/Plugin.features.no-spec.js
@@ -0,0 +1,83 @@
+var expect = chai.expect;
+var assert = chai.assert;
+var should = chai.should;
+
+// Test timeouts
+var testTimeout = 120000;
+
+// Get User Media timeout
+var gUMTimeout = 15000;
+
+// Test item timeout
+var testItemTimeout = 5000;
+
+// !!! THIS TEST ONLY APPLIES FOR PLUGIN-BASED BROWSERS !!!
+if(webrtcDetectedBrowser === 'safari' || webrtcDetectedBrowser === 'IE') {
+
+ describe('Plugin Features | Out of spec', function() {
+ this.timeout(testTimeout);
+
+ /* Attributes */
+ var video = null;
+ var stream = null;
+
+ /* WebRTC Object should be initialized in Safari/IE Plugin */
+ before(function (done) {
+ this.timeout(gUMTimeout);
+
+ AdapterJS.webRTCReady(function() {
+ done();
+ });
+ });
+
+ beforeEach(function (done) {
+ this.timeout(gUMTimeout);
+
+ window.navigator.getUserMedia({
+ audio: true,
+ video: true
+
+ }, function (data) {
+ stream = data;
+ video = document.createElement('video');
+ document.body.appendChild(video);
+ done();
+
+ }, function (error) {
+ throw error;
+ });
+
+ });
+
+ afterEach(function () {
+ document.body.removeChild(video);
+ stream = null;
+ });
+
+ it('getFrame', function(done) {
+ this.timeout(testTimeout);
+ video.onplay = function(e) {
+ assert.isNotNull(video.getFrame);
+ // assert.typeOf(video.getFrame, 'function');
+
+ var canvas = document.createElement('canvas');
+ document.body.appendChild(canvas);
+
+ var base64 = video.getFrame();
+ assert.isString(base64);
+ expect(base64).to.have.length.above(1000);
+
+ var img = new Image();
+ img.onload = function () {
+ canvas.getContext('2d').
+ drawImage(img, 0, 0, canvas.width, canvas.height);
+ done();
+ };
+ img.setAttribute('src', 'data:image/png;base64,' + base64);
+
+ };
+ video = attachMediaStream(video, stream);
+ });
+
+ }); // describe('Plugin Object | Stability'
+} // if(webrtcDetectedBrowser === 'safari' || webrtcDetectedBrowser === 'IE')
diff --git a/tests/unit/Plugin.object.stability.spec.js b/tests/unit/Plugin.object.stability.spec.js
index 18291a7..1b30f79 100644
--- a/tests/unit/Plugin.object.stability.spec.js
+++ b/tests/unit/Plugin.object.stability.spec.js
@@ -61,23 +61,23 @@ if(webrtcDetectedBrowser === 'safari' || webrtcDetectedBrowser === 'IE') {
this.timeout(testItemTimeout);
var popCount = 0;
- var timeout;
+ var t;
var replaceVideoElement = function() {
- clearTimeout(timeout);
+ clearTimeout(t);
document.body.removeChild(video);
video = document.createElement('video');
document.body.appendChild(video);
video.onplay = replaceVideoElement;
video = attachMediaStream(video, stream);
- timeout = setTimeout(replaceVideoElement, 500);
+ t = setTimeout(replaceVideoElement, 500);
expect(video.valid).to.equal(true);
if (++popCount >= POP_REQUESTS) {
- clearTimeout(timeout);
+ clearTimeout(t);
done();
}
- }
+ };
replaceVideoElement();
});
diff --git a/tests/unit/RTCDTMFSender.event.spec.js b/tests/unit/RTCDTMFSender.event.spec.js
new file mode 100644
index 0000000..ea0e326
--- /dev/null
+++ b/tests/unit/RTCDTMFSender.event.spec.js
@@ -0,0 +1,90 @@
+var expect = chai.expect;
+var assert = chai.assert;
+var should = chai.should;
+
+// Test timeouts
+var testTimeout = 3500;
+
+// Get User Media timeout
+var gUMTimeout = 5000;
+
+// Test item timeout
+var testItemTimeout = 2000;
+
+describe('RTCDTMFSender | event', function() {
+ this.timeout(testTimeout);
+
+ var pc1 = null;
+ var pc2 = null;
+ var audioTrack = null;
+ var dtmfSender = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ /* WebRTC Object should be initialized in Safari/IE Plugin */
+ before(function (done) {
+ this.timeout(testItemTimeout);
+
+ AdapterJS.webRTCReady(function() {
+ window.navigator.getUserMedia({
+ audio: true,
+ video: false
+ }, function (s) {
+ stream = s;
+ audioTrack = stream.getAudioTracks()[0];
+ done();
+ }, function (error) {
+ throw error;
+ });
+ });
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ beforeEach(function (done) {
+ this.timeout(gUMTimeout);
+
+ pc1 = new RTCPeerConnection({ iceServers: [] });
+ pc2 = new RTCPeerConnection({ iceServers: [] });
+ pc1.oniceconnectionstatechange = function (evt) {
+ if(pc1 && pc1.iceConnectionState === 'connected') {
+ dtmfSender = pc1.createDTMFSender(audioTrack);
+ done();
+ }
+ };
+ pc1.addStream(stream);
+ connect(pc1, pc2);
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ afterEach(function(done) {
+ pc1.close();
+ pc2.close();
+ pc1 = null;
+ pc2 = null;
+ dtmfSender = null;
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.ontonechange :: emit', function (done) {
+ this.timeout(testItemTimeout);
+ var emitTone = '8';
+
+ dtmfSender.ontonechange = function(evt) {
+ assert.isNotNull(evt, 'Event argument missing');
+ assert.isNotNull(evt.target, 'Event target missing');
+ assert.isNotNull(evt.currentTarget, 'Event currentTarget missing');
+ assert.isNotNull(evt.srcElement, 'Event srcElement missing');
+ assert.isNotNull(evt.tone, 'Event tone missing');
+ assert.equal(evt.tone, emitTone, 'Wrong tone sent');
+
+ done();
+ };
+
+ dtmfSender.insertDTMF(emitTone, 100, 100);
+ });
+
+});
diff --git a/tests/unit/RTCDTMFSender.prop.spec.js b/tests/unit/RTCDTMFSender.prop.spec.js
new file mode 100644
index 0000000..ca988aa
--- /dev/null
+++ b/tests/unit/RTCDTMFSender.prop.spec.js
@@ -0,0 +1,168 @@
+var expect = chai.expect;
+var assert = chai.assert;
+var should = chai.should;
+
+// Test timeouts
+var testTimeout = 35000;
+
+// Get User Media timeout
+var gUMTimeout = 5000;
+
+// Test item timeout
+var testItemTimeout = 2000;
+
+describe('RTCDTMFSender | prop', function() {
+ this.timeout(testTimeout);
+
+ var pc1 = null;
+ var pc2 = null;
+ var audioTrack = null;
+ var dtmfSender = null;
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ /* WebRTC Object should be initialized in Safari/IE Plugin */
+ before(function (done) {
+ this.timeout(testItemTimeout);
+
+ AdapterJS.webRTCReady(function() {
+ window.navigator.getUserMedia({
+ audio: true,
+ video: false
+ }, function (s) {
+ stream = s;
+ audioTrack = stream.getAudioTracks()[0];
+ done();
+ }, function (error) {
+ throw error;
+ });
+ });
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ beforeEach(function (done) {
+ this.timeout(gUMTimeout);
+
+ pc1 = new RTCPeerConnection({ iceServers: [] });
+ pc2 = new RTCPeerConnection({ iceServers: [] });
+ pc1.oniceconnectionstatechange = function (evt) {
+ if(pc1 && pc1.iceConnectionState === 'connected') {
+ dtmfSender = pc1.createDTMFSender(audioTrack);
+ done();
+ }
+ };
+ pc1.addStream(stream);
+ connect(pc1, pc2);
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ afterEach(function(done) {
+ pc1.close();
+ pc2.close();
+ pc1 = null;
+ pc2 = null;
+ dtmfSender = null;
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.insertDTMF :: function', function (done) {
+ this.timeout(testItemTimeout);
+ assert.equal(typeof dtmfSender.insertDTMF, FUNCTION_TYPE);
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.insertDTMF :: success returns true', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isTrue(dtmfSender.insertDTMF('', 100, 100));
+ assert.isTrue(dtmfSender.insertDTMF('13,1', 100, 100));
+ assert.isTrue(dtmfSender.insertDTMF(',,,', 200, 100));
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.insertDTMF :: failure returns false', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isFalse(dtmfSender.insertDTMF('13,1', 10, 100));
+ assert.isFalse(dtmfSender.insertDTMF('13,1', 100, 10));
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.insertDTMF :: edge values', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isTrue(dtmfSender.insertDTMF('1', 70, 100), 'on low duration egde');
+ assert.isTrue(dtmfSender.insertDTMF('1', 6000, 100), 'on high duration egde');
+ assert.isTrue(dtmfSender.insertDTMF('1', 100, 50), 'low gap edge');
+
+ assert.isFalse(dtmfSender.insertDTMF('1', 69, 100), 'under duration egde');
+ assert.isFalse(dtmfSender.insertDTMF('1', 6001, 100), 'over duration egde');
+ assert.isFalse(dtmfSender.insertDTMF('1', 100, 49), 'under gap edge');
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.insertDTMF :: default arguments', function (done) {
+ this.timeout(testItemTimeout);
+ var e = /.*/;
+ assert.doesNotThrow(function(){dtmfSender.insertDTMF('1', 100);}, e, 'default gap, does not throw');
+ assert.doesNotThrow(function(){dtmfSender.insertDTMF('1');}, e, 'default duration, does not throw');
+ assert.throws(function(){dtmfSender.insertDTMF();}, e, 'Missing tones, throws');
+
+ assert.isTrue(dtmfSender.insertDTMF('1', 100), 'default gap');
+ assert.isTrue(dtmfSender.insertDTMF('1'), 'default duration');
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.canInsertDTMF :: bool', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isBoolean(dtmfSender.canInsertDTMF);
+ assert.isTrue(dtmfSender.canInsertDTMF);
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.track :: audioTrack', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isDefined(dtmfSender.track);
+ assert.isNotNull(dtmfSender.track);
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.toneBuffer :: string', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isString(dtmfSender.toneBuffer);
+ assert.equal(dtmfSender.toneBuffer, '');
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.duration :: int', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isNumber(dtmfSender.duration);
+ done();
+ });
+
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ it('RTCDTMFSender.interToneGap :: int', function (done) {
+ this.timeout(testItemTimeout);
+ assert.isNumber(dtmfSender.interToneGap);
+ done();
+ });
+
+});
diff --git a/tests/unit/RTCPeerConnection.constraints.spec.js b/tests/unit/RTCPeerConnection.constraints.spec.js
index 60667fc..f8cb092 100644
--- a/tests/unit/RTCPeerConnection.constraints.spec.js
+++ b/tests/unit/RTCPeerConnection.constraints.spec.js
@@ -14,10 +14,13 @@ var gUMTimeout = 25000;
// Test item timeout
var testItemTimeout = 2000;
+// TODO(J-O): Where are the assertions ? Is this even testing anything ?
describe('RTCPeerConnection | RTCConfiguration', function() {
this.timeout(testTimeout);
+ var peer = null;
+
/* WebRTC Object should be initialized in Safari/IE Plugin */
before(function (done) {
this.timeout(testItemTimeout);
@@ -27,121 +30,124 @@ describe('RTCPeerConnection | RTCConfiguration', function() {
});
});
- (function (constraints) {
-
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {
- this.timeout(testItemTimeout);
-
- var peer = new RTCPeerConnection(constraints);
- });
-
- })({ iceServers: [] });
-
-
- (function (constraints) {
-
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {
- this.timeout(testItemTimeout);
-
- var peer = new RTCPeerConnection(constraints);
- });
-
- })({ iceServers: [{ url: 'turn:numb.viagenie.ca', username: 'leticia.choo@temasys.com.sg', credential: 'xxxxx' }] });
-
-
- (function (constraints) {
+ /////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////
+ afterEach(function(done) {
+ peer = null;
+ done();
+ });
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {
+ var makeDesc = function (config, constraints) {
+ var description = 'new RTCPeerConnection(';
+ if (config !== undefined) {
+ description += JSON.stringify(config);
+ }
+ if (constraints !== undefined) {
+ description += ', ' + JSON.stringify(constraints);
+ }
+ description += ')';
+
+ return description;
+ };
+
+ var testRTCPCContruct = function (config, constraints) {
+ var description = makeDesc(config, constraints);
+ it(description, function () {
this.timeout(testItemTimeout);
- var peer = new RTCPeerConnection(constraints);
+ var peer = new RTCPeerConnection(config, constraints);
});
+ };
- })({ iceServers: [{ url: 'leticia.choo@temasys.com.sg@turn:numb.viagenie.ca', credential: 'xxxxx' }] });
-
-
- (function (constraints) {
-
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {
+ var testRTCPCContruct_skip = function (config, constraints) {
+ var description = makeDesc(config, constraints);
+ it.skip(description, function () {
this.timeout(testItemTimeout);
- var peer = new RTCPeerConnection(constraints);
+ var peer = new RTCPeerConnection(config, constraints);
});
+ };
- })({ iceServers: [{ urls: ['turn:numb.viagenie.ca', 'turn:numb.viagenie.ca'], username: 'leticia.choo@temasys.com.sg', credential: 'xxxxx' }] });
-
-
- (function (constraints) {
-
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {
+ var testRTCPCContruct_throw = function (config, constraints) {
+ var description = makeDesc(config, constraints);
+ description += ' -> Throws Error';
+ it(description, function () {
this.timeout(testItemTimeout);
- var peer = new RTCPeerConnection(constraints);
- });
-
- })({ iceServers: [{ url: 'stun:stun.l.google.com:19302' }] });
-
-
- (function (constraints) {
-
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {
- this.timeout(testItemTimeout);
-
- var peer = new RTCPeerConnection(constraints);
- });
-
- })({ iceServers: [{ url: 'turn:numb.viagenie.ca', username: 'leticia.choo@temasys.com.sg', credential: 'xxxxx' }, { url: 'stun:stun.l.google.com:19302' }] });
-
-
- (function (constraints) {
-
- it.skip('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {});
-
- })({ bundlePolicy: 'balanced' });
-
-
- (function (constraints) {
-
- it.skip('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {});
-
- })({ bundlePolicy: 'max-compat' });
-
-
- (function (constraints) {
-
- it.skip('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {});
-
- })({ bundlePolicy: 'max-bundle' });
-
-
- (function (constraints) {
-
- it.skip('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {});
-
- })({ iceTransportPolicy: 'none' });
-
-
- (function (constraints) {
-
- it.skip('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {});
-
- })({ iceTransportPolicy: 'relay' });
-
-
- (function (constraints) {
-
- it.skip('new RTCPeerConnection(' + JSON.stringify(constraints) + ')', function () {});
-
- })({ iceTransportPolicy: 'all' });
-
-
- (function (constraints, optional) {
-
- it('new RTCPeerConnection(' + JSON.stringify(constraints) + ', ' + JSON.stringify(optional) + ')', function () {
- this.timeout(testItemTimeout);
+ var fn = function() {
+ var peer = new RTCPeerConnection(config, constraints);
+ };
- var peer = new RTCPeerConnection(constraints, optional);
+ expect(fn).to.throw(Error);
});
+ };
+
+ // EXPECT WORKING
+ testRTCPCContruct();
+ testRTCPCContruct(null);
+ testRTCPCContruct(null, []);
+ testRTCPCContruct(null, {});
+ testRTCPCContruct(null, null);
+ testRTCPCContruct(null, null, null);
+ testRTCPCContruct(null, null, 0);
+ testRTCPCContruct(null, null, 1);
+ testRTCPCContruct(null, null, true);
+ testRTCPCContruct(null, null, false);
+ testRTCPCContruct(null, null, 'test');
+ testRTCPCContruct(null, null, {});
+ testRTCPCContruct(null, null, []);
+ testRTCPCContruct(null, null, { test: [] });
+ testRTCPCContruct({ iceServers: [] });
+ testRTCPCContruct({ iceServers: [{ url: 'turn:numb.viagenie.ca', username: 'leticia.choo@temasys.com.sg', credential: 'xxxxx' }] });
+ testRTCPCContruct({ iceServers: [{ url: 'leticia.choo@temasys.com.sg@turn:numb.viagenie.ca', credential: 'xxxxx' }] });
+ testRTCPCContruct({ iceServers: [{ urls: ['turn:numb.viagenie.ca', 'turn:numb.viagenie.ca'], username: 'leticia.choo@temasys.com.sg', credential: 'xxxxx' }] });
+ testRTCPCContruct({ iceServers: [{ url: 'stun:stun.l.google.com:19302' }] });
+ testRTCPCContruct({ iceServers: [{ url: 'turn:numb.viagenie.ca', username: 'leticia.choo@temasys.com.sg', credential: 'xxxxx' }, { url: 'stun:stun.l.google.com:19302' }] });
+ testRTCPCContruct({ iceServers: [] }, { optional: [{ DtlsSrtpKeyAgreement: true }] });
+ testRTCPCContruct({ iceServers: [] }, {});
+ testRTCPCContruct({ iceServers: [] }, { optional: [] });
+ testRTCPCContruct({ iceServers: [] }, { optional: undefined });
+ testRTCPCContruct({ iceServers: [] }, { optional: null });
+ testRTCPCContruct({ iceServers: [] }, { mandatory: {} });
+ testRTCPCContruct({ iceServers: [] }, { mandatory: undefined });
+ testRTCPCContruct({ iceServers: [] }, { mandatory: null });
+ testRTCPCContruct({ iceServers: [] }, { optional: [], mandatory: {} });
+ testRTCPCContruct({ iceServers: [] }, { optional: [], mandatory: null });
+
+ // EXPECT THROWNIG ERROR
+ testRTCPCContruct_throw(1);
+ testRTCPCContruct_throw(0);
+ testRTCPCContruct_throw(true);
+ testRTCPCContruct_throw(false);
+ testRTCPCContruct_throw('test');
+ testRTCPCContruct_throw({});
+ testRTCPCContruct_throw({ iceServers: null });
+ testRTCPCContruct_throw({ iceServers: '' });
+ testRTCPCContruct_throw({ iceServers: true });
+ testRTCPCContruct_throw(null, 1);
+ testRTCPCContruct_throw(null, 0);
+ testRTCPCContruct_throw(null, true);
+ testRTCPCContruct_throw(null, false);
+ testRTCPCContruct_throw(null, 'test');
+ testRTCPCContruct_throw(null, { optional: 1 });
+ testRTCPCContruct_throw(null, { optional: 0 });
+ testRTCPCContruct_throw(null, { optional: 'test' });
+ testRTCPCContruct_throw(null, { optional: true });
+ testRTCPCContruct_throw(null, { optional: false });
+ testRTCPCContruct_throw(null, { optional: {} }); // Should throw error as there are plugin failures
+ testRTCPCContruct_throw({ iceServers: [] }, { mandatory: [] });
+ testRTCPCContruct_throw({ iceServers: [] }, { mandatory: 'test'});
+ testRTCPCContruct_throw({ iceServers: [] }, { mandatory: 1 });
+ testRTCPCContruct_throw({ iceServers: [] }, { mandatory: 0 });
+ testRTCPCContruct_throw({ iceServers: [] }, { optional: 'test', mandatory: [] });
+ testRTCPCContruct_throw({ iceServers: [] }, { optional: [], mandatory: 'test' });
+
+ // SKIP TEST
+ testRTCPCContruct_skip({ bundlePolicy: 'balanced' });
+ testRTCPCContruct_skip({ bundlePolicy: 'max-compat' });
+ testRTCPCContruct_skip({ bundlePolicy: 'max-bundle' });
+ testRTCPCContruct_skip({ iceTransportPolicy: 'none' });
+ testRTCPCContruct_skip({ iceTransportPolicy: 'relay' });
+ testRTCPCContruct_skip({ iceTransportPolicy: 'all' });
- })({ iceServers: [] }, { optional: [{ DtlsSrtpKeyAgreement: true }] });
});
\ No newline at end of file
diff --git a/tests/unit/RTCPeerConnection.event.spec.js b/tests/unit/RTCPeerConnection.event.spec.js
index b9be996..1159bfb 100644
--- a/tests/unit/RTCPeerConnection.event.spec.js
+++ b/tests/unit/RTCPeerConnection.event.spec.js
@@ -29,7 +29,15 @@ describe('RTCPeerConnection | EventHandler', function() {
this.timeout(testItemTimeout);
AdapterJS.webRTCReady(function() {
- done();
+ window.navigator.getUserMedia({
+ audio: true,
+ video: true
+ }, function (data) {
+ stream = data;
+ done();
+ }, function (error) {
+ throw error;
+ });
});
});
@@ -38,21 +46,10 @@ describe('RTCPeerConnection | EventHandler', function() {
this.slow(1000);
this.timeout(gUMTimeout + 1000);
- window.navigator.getUserMedia({
- audio: true,
- video: true
-
- }, function (data) {
- stream = data;
-
- peer1 = new RTCPeerConnection({ iceServers: [] });
- peer2 = new RTCPeerConnection({ iceServers: [] });
-
- done();
+ peer1 = new RTCPeerConnection({ iceServers: [] });
+ peer2 = new RTCPeerConnection({ iceServers: [] });
- }, function (error) {
- throw error;
- });
+ done();
});
@@ -92,7 +89,7 @@ describe('RTCPeerConnection | EventHandler', function() {
}
};
- peer1.onicecandidate = function () {
+ peer1.onicecandidate = function (event) {
var candidate = event.candidate;
if (candidate === null) {
@@ -107,7 +104,7 @@ describe('RTCPeerConnection | EventHandler', function() {
}
};
- peer2.onicecandidate = function () {
+ peer2.onicecandidate = function (event) {
var candidate = event.candidate;
if (candidate === null) {
@@ -134,11 +131,11 @@ describe('RTCPeerConnection | EventHandler', function() {
var array2 = [];
var checkdone = function() {
- if ( isArrayEqual( array1, ['stable', 'have-local-offer', 'stable'] )
- && isArrayEqual( array2, ['stable', 'have-remote-offer', 'stable'] )) {
+ if ( isArrayEqual( array1, ['stable', 'have-local-offer', 'stable'] ) &&
+ isArrayEqual( array2, ['stable', 'have-remote-offer', 'stable'])) {
done();
}
- }
+ };
peer1.onsignalingstatechange = function () {
array1.push(peer1.signalingState);
@@ -197,11 +194,11 @@ describe('RTCPeerConnection | EventHandler', function() {
var array2 = [];
var checkdone = function() {
- if ( isArrayEqual( array1, ['new', 'checking', 'completed', 'completed'/*, 'closed'*/] )
- && isArrayEqual( array2, ['new', 'checking', 'connected'/*, 'completed', 'closed'*/] )) {
+ if ( isArrayEqual( array1, ['new', 'checking', 'completed', 'completed'/*, 'closed'*/] ) &&
+ isArrayEqual( array2, ['new', 'checking', 'connected'/*, 'completed', 'closed'*/] )) {
done();
}
- }
+ };
peer1.oniceconnectionstatechange = function () {
array1.push(peer1.iceConnectionState);
@@ -233,11 +230,11 @@ describe('RTCPeerConnection | EventHandler', function() {
assert.deepEqual(array1, ['new', 'gathering', 'complete']);
assert.deepEqual(array2, ['new', 'gathering', 'complete']);
- if ( isArrayEqual( array1, ['new', 'gathering', 'complete'] )
- && isArrayEqual( array2, ['new', 'gathering', 'complete'] )) {
+ if ( isArrayEqual( array1, ['new', 'gathering', 'complete'] ) &&
+ isArrayEqual( array2, ['new', 'gathering', 'complete'] )) {
done();
}
- }
+ };
peer1.onicegatheringstatechange = function () {
array1.push(peer1.iceGatheringState);
diff --git a/tests/unit/RTCPeerConnection.offerconstraints.spec.js b/tests/unit/RTCPeerConnection.offerconstraints.spec.js
index e4b8d6f..38b7623 100644
--- a/tests/unit/RTCPeerConnection.offerconstraints.spec.js
+++ b/tests/unit/RTCPeerConnection.offerconstraints.spec.js
@@ -52,7 +52,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
iceServers: []
});
- peer1.addStream(stream);
+ peer2.addStream(stream);
done();
});
@@ -76,7 +76,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
expect(remoteStream.getAudioTracks()).to.have.length(0);
@@ -96,10 +96,10 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
- expect(remoteStream.getAudioTracks()).to.have.length(1);
+ expect(remoteStream.getAudioTracks()).to.have.length(0);
expect(remoteStream.getVideoTracks()).to.have.length(1);
done();
@@ -116,7 +116,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
expect(remoteStream.getAudioTracks()).to.have.length(1);
@@ -136,7 +136,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
expect(remoteStream.getAudioTracks()).to.have.length(1);
@@ -156,7 +156,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
expect(remoteStream.getAudioTracks()).to.have.length(1);
@@ -176,7 +176,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
expect(remoteStream.getAudioTracks()).to.have.length(0);
@@ -196,7 +196,7 @@ describe('RTCPeerConnection.createOffer | RTCOfferOptions', function() {
it('RTCPeerConnection.createOffer(successCb, failureCb, ' + JSON.stringify(constraints) + ')', function (done) {
this.timeout(testItemTimeout);
- peer2.onaddstream = function (event) {
+ peer1.onaddstream = function (event) {
var remoteStream = event.stream || event;
expect(remoteStream.getAudioTracks()).to.have.length(1);
diff --git a/tests/unit/RTCPeerConnection.prop.spec.js b/tests/unit/RTCPeerConnection.prop.spec.js
index 2d6ec3d..00b7933 100644
--- a/tests/unit/RTCPeerConnection.prop.spec.js
+++ b/tests/unit/RTCPeerConnection.prop.spec.js
@@ -241,20 +241,14 @@ describe('RTCPeerConnection | Properties', function() {
assert.equal(typeof peer1.getStreamById, FUNCTION_TYPE);
assert.equal(typeof peer2.getStreamById, FUNCTION_TYPE);
- var result1 = peer1.getStreamById(stream.id);
- var result2 = peer2.getStreamById(stream.id);
-
- expect(result1).to.be.null;
- expect(result2).to.be.null;
+ assert.isNull(peer1.getStreamById(stream.id));
+ assert.isNull(peer2.getStreamById(stream.id));
peer1.addStream(stream);
peer2.addStream(stream);
- var result1 = peer1.getStreamById(stream.id);
- var result2 = peer2.getStreamById(stream.id);
-
- expect(result1).to.equal(stream);
- expect(result2).to.equal(stream);
+ expect(peer1.getStreamById(stream.id)).to.equal(stream);
+ expect(peer2.getStreamById(stream.id)).to.equal(stream);
done();
});
diff --git a/tests/unit/VideoElement.event.spec.js b/tests/unit/VideoElement.event.spec.js
index 7c4561d..aaf076b 100644
--- a/tests/unit/VideoElement.event.spec.js
+++ b/tests/unit/VideoElement.event.spec.js
@@ -68,7 +68,7 @@ describe('VideoElement | EventHandler', function() {
video.onplaying = function(event) {
done();
- }
+ };
video = attachMediaStream(video, stream);
});
@@ -81,10 +81,10 @@ describe('VideoElement | EventHandler', function() {
var now = new Date().getTime();
video.onplaying = function(event) {
- expect(event.target).not.to.be.undefined;
- expect(event.currentTarget).not.to.be.undefined;
- expect(event.srcElement).not.to.be.undefined;
- expect(event.timeStamp).not.to.be.undefined;
+ assert.isDefined(event.target);
+ assert.isDefined(event.currentTarget);
+ assert.isDefined(event.srcElement);
+ assert.isDefined(event.timeStamp);
expect(event.timeStamp).to.be.above(0);
expect(event.timeStamp).to.be.within(now - timeStampMaxError, now + timeStampMaxError);
@@ -105,12 +105,12 @@ describe('VideoElement | EventHandler', function() {
var expectedOnplayCaught = 2;
video.onplay = function() {
- if(++onPlayCaught == expectedOnplayCaught) {
+ if(++onPlayCaught === expectedOnplayCaught) {
done();
}
video.pause();
video.play();
- }
+ };
video = attachMediaStream(video, stream);
});
@@ -123,10 +123,10 @@ describe('VideoElement | EventHandler', function() {
var now = new Date().getTime();
video.onplay = function(event) {
- expect(event.target).not.to.be.undefined;
- expect(event.currentTarget).not.to.be.undefined;
- expect(event.srcElement).not.to.be.undefined;
- expect(event.timeStamp).not.to.be.undefined;
+ assert.isDefined(event.target);
+ assert.isDefined(event.currentTarget);
+ assert.isDefined(event.srcElement);
+ assert.isDefined(event.timeStamp);
expect(event.timeStamp).to.be.above(0);
expect(event.timeStamp).to.be.within(now - timeStampMaxError, now + timeStampMaxError);
@@ -161,10 +161,10 @@ it('VideoElement.onloadedmetadata :: emit', function (done) {
var now = new Date().getTime();
video.onloadedmetadata = function(event) {
- expect(event.target).not.to.be.undefined;
- expect(event.currentTarget).not.to.be.undefined;
- expect(event.srcElement).not.to.be.undefined;
- expect(event.timeStamp).not.to.be.undefined;
+ assert.isDefined(event.target);
+ assert.isDefined(event.currentTarget);
+ assert.isDefined(event.srcElement);
+ assert.isDefined(event.timeStamp);
expect(event.timeStamp).to.be.above(0);
expect(event.timeStamp).to.be.within(now - timeStampMaxError, now + timeStampMaxError);
diff --git a/third_party/adapter b/third_party/adapter
index b2c9d00..10ba31e 160000
--- a/third_party/adapter
+++ b/third_party/adapter
@@ -1 +1 @@
-Subproject commit b2c9d00ccdd0bbe1e39cda24a75aae0bea163474
+Subproject commit 10ba31ea2d874eeee51aba45e6eacced6fc56636