spreed/js/simplewebrtc/bundled.js
Daniel Calviño Sánchez ab7f97c011 Fix "module.exports" no longer available in WebRTC adapter 7.2.5
Since 7.2.5 the "main" property in the "package.json" of WebRTC adapter
points to an ES6 file which does not provide "module.exports" but
"exports.default". Due to this uses of "var adapter =
require('webrtc-adapter');" now fail, as the properties of the module
are loaded in "adapter.default" instead of in "adapter". To restore the
previous and expected behaviour a Babel plugin is now used to also add
"module.exports" if "exports.default" exists.

As WebRTC adapter still provides an ES5 file an alternative solution
would have been to alias "webrtc-adapter" to that file with
'--transform [ aliasify --global --aliases [ --webrtc-adapter
"webrtc-adapter/dist/adapter_core5.js" ] ]'.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2019-07-11 15:06:17 +02:00

8704 lines
264 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SimpleWebRTC = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
/* global module, chrome */
// getScreenMedia helper by @HenrikJoreteg
var getUserMedia = function getUserMedia(constraints, callback) {
if (!window.navigator || !window.navigator.mediaDevices || !window.navigator.mediaDevices.getUserMedia) {
var error = new Error('MediaStreamError');
error.name = 'NotSupportedError';
if (callback) {
callback(error, null);
}
return;
}
window.navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
callback(null, stream);
}).catch(function (error) {
callback(error, null);
});
}; // cache for constraints and callback
var cache = {};
module.exports = function (mode, constraints, cb) {
var hasConstraints = arguments.length === 3;
var callback = hasConstraints ? cb : constraints;
var error;
if (typeof window === 'undefined' || window.location.protocol === 'http:') {
error = new Error('NavigatorUserMediaError');
error.name = 'HTTPS_REQUIRED';
return callback(error);
}
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
navigator.mediaDevices.getDisplayMedia({
video: true
}).then(function (stream) {
callback(null, stream);
}).catch(function (error) {
callback(error, null);
});
} else if (navigator.webkitGetUserMedia) {
var chromever = parseInt(window.navigator.userAgent.match(/Chrome\/(\d+)\./)[1], 10);
var maxver = 33; // Chrome 71 dropped support for "window.chrome.webstore;".
var isCef = chromever < 71 && !window.chrome.webstore; // "known" crash in chrome 34 and 35 on linux
if (window.navigator.userAgent.match('Linux')) {
maxver = 35;
} // check that the extension is installed by looking for a
// sessionStorage variable that contains the extension id
// this has to be set after installation unless the contest
// script does that
if (sessionStorage.getScreenMediaJSExtensionId) {
chrome.runtime.sendMessage(sessionStorage.getScreenMediaJSExtensionId, {
type: 'getScreen',
id: 1
}, null, function (data) {
if (!data || data.sourceId === '') {
// user canceled
var error = new Error('NavigatorUserMediaError');
error.name = 'PERMISSION_DENIED';
callback(error);
} else {
constraints = hasConstraints && constraints || {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
maxWidth: window.screen.width,
maxHeight: window.screen.height,
maxFrameRate: 3
}
}
};
constraints.video.mandatory.chromeMediaSourceId = data.sourceId;
getUserMedia(constraints, callback);
}
});
} else if (window.cefGetScreenMedia) {
//window.cefGetScreenMedia is experimental - may be removed without notice
window.cefGetScreenMedia(function (sourceId) {
if (!sourceId) {
var error = new Error('cefGetScreenMediaError');
error.name = 'CEF_GETSCREENMEDIA_CANCELED';
callback(error);
} else {
constraints = hasConstraints && constraints || {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
maxWidth: window.screen.width,
maxHeight: window.screen.height,
maxFrameRate: 3
},
optional: [{
googLeakyBucket: true
}, {
googTemporalLayeredScreencast: true
}]
}
};
constraints.video.mandatory.chromeMediaSourceId = sourceId;
getUserMedia(constraints, callback);
}
});
} else if (isCef || chromever >= 26 && chromever <= maxver) {
// chrome 26 - chrome 33 way to do it -- requires bad chrome://flags
// note: this is basically in maintenance mode and will go away soon
constraints = hasConstraints && constraints || {
video: {
mandatory: {
googLeakyBucket: true,
maxWidth: window.screen.width,
maxHeight: window.screen.height,
maxFrameRate: 3,
chromeMediaSource: 'screen'
}
}
};
getUserMedia(constraints, callback);
} else {
// chrome 34+ way requiring an extension
var pending = window.setTimeout(function () {
error = new Error('NavigatorUserMediaError');
error.name = 'EXTENSION_UNAVAILABLE';
return callback(error);
}, 1000);
cache[pending] = [callback, hasConstraints ? constraints : null];
window.postMessage({
type: 'getScreen',
id: pending
}, '*');
}
} else if (window.navigator.userAgent.match('Firefox')) {
var ffver = parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1], 10);
if (ffver >= 52) {
mode = mode || 'window';
constraints = hasConstraints && constraints || {
video: {
mozMediaSource: mode,
mediaSource: mode
}
};
getUserMedia(constraints, function (err, stream) {
callback(err, stream);
if (err) {
return;
} // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1045810
var lastTime = stream.currentTime;
var polly = window.setInterval(function () {
if (!stream) {
window.clearInterval(polly);
}
if (stream.currentTime === lastTime) {
window.clearInterval(polly);
if (stream.onended) {
stream.onended();
}
}
lastTime = stream.currentTime;
}, 500);
});
} else {
error = new Error('NavigatorUserMediaError');
error.name = 'FF52_REQUIRED';
return callback(error);
}
}
};
typeof window !== 'undefined' && window.addEventListener('message', function (event) {
if (event.origin !== window.location.origin && !event.isTrusted) {
return;
}
if (event.data.type === 'gotScreen' && cache[event.data.id]) {
var data = cache[event.data.id];
var constraints = data[1];
var callback = data[0];
delete cache[event.data.id];
if (event.data.sourceId === '') {
// user canceled
var error = new Error('NavigatorUserMediaError');
error.name = 'PERMISSION_DENIED';
callback(error);
} else {
constraints = constraints || {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
maxWidth: window.screen.width,
maxHeight: window.screen.height,
maxFrameRate: 3
},
optional: [{
googLeakyBucket: true
}, {
googTemporalLayeredScreencast: true
}]
}
};
constraints.video.mandatory.chromeMediaSourceId = event.data.sourceId;
getUserMedia(constraints, callback);
}
} else if (event.data.type === 'getScreenPending') {
window.clearTimeout(event.data.id);
}
});
},{}],2:[function(require,module,exports){
"use strict";
/* global module */
var util = require('util');
var hark = require('hark');
var getScreenMedia = require('./getscreenmedia');
var WildEmitter = require('wildemitter');
var mockconsole = require('mockconsole');
function isAllTracksEnded(stream) {
var isAllTracksEnded = true;
stream.getTracks().forEach(function (t) {
isAllTracksEnded = t.readyState === 'ended' && isAllTracksEnded;
});
return isAllTracksEnded;
}
function LocalMedia(opts) {
WildEmitter.call(this);
var config = this.config = {
detectSpeakingEvents: false,
audioFallback: false,
media: {
audio: true,
video: true
},
harkOptions: null,
logger: mockconsole
};
var item;
for (item in opts) {
if (opts.hasOwnProperty(item)) {
this.config[item] = opts[item];
}
}
this.logger = config.logger;
this._log = this.logger.log.bind(this.logger, 'LocalMedia:');
this._logerror = this.logger.error.bind(this.logger, 'LocalMedia:');
this.localStreams = [];
this.localScreens = [];
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
this._logerror('Your browser does not support local media capture.');
}
this._audioMonitors = [];
this.on('localStreamStopped', this._stopAudioMonitor.bind(this));
this.on('localScreenStopped', this._stopAudioMonitor.bind(this));
}
util.inherits(LocalMedia, WildEmitter);
LocalMedia.prototype.start = function (mediaConstraints, cb) {
var self = this;
var constraints = mediaConstraints || this.config.media;
if (!navigator || !navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
var error = new Error('MediaStreamError');
error.name = 'NotSupportedError';
if (cb) {
return cb(error, null);
}
return;
}
this.emit('localStreamRequested', constraints);
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
// Although the promise should be resolved only if all the constraints
// are met Edge resolves it if both audio and video are requested but
// only audio is available.
if (constraints.video && stream.getVideoTracks().length === 0) {
constraints.video = false;
self.start(constraints, cb);
return;
}
if (constraints.audio && self.config.detectSpeakingEvents) {
self._setupAudioMonitor(stream, self.config.harkOptions);
}
self.localStreams.push(stream);
stream.getTracks().forEach(function (track) {
track.addEventListener('ended', function () {
if (isAllTracksEnded(stream)) {
self._removeStream(stream);
}
});
});
self.emit('localStream', stream);
if (cb) {
return cb(null, stream);
}
}).catch(function (err) {
// Fallback for users without a camera
if (self.config.audioFallback && err.name === 'NotFoundError' && constraints.video !== false) {
constraints.video = false;
self.start(constraints, cb);
return;
}
self.emit('localStreamRequestFailed', constraints);
if (cb) {
return cb(err, null);
}
});
};
LocalMedia.prototype.stop = function (stream) {
this.stopStream(stream);
this.stopScreenShare(stream);
};
LocalMedia.prototype.stopStream = function (stream) {
var self = this;
if (stream) {
var idx = this.localStreams.indexOf(stream);
if (idx > -1) {
stream.getTracks().forEach(function (track) {
track.stop();
});
this._removeStream(stream);
}
} else {
this.localStreams.forEach(function (stream) {
stream.getTracks().forEach(function (track) {
track.stop();
});
self._removeStream(stream);
});
}
};
LocalMedia.prototype.startScreenShare = function (mode, constraints, cb) {
var self = this;
this.emit('localScreenRequested');
if (typeof constraints === 'function' && !cb) {
cb = constraints;
constraints = null;
}
getScreenMedia(mode, constraints, function (err, stream) {
if (!err) {
self.localScreens.push(stream);
stream.getTracks().forEach(function (track) {
track.addEventListener('ended', function () {
var isAllTracksEnded = true;
stream.getTracks().forEach(function (t) {
isAllTracksEnded = t.readyState === 'ended' && isAllTracksEnded;
});
if (isAllTracksEnded) {
self._removeStream(stream);
}
});
});
self.emit('localScreen', stream);
} else {
self.emit('localScreenRequestFailed');
} // enable the callback
if (cb) {
return cb(err, stream);
}
});
};
LocalMedia.prototype.stopScreenShare = function (stream) {
var self = this;
if (stream) {
var idx = this.localScreens.indexOf(stream);
if (idx > -1) {
stream.getTracks().forEach(function (track) {
track.stop();
});
this._removeStream(stream);
}
} else {
this.localScreens.forEach(function (stream) {
stream.getTracks().forEach(function (track) {
track.stop();
});
self._removeStream(stream);
});
}
}; // Audio controls
LocalMedia.prototype.mute = function () {
this._audioEnabled(false);
this.emit('audioOff');
};
LocalMedia.prototype.unmute = function () {
this._audioEnabled(true);
this.emit('audioOn');
}; // Video controls
LocalMedia.prototype.pauseVideo = function () {
this._videoEnabled(false);
this.emit('videoOff');
};
LocalMedia.prototype.resumeVideo = function () {
this._videoEnabled(true);
this.emit('videoOn');
}; // Combined controls
LocalMedia.prototype.pause = function () {
this.mute();
this.pauseVideo();
};
LocalMedia.prototype.resume = function () {
this.unmute();
this.resumeVideo();
}; // Internal methods for enabling/disabling audio/video
LocalMedia.prototype._audioEnabled = function (bool) {
this.localStreams.forEach(function (stream) {
stream.getAudioTracks().forEach(function (track) {
track.enabled = !!bool;
});
});
};
LocalMedia.prototype._videoEnabled = function (bool) {
this.localStreams.forEach(function (stream) {
stream.getVideoTracks().forEach(function (track) {
track.enabled = !!bool;
});
});
}; // check if all audio streams are enabled
LocalMedia.prototype.isAudioEnabled = function () {
var enabled = true;
var hasAudioTracks = false;
this.localStreams.forEach(function (stream) {
var audioTracks = stream.getAudioTracks();
if (audioTracks.length > 0) {
hasAudioTracks = true;
audioTracks.forEach(function (track) {
enabled = enabled && track.enabled;
});
}
}); // If no audioTracks were found, that means there is no microphone device.
// In that case, isAudioEnabled should return false.
if (!hasAudioTracks) {
return false;
}
return enabled;
}; // check if all video streams are enabled
LocalMedia.prototype.isVideoEnabled = function () {
var enabled = true;
var hasVideoTracks = false;
this.localStreams.forEach(function (stream) {
var videoTracks = stream.getVideoTracks();
if (videoTracks.length > 0) {
hasVideoTracks = true;
videoTracks.forEach(function (track) {
enabled = enabled && track.enabled;
});
}
}); // If no videoTracks were found, that means there is no camera device.
// In that case, isVideoEnabled should return false.
if (!hasVideoTracks) {
return false;
}
return enabled;
};
LocalMedia.prototype._removeStream = function (stream) {
var idx = this.localStreams.indexOf(stream);
if (idx > -1) {
this.localStreams.splice(idx, 1);
this.emit('localStreamStopped', stream);
} else {
idx = this.localScreens.indexOf(stream);
if (idx > -1) {
this.localScreens.splice(idx, 1);
this.emit('localScreenStopped', stream);
}
}
};
LocalMedia.prototype._setupAudioMonitor = function (stream, harkOptions) {
this._log('Setup audio');
var audio = hark(stream, harkOptions);
var self = this;
var timeout;
audio.on('speaking', function () {
self.emit('speaking');
});
audio.on('stopped_speaking', function () {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function () {
self.emit('stoppedSpeaking');
}, 1000);
});
audio.on('volume_change', function (volume, threshold) {
self.emit('volumeChange', volume, threshold);
});
this._audioMonitors.push({
audio: audio,
stream: stream
});
};
LocalMedia.prototype._stopAudioMonitor = function (stream) {
var idx = -1;
this._audioMonitors.forEach(function (monitors, i) {
if (monitors.stream === stream) {
idx = i;
}
});
if (idx > -1) {
this._audioMonitors[idx].audio.stop();
this._audioMonitors.splice(idx, 1);
}
}; // fallback for old .localScreen behaviour
Object.defineProperty(LocalMedia.prototype, 'localScreen', {
get: function get() {
return this.localScreens.length > 0 ? this.localScreens[0] : null;
}
});
module.exports = LocalMedia;
},{"./getscreenmedia":1,"hark":9,"mockconsole":11,"util":8,"wildemitter":31}],3:[function(require,module,exports){
"use strict";
/* global module */
var util = require('util');
var webrtcSupport = require('webrtcsupport');
var WildEmitter = require('wildemitter');
function isAllTracksEnded(stream) {
var isAllTracksEnded = true;
stream.getTracks().forEach(function (t) {
isAllTracksEnded = t.readyState === 'ended' && isAllTracksEnded;
});
return isAllTracksEnded;
}
function Peer(options) {
var self = this; // call emitter constructor
WildEmitter.call(this);
this.id = options.id;
this.parent = options.parent;
this.type = options.type || 'video';
this.oneway = options.oneway || false;
this.sharemyscreen = options.sharemyscreen || false;
this.browserPrefix = options.prefix;
this.stream = options.stream;
this.sendVideoIfAvailable = options.sendVideoIfAvailable === undefined ? true : options.sendVideoIfAvailable;
this.enableDataChannels = options.enableDataChannels === undefined ? this.parent.config.enableDataChannels : options.enableDataChannels;
this.receiveMedia = options.receiveMedia || this.parent.config.receiveMedia;
this.channels = {};
this.pendingDCMessages = []; // key (datachannel label) -> value (array[pending messages])
this.sid = options.sid || Date.now().toString();
this.pc = new RTCPeerConnection(this.parent.config.peerConnectionConfig);
this.pc.addEventListener('icecandidate', this.onIceCandidate.bind(this));
this.pc.addEventListener('endofcandidates', function (event) {
self.send('endOfCandidates', event);
});
this.pc.addEventListener('addstream', this.handleRemoteStreamAdded.bind(this));
this.pc.addEventListener('datachannel', this.handleDataChannelAdded.bind(this));
this.pc.addEventListener('removestream', this.handleStreamRemoved.bind(this)); // Just fire negotiation needed events for now
// When browser re-negotiation handling seems to work
// we can use this as the trigger for starting the offer/answer process
// automatically. We'll just leave it be for now while this stabalizes.
this.pc.addEventListener('negotiationneeded', this.emit.bind(this, 'negotiationNeeded'));
this.pc.addEventListener('iceconnectionstatechange', this.emit.bind(this, 'iceConnectionStateChange'));
this.pc.addEventListener('iceconnectionstatechange', function () {
switch (self.pc.iceConnectionState) {
case 'failed':
// currently, in chrome only the initiator goes to failed
// so we need to signal this to the peer
if (self.pc.localDescription.type === 'offer') {
self.parent.emit('iceFailed', self);
self.send('connectivityError');
}
break;
}
});
this.pc.addEventListener('signalingstatechange', this.emit.bind(this, 'signalingStateChange'));
this.logger = this.parent.logger; // handle screensharing/broadcast mode
if (options.type === 'screen') {
if (this.parent.localScreen && this.sharemyscreen) {
this.logger.log('adding local screen stream to peer connection');
this.pc.addStream(this.parent.localScreen);
this.broadcaster = options.broadcaster;
}
} else {
this.parent.localStreams.forEach(function (stream) {
stream.getTracks().forEach(function (track) {
if (track.kind !== 'video' || self.sendVideoIfAvailable) {
self.pc.addTrack(track, stream);
}
});
});
} // proxy events to parent
this.on('*', function () {
self.parent.emit.apply(self.parent, arguments);
});
}
util.inherits(Peer, WildEmitter);
Peer.prototype.offer = function (options) {
this.pc.createOffer(options).then(function (offer) {
this.pc.setLocalDescription(offer).then(function () {
if (this.parent.config.nick) {
// The offer is a RTCSessionDescription that only serializes
// its own attributes to JSON, so if extra attributes are needed
// a regular object has to be sent instead.
offer = {
type: offer.type,
sdp: offer.sdp,
nick: this.parent.config.nick
};
}
this.send('offer', offer);
}.bind(this)).catch(function (error) {
console.warn("setLocalDescription for offer failed: ", error);
}.bind(this));
}.bind(this)).catch(function (error) {
console.warn("createOffer failed: ", error);
}.bind(this));
};
Peer.prototype.handleOffer = function (offer) {
this.pc.setRemoteDescription(offer).then(function () {
this.answer();
}.bind(this)).catch(function (error) {
console.warn("setRemoteDescription for offer failed: ", error);
}.bind(this));
};
Peer.prototype.answer = function () {
this.pc.createAnswer().then(function (answer) {
this.pc.setLocalDescription(answer).then(function () {
if (this.parent.config.nick) {
// The answer is a RTCSessionDescription that only serializes
// its own attributes to JSON, so if extra attributes are needed
// a regular object has to be sent instead.
answer = {
type: answer.type,
sdp: answer.sdp,
nick: this.parent.config.nick
};
}
this.send('answer', answer);
}.bind(this)).catch(function (error) {
console.warn("setLocalDescription for answer failed: ", error);
}.bind(this));
}.bind(this)).catch(function (error) {
console.warn("createAnswer failed: ", error);
}.bind(this));
};
Peer.prototype.handleAnswer = function (answer) {
this.pc.setRemoteDescription(answer).catch(function (error) {
console.warn("setRemoteDescription for answer failed: ", error);
}.bind(this));
};
Peer.prototype.handleMessage = function (message) {
var self = this;
this.logger.log('getting', message.type, message);
if (message.prefix) {
this.browserPrefix = message.prefix;
}
if (message.type === 'offer') {
if (!this.nick) {
this.nick = message.payload.nick;
}
delete message.payload.nick;
this.handleOffer(message.payload);
} else if (message.type === 'answer') {
if (!this.nick) {
this.nick = message.payload.nick;
}
delete message.payload.nick;
this.handleAnswer(message.payload);
} else if (message.type === 'candidate') {
this.pc.addIceCandidate(message.payload.candidate);
} else if (message.type === 'connectivityError') {
this.parent.emit('connectivityError', self);
} else if (message.type === 'mute') {
this.parent.emit('mute', {
id: message.from,
name: message.payload.name
});
} else if (message.type === 'unmute') {
this.parent.emit('unmute', {
id: message.from,
name: message.payload.name
});
} else if (message.type === 'endOfCandidates') {
this.pc.addIceCandidate('');
} else if (message.type === 'unshareScreen') {
this.parent.emit('unshareScreen', {
id: message.from
});
this.end();
}
}; // send via signalling channel
Peer.prototype.send = function (messageType, payload) {
var message = {
to: this.id,
sid: this.sid,
broadcaster: this.broadcaster,
roomType: this.type,
type: messageType,
payload: payload,
prefix: webrtcSupport.prefix
};
this.logger.log('sending', messageType, message);
this.parent.emit('message', message);
}; // send via data channel
// returns true when message was sent and false if channel is not open
Peer.prototype.sendDirectly = function (channel, messageType, payload) {
var message = {
type: messageType,
payload: payload
};
this.logger.log('sending via datachannel', channel, messageType, message);
var dc = this.getDataChannel(channel);
if (dc.readyState !== 'open') {
if (!this.pendingDCMessages.hasOwnProperty(channel)) {
this.pendingDCMessages[channel] = [];
}
this.pendingDCMessages[channel].push(message);
return false;
}
dc.send(JSON.stringify(message));
return true;
}; // Internal method registering handlers for a data channel and emitting events on the peer
Peer.prototype._observeDataChannel = function (channel) {
var self = this;
channel.onclose = this.emit.bind(this, 'channelClose', channel);
channel.onerror = this.emit.bind(this, 'channelError', channel);
channel.onmessage = function (event) {
self.emit('channelMessage', self, channel.label, JSON.parse(event.data), channel, event);
};
channel.onopen = function () {
self.emit('channelOpen', channel); // Check if there are messages that could not be send
if (self.pendingDCMessages.hasOwnProperty(channel.label)) {
var pendingMessages = self.pendingDCMessages[channel.label];
for (var i = 0; i < pendingMessages.length; i++) {
self.sendDirectly(channel.label, pendingMessages[i].type, pendingMessages[i].payload);
}
self.pendingDCMessages[channel.label] = [];
}
};
}; // Fetch or create a data channel by the given name
Peer.prototype.getDataChannel = function (name, opts) {
if (!webrtcSupport.supportDataChannel) {
return this.emit('error', new Error('createDataChannel not supported'));
}
var channel = this.channels[name];
opts || (opts = {});
if (channel) {
return channel;
} // if we don't have one by this label, create it
channel = this.channels[name] = this.pc.createDataChannel(name, opts);
this._observeDataChannel(channel);
return channel;
};
Peer.prototype.onIceCandidate = function (event) {
var candidate = event.candidate;
if (this.closed) {
return;
}
if (candidate) {
var pcConfig = this.parent.config.peerConnectionConfig;
if (webrtcSupport.prefix === 'moz' && pcConfig && pcConfig.iceTransports && candidate.candidate && candidate.candidate.candidate && candidate.candidate.candidate.indexOf(pcConfig.iceTransports) < 0) {
this.logger.log('Ignoring ice candidate not matching pcConfig iceTransports type: ', pcConfig.iceTransports);
} else {
// Retain legacy data structure for compatibility with
// mobile clients.
var expandedCandidate = {
candidate: {
candidate: candidate.candidate,
sdpMid: candidate.sdpMid,
sdpMLineIndex: candidate.sdpMLineIndex
}
};
this.send('candidate', expandedCandidate);
}
} else {
this.logger.log("End of candidates.");
}
};
Peer.prototype.start = function () {
// well, the webrtc api requires that we either
// a) create a datachannel a priori
// b) do a renegotiation later to add the SCTP m-line
// Let's do (a) first...
if (this.enableDataChannels) {
this.getDataChannel('simplewebrtc');
}
this.offer(this.receiveMedia);
};
Peer.prototype.icerestart = function () {
var constraints = this.receiveMedia;
constraints.iceRestart = true;
this.offer(constraints);
};
Peer.prototype.end = function () {
if (this.closed) {
return;
}
this.pc.close();
this.handleStreamRemoved();
};
Peer.prototype.handleRemoteStreamAdded = function (event) {
var self = this;
if (this.stream) {
this.logger.warn('Already have a remote stream');
} else {
this.stream = event.stream;
this.stream.getTracks().forEach(function (track) {
track.addEventListener('ended', function () {
if (isAllTracksEnded(self.stream)) {
self.end();
}
});
});
this.parent.emit('peerStreamAdded', this);
}
};
Peer.prototype.handleStreamRemoved = function () {
var peerIndex = this.parent.peers.indexOf(this);
if (peerIndex > -1) {
this.parent.peers.splice(peerIndex, 1);
this.closed = true;
this.parent.emit('peerStreamRemoved', this);
}
};
Peer.prototype.handleDataChannelAdded = function (event) {
var channel = event.channel;
this.channels[channel.label] = channel;
this._observeDataChannel(channel);
};
module.exports = Peer;
},{"util":8,"webrtcsupport":30,"wildemitter":31}],4:[function(require,module,exports){
"use strict";
/* global module */
var WebRTC = require('./webrtc');
var WildEmitter = require('wildemitter');
var webrtcSupport = require('webrtcsupport');
var attachMediaStream = require('attachmediastream');
var mockconsole = require('mockconsole');
function SimpleWebRTC(opts) {
var self = this;
var options = opts || {};
var config = this.config = {
socketio: {
/* 'force new connection':true*/
},
connection: null,
debug: false,
localVideoEl: '',
remoteVideosEl: '',
enableDataChannels: true,
autoRequestMedia: false,
autoRemoveVideos: true,
adjustPeerVolume: false,
peerVolumeWhenSpeaking: 0.25,
media: {
video: true,
audio: true
},
receiveMedia: {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
},
localVideo: {
autoplay: true,
mirror: true,
muted: true
}
};
var item, connection; // We also allow a 'logger' option. It can be any object that implements
// log, warn, and error methods.
// We log nothing by default, following "the rule of silence":
// http://www.linfo.org/rule_of_silence.html
this.logger = function () {
// we assume that if you're in debug mode and you didn't
// pass in a logger, you actually want to log as much as
// possible.
if (opts.debug) {
return opts.logger || console;
} else {
// or we'll use your logger which should have its own logic
// for output. Or we'll return the no-op.
return opts.logger || mockconsole;
}
}(); // set our config from options
for (item in options) {
if (options.hasOwnProperty(item)) {
this.config[item] = options[item];
}
} // Override screensharing support detection to fit the custom
// "getScreenMedia" module.
// Note that this is a coarse check; calling "getScreenMedia" may fail even
// if "supportScreenSharing" is true.
var screenSharingSupported = window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia || window.navigator.webkitGetUserMedia || window.navigator.userAgent.match('Firefox');
webrtcSupport.supportScreenSharing = window.location.protocol === 'https:' && screenSharingSupported; // attach detected support for convenience
this.capabilities = webrtcSupport; // call WildEmitter constructor
WildEmitter.call(this);
if (this.config.connection === null) {
throw 'no connection object given in the configuration';
} else {
connection = this.connection = this.config.connection;
}
connection.on('connect', function () {
self.emit('connectionReady', connection.getSessionid());
self.sessionReady = true;
self.testReadiness();
});
connection.on('message', function (message) {
var peers = self.webrtc.getPeers(message.from, message.roomType);
var peer;
if (message.type === 'offer') {
if (peers.length) {
peers.forEach(function (p) {
if (p.sid === message.sid) {
peer = p;
}
}); //if (!peer) peer = peers[0]; // fallback for old protocol versions
}
if (!peer) {
peer = self.webrtc.createPeer({
id: message.from,
sid: message.sid,
type: message.roomType,
enableDataChannels: self.config.enableDataChannels && message.roomType !== 'screen',
sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null,
sendVideoIfAvailable: self.connection.getSendVideoIfAvailable()
});
self.emit('createdPeer', peer);
}
peer.handleMessage(message);
} else if (peers.length) {
peers.forEach(function (peer) {
if (message.sid) {
if (peer.sid === message.sid) {
peer.handleMessage(message);
}
} else {
peer.handleMessage(message);
}
});
}
});
connection.on('remove', function (room) {
if (room.id !== self.connection.getSessionid()) {
self.webrtc.removePeers(room.id, room.type);
}
}); // instantiate our main WebRTC helper
// using same logger from logic here
opts.logger = this.logger;
opts.debug = false;
this.webrtc = new WebRTC(opts); // attach a few methods from underlying lib to simple.
['mute', 'unmute', 'pauseVideo', 'resumeVideo', 'pause', 'resume', 'sendToAll', 'sendDirectlyToAll', 'getPeers', 'createPeer', 'removePeers'].forEach(function (method) {
self[method] = self.webrtc[method].bind(self.webrtc);
}); // proxy events from WebRTC
this.webrtc.on('*', function () {
self.emit.apply(self, arguments);
}); // log all events in debug mode
if (config.debug) {
this.on('*', this.logger.log.bind(this.logger, 'SimpleWebRTC event:'));
} // check for readiness
this.webrtc.on('localStream', function () {
self.testReadiness();
});
this.webrtc.on('message', function (payload) {
self.connection.emit('message', payload);
});
this.webrtc.on('peerStreamAdded', this.handlePeerStreamAdded.bind(this));
this.webrtc.on('peerStreamRemoved', this.handlePeerStreamRemoved.bind(this)); // echo cancellation attempts
if (this.config.adjustPeerVolume) {
this.webrtc.on('speaking', this.setVolumeForAll.bind(this, this.config.peerVolumeWhenSpeaking));
this.webrtc.on('stoppedSpeaking', this.setVolumeForAll.bind(this, 1));
}
connection.on('stunservers', function (args) {
// resets/overrides the config
self.webrtc.config.peerConnectionConfig.iceServers = args;
self.emit('stunservers', args);
});
connection.on('turnservers', function (args) {
// appends to the config
self.webrtc.config.peerConnectionConfig.iceServers = self.webrtc.config.peerConnectionConfig.iceServers.concat(args);
self.emit('turnservers', args);
});
this.webrtc.on('iceFailed', function ()
/*peer*/
{// local ice failure
});
this.webrtc.on('connectivityError', function ()
/*peer*/
{// remote ice failure
}); // sending mute/unmute to all peers
this.webrtc.on('audioOn', function () {
self.webrtc.sendToAll('unmute', {
name: 'audio'
});
});
this.webrtc.on('audioOff', function () {
self.webrtc.sendToAll('mute', {
name: 'audio'
});
});
this.webrtc.on('videoOn', function () {
self.webrtc.sendToAll('unmute', {
name: 'video'
});
});
this.webrtc.on('videoOff', function () {
self.webrtc.sendToAll('mute', {
name: 'video'
});
}); // screensharing events
this.webrtc.on('localScreen', function (stream) {
var el = document.createElement('video'),
container = self.getRemoteVideoContainer();
el.oncontextmenu = function () {
return false;
};
el.id = 'localScreen';
attachMediaStream(stream, el);
if (container) {
container.appendChild(el);
}
self.emit('localScreenAdded', el);
self.connection.emit('shareScreen'); // NOTE: we don't create screen peers for existing video peers here,
// this is done by the application code in "webrtc.js".
});
this.webrtc.on('localScreenStopped', function ()
/*stream*/
{
self.stopScreenShare();
/*
self.connection.emit('unshareScreen');
self.webrtc.peers.forEach(function (peer) {
if (peer.sharemyscreen) {
peer.end();
}
});
*/
});
}
SimpleWebRTC.prototype = Object.create(WildEmitter.prototype, {
constructor: {
value: SimpleWebRTC
}
});
SimpleWebRTC.prototype.leaveCall = function () {
if (this.roomName) {
while (this.webrtc.peers.length) {
this.webrtc.peers[0].end();
}
if (this.getLocalScreen()) {
this.stopScreenShare();
}
this.emit('leftRoom', this.roomName);
this.stopLocalVideo();
this.roomName = undefined;
}
};
SimpleWebRTC.prototype.disconnect = function () {
this.connection.disconnect();
delete this.connection;
};
SimpleWebRTC.prototype.handlePeerStreamAdded = function (peer) {
var self = this;
var container = this.getRemoteVideoContainer();
var video = attachMediaStream(peer.stream); // At least Firefox, Opera and Edge move the video to a wrong position
// instead of keeping it unchanged when "transform: scaleX(1)" is used
// ("transform: scaleX(-1)" is fine); as it should have no effect the
// transform is removed.
if (video.style.transform === 'scaleX(1)') {
video.style.transform = '';
} // store video element as part of peer for easy removal
peer.videoEl = video;
video.id = this.getDomId(peer);
if (container) {
container.appendChild(video);
}
this.emit('videoAdded', video, peer); // send our mute status to new peer if we're muted
// currently called with a small delay because it arrives before
// the video element is created otherwise (which happens after
// the async setRemoteDescription-createAnswer)
window.setTimeout(function () {
if (!self.webrtc.isAudioEnabled()) {
peer.send('mute', {
name: 'audio'
});
}
if (!self.webrtc.isVideoEnabled()) {
peer.send('mute', {
name: 'video'
});
}
}, 250);
};
SimpleWebRTC.prototype.handlePeerStreamRemoved = function (peer) {
var container = this.getRemoteVideoContainer();
var videoEl = peer.videoEl;
if (this.config.autoRemoveVideos && container && videoEl) {
container.removeChild(videoEl);
}
if (videoEl) {
this.emit('videoRemoved', videoEl, peer);
}
};
SimpleWebRTC.prototype.getDomId = function (peer) {
return [peer.id, peer.type, peer.broadcaster ? 'broadcasting' : 'incoming'].join('_');
}; // set volume on video tag for all peers takse a value between 0 and 1
SimpleWebRTC.prototype.setVolumeForAll = function (volume) {
this.webrtc.peers.forEach(function (peer) {
if (peer.videoEl) {
peer.videoEl.volume = volume;
}
});
};
SimpleWebRTC.prototype.joinCall = function (name) {
if (this.config.autoRequestMedia) {
this.startLocalVideo();
}
this.roomName = name;
this.emit('joinedRoom', name);
};
SimpleWebRTC.prototype.getEl = function (idOrEl) {
if (typeof idOrEl === 'string') {
return document.getElementById(idOrEl);
} else {
return idOrEl;
}
};
SimpleWebRTC.prototype.startLocalVideo = function () {
var self = this;
this.webrtc.start(this.config.media, function (err, stream) {
if (err) {
self.emit('localMediaError', err);
} else {
self.emit('localMediaStarted', self.config.media);
attachMediaStream(stream, self.getLocalVideoContainer(), self.config.localVideo);
}
});
};
SimpleWebRTC.prototype.stopLocalVideo = function () {
this.webrtc.stop();
}; // this accepts either element ID or element
// and either the video tag itself or a container
// that will be used to put the video tag into.
SimpleWebRTC.prototype.getLocalVideoContainer = function () {
var el = this.getEl(this.config.localVideoEl);
if (el && el.tagName === 'VIDEO') {
el.oncontextmenu = function () {
return false;
};
return el;
} else if (el) {
var video = document.createElement('video');
video.oncontextmenu = function () {
return false;
};
el.appendChild(video);
return video;
} else {
return;
}
};
SimpleWebRTC.prototype.getRemoteVideoContainer = function () {
return this.getEl(this.config.remoteVideosEl);
};
SimpleWebRTC.prototype.shareScreen = function (mode, cb) {
this.webrtc.startScreenShare(mode, cb);
};
SimpleWebRTC.prototype.getLocalScreen = function () {
return this.webrtc.localScreen;
};
SimpleWebRTC.prototype.stopScreenShare = function () {
this.connection.emit('unshareScreen');
var videoEl = document.getElementById('localScreen');
var container = this.getRemoteVideoContainer();
if (this.config.autoRemoveVideos && container && videoEl) {
container.removeChild(videoEl);
} // a hack to emit the event the removes the video
// element that we want
if (videoEl) {
this.emit('videoRemoved', videoEl);
}
if (this.getLocalScreen()) {
this.webrtc.stopScreenShare();
} // Notify peers were sending to.
this.webrtc.peers.forEach(function (peer) {
if (peer.type === 'screen' && peer.sharemyscreen) {
peer.send('unshareScreen');
}
if (peer.broadcaster) {
peer.end();
}
});
};
SimpleWebRTC.prototype.testReadiness = function () {
var self = this;
if (this.sessionReady) {
if (!this.config.media.video && !this.config.media.audio) {
self.emit('readyToCall', self.connection.getSessionid());
} else if (this.webrtc.localStreams.length > 0) {
self.emit('readyToCall', self.connection.getSessionid());
}
}
};
SimpleWebRTC.prototype.createRoom = function (name, cb) {
this.roomName = name;
if (arguments.length === 2) {
this.connection.emit('create', name, cb);
} else {
this.connection.emit('create', name);
}
};
module.exports = SimpleWebRTC;
},{"./webrtc":5,"attachmediastream":6,"mockconsole":11,"webrtcsupport":30,"wildemitter":31}],5:[function(require,module,exports){
"use strict";
/* global module */
var util = require('util');
var webrtcSupport = require('webrtcsupport');
var mockconsole = require('mockconsole');
var localMedia = require('./localmedia');
var Peer = require('./peer');
function WebRTC(opts) {
var self = this;
var options = opts || {};
this.config = {
debug: false,
// makes the entire PC config overridable
peerConnectionConfig: {
iceServers: []
},
receiveMedia: {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
},
enableDataChannels: true
};
var item; // We also allow a 'logger' option. It can be any object that implements
// log, warn, and error methods.
// We log nothing by default, following "the rule of silence":
// http://www.linfo.org/rule_of_silence.html
this.logger = function () {
// we assume that if you're in debug mode and you didn't
// pass in a logger, you actually want to log as much as
// possible.
if (opts.debug) {
return opts.logger || console;
} else {
// or we'll use your logger which should have its own logic
// for output. Or we'll return the no-op.
return opts.logger || mockconsole;
}
}(); // set options
for (item in options) {
if (options.hasOwnProperty(item)) {
this.config[item] = options[item];
}
} // check for support
if (!webrtcSupport.support) {
this.logger.error('Your browser doesn\'t seem to support WebRTC');
} // where we'll store our peer connections
this.peers = []; // call localMedia constructor
localMedia.call(this, this.config);
this.on('speaking', function () {
if (!self.hardMuted) {
// FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload
self.peers.forEach(function (peer) {
if (peer.enableDataChannels) {
var dc = peer.getDataChannel('hark');
if (dc.readyState !== 'open') {
return;
}
dc.send(JSON.stringify({
type: 'speaking'
}));
}
});
}
});
this.on('stoppedSpeaking', function () {
if (!self.hardMuted) {
// FIXME: should use sendDirectlyToAll, but currently has different semantics wrt payload
self.peers.forEach(function (peer) {
if (peer.enableDataChannels) {
var dc = peer.getDataChannel('hark');
if (dc.readyState !== 'open') {
return;
}
dc.send(JSON.stringify({
type: 'stoppedSpeaking'
}));
}
});
}
});
this.on('unshareScreen', function (message) {
// End peers we were receiving the screensharing stream from.
var peers = self.getPeers(message.from, 'screen');
peers.forEach(function (peer) {
if (!peer.sharemyscreen) {
peer.end();
}
});
}); // log events in debug mode
if (this.config.debug) {
this.on('*', function (event, val1, val2) {
var logger; // if you didn't pass in a logger and you explicitly turning on debug
// we're just going to assume you're wanting log output with console
if (self.config.logger === mockconsole) {
logger = console;
} else {
logger = self.logger;
}
logger.log('event:', event, val1, val2);
});
}
}
util.inherits(WebRTC, localMedia);
WebRTC.prototype.createPeer = function (opts) {
var peer;
opts.parent = this;
peer = new Peer(opts);
this.peers.push(peer);
return peer;
}; // removes peers
WebRTC.prototype.removePeers = function (id, type) {
this.getPeers(id, type).forEach(function (peer) {
peer.end();
});
}; // fetches all Peer objects by session id and/or type
WebRTC.prototype.getPeers = function (sessionId, type) {
return this.peers.filter(function (peer) {
return (!sessionId || peer.id === sessionId) && (!type || peer.type === type);
});
}; // sends message to all
WebRTC.prototype.sendToAll = function (message, payload) {
this.peers.forEach(function (peer) {
peer.send(message, payload);
});
}; // sends message to all using a datachannel
// only sends to anyone who has an open datachannel
WebRTC.prototype.sendDirectlyToAll = function (channel, message, payload) {
this.peers.forEach(function (peer) {
if (peer.enableDataChannels) {
peer.sendDirectly(channel, message, payload);
}
});
};
module.exports = WebRTC;
},{"./localmedia":2,"./peer":3,"mockconsole":11,"util":8,"webrtcsupport":30}],6:[function(require,module,exports){
"use strict";
var adapter = require('webrtc-adapter');
module.exports = function (stream, el, options) {
var item;
var element = el;
var opts = {
autoplay: true,
mirror: false,
muted: false,
audio: false,
disableContextMenu: false
};
if (options) {
for (item in options) {
opts[item] = options[item];
}
}
if (!element) {
element = document.createElement(opts.audio ? 'audio' : 'video');
} else if (element.tagName.toLowerCase() === 'audio') {
opts.audio = true;
}
if (opts.disableContextMenu) {
element.oncontextmenu = function (e) {
e.preventDefault();
};
}
if (opts.autoplay) element.autoplay = 'autoplay';
element.muted = !!opts.muted;
if (!opts.audio) {
['', 'moz', 'webkit', 'o', 'ms'].forEach(function (prefix) {
var styleName = prefix ? prefix + 'Transform' : 'transform';
element.style[styleName] = opts.mirror ? 'scaleX(-1)' : 'scaleX(1)';
});
}
if (adapter.browserDetails.browser === 'safari') {
element.setAttribute('playsinline', true);
}
element.srcObject = stream;
return element;
};
},{"webrtc-adapter":15}],7:[function(require,module,exports){
"use strict";
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
module.exports = function isBuffer(arg) {
return arg && _typeof(arg) === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function';
};
},{}],8:[function(require,module,exports){
(function (process,global){
"use strict";
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var formatRegExp = /%[sdj%]/g;
exports.format = function (f) {
if (!isString(f)) {
var objects = [];
for (var i = 0; i < arguments.length; i++) {
objects.push(inspect(arguments[i]));
}
return objects.join(' ');
}
var i = 1;
var args = arguments;
var len = args.length;
var str = String(f).replace(formatRegExp, function (x) {
if (x === '%%') return '%';
if (i >= len) return x;
switch (x) {
case '%s':
return String(args[i++]);
case '%d':
return Number(args[i++]);
case '%j':
try {
return JSON.stringify(args[i++]);
} catch (_) {
return '[Circular]';
}
default:
return x;
}
});
for (var x = args[i]; i < len; x = args[++i]) {
if (isNull(x) || !isObject(x)) {
str += ' ' + x;
} else {
str += ' ' + inspect(x);
}
}
return str;
}; // Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function (fn, msg) {
// Allow for deprecating things in the process of starting up.
if (isUndefined(global.process)) {
return function () {
return exports.deprecate(fn, msg).apply(this, arguments);
};
}
if (process.noDeprecation === true) {
return fn;
}
var warned = false;
function deprecated() {
if (!warned) {
if (process.throwDeprecation) {
throw new Error(msg);
} else if (process.traceDeprecation) {
console.trace(msg);
} else {
console.error(msg);
}
warned = true;
}
return fn.apply(this, arguments);
}
return deprecated;
};
var debugs = {};
var debugEnviron;
exports.debuglog = function (set) {
if (isUndefined(debugEnviron)) debugEnviron = process.env.NODE_DEBUG || '';
set = set.toUpperCase();
if (!debugs[set]) {
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
var pid = process.pid;
debugs[set] = function () {
var msg = exports.format.apply(exports, arguments);
console.error('%s %d: %s', set, pid, msg);
};
} else {
debugs[set] = function () {};
}
}
return debugs[set];
};
/**
* Echos the value of a value. Trys to print the value out
* in the best way possible given the different types.
*
* @param {Object} obj The object to print out.
* @param {Object} opts Optional options object that alters the output.
*/
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
// default options
var ctx = {
seen: [],
stylize: stylizeNoColor
}; // legacy...
if (arguments.length >= 3) ctx.depth = arguments[2];
if (arguments.length >= 4) ctx.colors = arguments[3];
if (isBoolean(opts)) {
// legacy...
ctx.showHidden = opts;
} else if (opts) {
// got an "options" object
exports._extend(ctx, opts);
} // set default options
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
if (isUndefined(ctx.depth)) ctx.depth = 2;
if (isUndefined(ctx.colors)) ctx.colors = false;
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
if (ctx.colors) ctx.stylize = stylizeWithColor;
return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect; // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
'bold': [1, 22],
'italic': [3, 23],
'underline': [4, 24],
'inverse': [7, 27],
'white': [37, 39],
'grey': [90, 39],
'black': [30, 39],
'blue': [34, 39],
'cyan': [36, 39],
'green': [32, 39],
'magenta': [35, 39],
'red': [31, 39],
'yellow': [33, 39]
}; // Don't use 'blue' not visible on cmd.exe
inspect.styles = {
'special': 'cyan',
'number': 'yellow',
'boolean': 'yellow',
'undefined': 'grey',
'null': 'bold',
'string': 'green',
'date': 'magenta',
// "name": intentionally not styling
'regexp': 'red'
};
function stylizeWithColor(str, styleType) {
var style = inspect.styles[styleType];
if (style) {
return "\x1B[" + inspect.colors[style][0] + 'm' + str + "\x1B[" + inspect.colors[style][1] + 'm';
} else {
return str;
}
}
function stylizeNoColor(str, styleType) {
return str;
}
function arrayToHash(array) {
var hash = {};
array.forEach(function (val, idx) {
hash[val] = true;
});
return hash;
}
function formatValue(ctx, value, recurseTimes) {
// Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it
if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special
value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check.
!(value.constructor && value.constructor.prototype === value)) {
var ret = value.inspect(recurseTimes, ctx);
if (!isString(ret)) {
ret = formatValue(ctx, ret, recurseTimes);
}
return ret;
} // Primitive types cannot have properties
var primitive = formatPrimitive(ctx, value);
if (primitive) {
return primitive;
} // Look up the keys of the object.
var keys = Object.keys(value);
var visibleKeys = arrayToHash(keys);
if (ctx.showHidden) {
keys = Object.getOwnPropertyNames(value);
} // IE doesn't make error fields non-enumerable
// http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
return formatError(value);
} // Some type of object without properties can be shortcutted.
if (keys.length === 0) {
if (isFunction(value)) {
var name = value.name ? ': ' + value.name : '';
return ctx.stylize('[Function' + name + ']', 'special');
}
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
}
if (isDate(value)) {
return ctx.stylize(Date.prototype.toString.call(value), 'date');
}
if (isError(value)) {
return formatError(value);
}
}
var base = '',
array = false,
braces = ['{', '}']; // Make Array say that they are Array
if (isArray(value)) {
array = true;
braces = ['[', ']'];
} // Make functions say that they are functions
if (isFunction(value)) {
var n = value.name ? ': ' + value.name : '';
base = ' [Function' + n + ']';
} // Make RegExps say that they are RegExps
if (isRegExp(value)) {
base = ' ' + RegExp.prototype.toString.call(value);
} // Make dates with properties first say the date
if (isDate(value)) {
base = ' ' + Date.prototype.toUTCString.call(value);
} // Make error with message first say the error
if (isError(value)) {
base = ' ' + formatError(value);
}
if (keys.length === 0 && (!array || value.length == 0)) {
return braces[0] + base + braces[1];
}
if (recurseTimes < 0) {
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
} else {
return ctx.stylize('[Object]', 'special');
}
}
ctx.seen.push(value);
var output;
if (array) {
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
} else {
output = keys.map(function (key) {
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
});
}
ctx.seen.pop();
return reduceToSingleString(output, base, braces);
}
function formatPrimitive(ctx, value) {
if (isUndefined(value)) return ctx.stylize('undefined', 'undefined');
if (isString(value)) {
var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '').replace(/'/g, "\\'").replace(/\\"/g, '"') + '\'';
return ctx.stylize(simple, 'string');
}
if (isNumber(value)) return ctx.stylize('' + value, 'number');
if (isBoolean(value)) return ctx.stylize('' + value, 'boolean'); // For some reason typeof null is "object", so special case here.
if (isNull(value)) return ctx.stylize('null', 'null');
}
function formatError(value) {
return '[' + Error.prototype.toString.call(value) + ']';
}
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
var output = [];
for (var i = 0, l = value.length; i < l; ++i) {
if (hasOwnProperty(value, String(i))) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true));
} else {
output.push('');
}
}
keys.forEach(function (key) {
if (!key.match(/^\d+$/)) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true));
}
});
return output;
}
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
var name, str, desc;
desc = Object.getOwnPropertyDescriptor(value, key) || {
value: value[key]
};
if (desc.get) {
if (desc.set) {
str = ctx.stylize('[Getter/Setter]', 'special');
} else {
str = ctx.stylize('[Getter]', 'special');
}
} else {
if (desc.set) {
str = ctx.stylize('[Setter]', 'special');
}
}
if (!hasOwnProperty(visibleKeys, key)) {
name = '[' + key + ']';
}
if (!str) {
if (ctx.seen.indexOf(desc.value) < 0) {
if (isNull(recurseTimes)) {
str = formatValue(ctx, desc.value, null);
} else {
str = formatValue(ctx, desc.value, recurseTimes - 1);
}
if (str.indexOf('\n') > -1) {
if (array) {
str = str.split('\n').map(function (line) {
return ' ' + line;
}).join('\n').substr(2);
} else {
str = '\n' + str.split('\n').map(function (line) {
return ' ' + line;
}).join('\n');
}
}
} else {
str = ctx.stylize('[Circular]', 'special');
}
}
if (isUndefined(name)) {
if (array && key.match(/^\d+$/)) {
return str;
}
name = JSON.stringify('' + key);
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
name = name.substr(1, name.length - 2);
name = ctx.stylize(name, 'name');
} else {
name = name.replace(/'/g, "\\'").replace(/\\"/g, '"').replace(/(^"|"$)/g, "'");
name = ctx.stylize(name, 'string');
}
}
return name + ': ' + str;
}
function reduceToSingleString(output, base, braces) {
var numLinesEst = 0;
var length = output.reduce(function (prev, cur) {
numLinesEst++;
if (cur.indexOf('\n') >= 0) numLinesEst++;
return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
}, 0);
if (length > 60) {
return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1];
}
return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
} // NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return _typeof(arg) === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return _typeof(arg) === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || _typeof(arg) === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
exports.isBuffer = require('./support/isBuffer');
function objectToString(o) {
return Object.prototype.toString.call(o);
}
function pad(n) {
return n < 10 ? '0' + n.toString(10) : n.toString(10);
}
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // 26 Feb 16:19:34
function timestamp() {
var d = new Date();
var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':');
return [d.getDate(), months[d.getMonth()], time].join(' ');
} // log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function () {
console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};
/**
* Inherit the prototype methods from one constructor into another.
*
* The Function.prototype.inherits from lang.js rewritten as a standalone
* function (not on Function.prototype). NOTE: If this file is to be loaded
* during bootstrapping this function needs to be rewritten using some native
* functions as prototype setup using normal JavaScript does not work as
* expected during bootstrapping (see mirror.js in r114903).
*
* @param {function} ctor Constructor function which needs to inherit the
* prototype.
* @param {function} superCtor Constructor function to inherit prototype from.
*/
exports.inherits = require('inherits');
exports._extend = function (origin, add) {
// Don't do anything if add isn't an object
if (!add || !isObject(add)) return origin;
var keys = Object.keys(add);
var i = keys.length;
while (i--) {
origin[keys[i]] = add[keys[i]];
}
return origin;
};
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":7,"_process":12,"inherits":10}],9:[function(require,module,exports){
"use strict";
var WildEmitter = require('wildemitter');
function getMaxVolume(analyser, fftBins) {
var maxVolume = -Infinity;
analyser.getFloatFrequencyData(fftBins);
for (var i = 4, ii = fftBins.length; i < ii; i++) {
if (fftBins[i] > maxVolume && fftBins[i] < 0) {
maxVolume = fftBins[i];
}
}
;
return maxVolume;
}
var audioContextType;
if (typeof window !== 'undefined') {
audioContextType = window.AudioContext || window.webkitAudioContext;
} // use a single audio context due to hardware limits
var audioContext = null;
module.exports = function (stream, options) {
var harker = new WildEmitter(); // make it not break in non-supported browsers
if (!audioContextType) return harker; //Config
var options = options || {},
smoothing = options.smoothing || 0.1,
interval = options.interval || 50,
threshold = options.threshold,
play = options.play,
history = options.history || 10,
running = true; // Ensure that just a single AudioContext is internally created
audioContext = options.audioContext || audioContext || new audioContextType();
var sourceNode, fftBins, analyser;
analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.smoothingTimeConstant = smoothing;
fftBins = new Float32Array(analyser.frequencyBinCount);
if (stream.jquery) stream = stream[0];
if (stream instanceof HTMLAudioElement || stream instanceof HTMLVideoElement) {
//Audio Tag
sourceNode = audioContext.createMediaElementSource(stream);
if (typeof play === 'undefined') play = true;
threshold = threshold || -50;
} else {
//WebRTC Stream
sourceNode = audioContext.createMediaStreamSource(stream);
threshold = threshold || -50;
}
sourceNode.connect(analyser);
if (play) analyser.connect(audioContext.destination);
harker.speaking = false;
harker.suspend = function () {
return audioContext.suspend();
};
harker.resume = function () {
return audioContext.resume();
};
Object.defineProperty(harker, 'state', {
get: function get() {
return audioContext.state;
}
});
audioContext.onstatechange = function () {
harker.emit('state_change', audioContext.state);
};
harker.setThreshold = function (t) {
threshold = t;
};
harker.setInterval = function (i) {
interval = i;
};
harker.stop = function () {
running = false;
harker.emit('volume_change', -100, threshold);
if (harker.speaking) {
harker.speaking = false;
harker.emit('stopped_speaking');
}
analyser.disconnect();
sourceNode.disconnect();
};
harker.speakingHistory = [];
for (var i = 0; i < history; i++) {
harker.speakingHistory.push(0);
} // Poll the analyser node to determine if speaking
// and emit events if changed
var looper = function looper() {
setTimeout(function () {
//check if stop has been called
if (!running) {
return;
}
var currentVolume = getMaxVolume(analyser, fftBins);
harker.emit('volume_change', currentVolume, threshold);
var history = 0;
if (currentVolume > threshold && !harker.speaking) {
// trigger quickly, short history
for (var i = harker.speakingHistory.length - 3; i < harker.speakingHistory.length; i++) {
history += harker.speakingHistory[i];
}
if (history >= 2) {
harker.speaking = true;
harker.emit('speaking');
}
} else if (currentVolume < threshold && harker.speaking) {
for (var i = 0; i < harker.speakingHistory.length; i++) {
history += harker.speakingHistory[i];
}
if (history == 0) {
harker.speaking = false;
harker.emit('stopped_speaking');
}
}
harker.speakingHistory.shift();
harker.speakingHistory.push(0 + (currentVolume > threshold));
looper();
}, interval);
};
looper();
return harker;
};
},{"wildemitter":31}],10:[function(require,module,exports){
"use strict";
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor;
var TempCtor = function TempCtor() {};
TempCtor.prototype = superCtor.prototype;
ctor.prototype = new TempCtor();
ctor.prototype.constructor = ctor;
};
}
},{}],11:[function(require,module,exports){
"use strict";
var methods = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(",");
var l = methods.length;
var fn = function fn() {};
var mockconsole = {};
while (l--) {
mockconsole[methods[l]] = fn;
}
module.exports = mockconsole;
},{}],12:[function(require,module,exports){
"use strict";
// shim for using process in browser
var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it
// don't break things. But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals. It's inside a
// function because try/catches deoptimize in certain engines.
var cachedSetTimeout;
var cachedClearTimeout;
function defaultSetTimout() {
throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout() {
throw new Error('clearTimeout has not been defined');
}
(function () {
try {
if (typeof setTimeout === 'function') {
cachedSetTimeout = setTimeout;
} else {
cachedSetTimeout = defaultSetTimout;
}
} catch (e) {
cachedSetTimeout = defaultSetTimout;
}
try {
if (typeof clearTimeout === 'function') {
cachedClearTimeout = clearTimeout;
} else {
cachedClearTimeout = defaultClearTimeout;
}
} catch (e) {
cachedClearTimeout = defaultClearTimeout;
}
})();
function runTimeout(fun) {
if (cachedSetTimeout === setTimeout) {
//normal enviroments in sane situations
return setTimeout(fun, 0);
} // if setTimeout wasn't available but was latter defined
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
cachedSetTimeout = setTimeout;
return setTimeout(fun, 0);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedSetTimeout(fun, 0);
} catch (e) {
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedSetTimeout.call(null, fun, 0);
} catch (e) {
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
return cachedSetTimeout.call(this, fun, 0);
}
}
}
function runClearTimeout(marker) {
if (cachedClearTimeout === clearTimeout) {
//normal enviroments in sane situations
return clearTimeout(marker);
} // if clearTimeout wasn't available but was latter defined
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
cachedClearTimeout = clearTimeout;
return clearTimeout(marker);
}
try {
// when when somebody has screwed with setTimeout but no I.E. maddness
return cachedClearTimeout(marker);
} catch (e) {
try {
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
return cachedClearTimeout.call(null, marker);
} catch (e) {
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
return cachedClearTimeout.call(this, marker);
}
}
}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
if (!draining || !currentQueue) {
return;
}
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = runTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while (len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
runClearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
runTimeout(drainQueue);
}
}; // v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;
process.listeners = function (name) {
return [];
};
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () {
return '/';
};
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function () {
return 0;
};
},{}],13:[function(require,module,exports){
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
var SDPUtils = require('sdp');
function fixStatsType(stat) {
return {
inboundrtp: 'inbound-rtp',
outboundrtp: 'outbound-rtp',
candidatepair: 'candidate-pair',
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[stat.type] || stat.type;
}
function writeMediaSection(transceiver, caps, type, stream, dtlsRole) {
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' : dtlsRole || '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';
}
if (transceiver.rtpSender) {
var trackId = transceiver.rtpSender._initialTrackId || transceiver.rtpSender.track.id;
transceiver.rtpSender._initialTrackId = trackId; // spec.
var msid = 'msid:' + (stream ? stream.id : '-') + ' ' + trackId + '\r\n';
sdp += 'a=' + msid; // for Chrome. Legacy should no longer be required.
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + ' ' + msid; // RTX
if (transceiver.sendEncodingParameters[0].rtx) {
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + ' ' + msid;
sdp += 'a=ssrc-group:FID ' + transceiver.sendEncodingParameters[0].ssrc + ' ' + transceiver.sendEncodingParameters[0].rtx.ssrc + '\r\n';
}
} // FIXME: this should be written by writeRtpDescription.
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + ' cname:' + SDPUtils.localCName + '\r\n';
if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + ' cname:' + SDPUtils.localCName + '\r\n';
}
return sdp;
} // Edge does not like
// 1) stun: filtered after 14393 unless ?transport=udp is present
// 2) turn: that does not have all of turn:host:port?transport=udp
// 3) turn: with ipv6 addresses
// 4) turn: occurring muliple times
function filterIceServers(iceServers, edgeVersion) {
var hasTurn = false;
iceServers = JSON.parse(JSON.stringify(iceServers));
return iceServers.filter(function (server) {
if (server && (server.urls || server.url)) {
var urls = server.urls || server.url;
if (server.url && !server.urls) {
console.warn('RTCIceServer.url is deprecated! Use urls instead.');
}
var isString = typeof urls === 'string';
if (isString) {
urls = [urls];
}
urls = urls.filter(function (url) {
var validTurn = url.indexOf('turn:') === 0 && url.indexOf('transport=udp') !== -1 && url.indexOf('turn:[') === -1 && !hasTurn;
if (validTurn) {
hasTurn = true;
return true;
}
return url.indexOf('stun:') === 0 && edgeVersion >= 14393 && url.indexOf('?transport=udp') === -1;
});
delete server.url;
server.urls = isString ? urls[0] : urls;
return !!urls.length;
}
});
} // Determines the intersection of local and remote capabilities.
function getCommonCapabilities(localCapabilities, remoteCapabilities) {
var commonCapabilities = {
codecs: [],
headerExtensions: [],
fecMechanisms: []
};
var findCodecByPayloadType = function findCodecByPayloadType(pt, codecs) {
pt = parseInt(pt, 10);
for (var i = 0; i < codecs.length; i++) {
if (codecs[i].payloadType === pt || codecs[i].preferredPayloadType === pt) {
return codecs[i];
}
}
};
var rtxCapabilityMatches = function rtxCapabilityMatches(lRtx, rRtx, lCodecs, rCodecs) {
var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
return lCodec && rCodec && lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
};
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) {
if (lCodec.name.toLowerCase() === 'rtx' && lCodec.parameters && rCodec.parameters.apt) {
// for RTX we need to find the local rtx that has a apt
// which points to the same local codec as the remote one.
if (!rtxCapabilityMatches(lCodec, rCodec, localCapabilities.codecs, remoteCapabilities.codecs)) {
continue;
}
}
rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
// number of channels is the highest common number of channels
rCodec.numChannels = Math.min(lCodec.numChannels, rCodec.numChannels); // push rCodec so we reply with offerer payload type
commonCapabilities.codecs.push(rCodec); // determine common feedback mechanisms
rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function (fb) {
for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
if (lCodec.rtcpFeedback[j].type === fb.type && lCodec.rtcpFeedback[j].parameter === fb.parameter) {
return true;
}
}
return false;
}); // FIXME: also need to determine .parameters
// see https://github.com/openpeer/ortc/issues/569
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;
} // is action=setLocalDescription with type allowed in signalingState
function isActionAllowedInSignalingState(action, type, signalingState) {
return {
offer: {
setLocalDescription: ['stable', 'have-local-offer'],
setRemoteDescription: ['stable', 'have-remote-offer']
},
answer: {
setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
}
}[type][action].indexOf(signalingState) !== -1;
}
function maybeAddCandidate(iceTransport, candidate) {
// Edge's internal representation adds some fields therefore
// not all fieldѕ are taken into account.
var alreadyAdded = iceTransport.getRemoteCandidates().find(function (remoteCandidate) {
return candidate.foundation === remoteCandidate.foundation && candidate.ip === remoteCandidate.ip && candidate.port === remoteCandidate.port && candidate.priority === remoteCandidate.priority && candidate.protocol === remoteCandidate.protocol && candidate.type === remoteCandidate.type;
});
if (!alreadyAdded) {
iceTransport.addRemoteCandidate(candidate);
}
return !alreadyAdded;
}
function makeError(name, description) {
var e = new Error(description);
e.name = name; // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names
e.code = {
NotSupportedError: 9,
InvalidStateError: 11,
InvalidAccessError: 15,
TypeError: undefined,
OperationError: undefined
}[name];
return e;
}
module.exports = function (window, edgeVersion) {
// https://w3c.github.io/mediacapture-main/#mediastream
// Helper function to add the track to the stream and
// dispatch the event ourselves.
function addTrackToStreamAndFireEvent(track, stream) {
stream.addTrack(track);
stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack', {
track: track
}));
}
function removeTrackFromStreamAndFireEvent(track, stream) {
stream.removeTrack(track);
stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack', {
track: track
}));
}
function fireAddTrack(pc, track, receiver, streams) {
var trackEvent = new Event('track');
trackEvent.track = track;
trackEvent.receiver = receiver;
trackEvent.transceiver = {
receiver: receiver
};
trackEvent.streams = streams;
window.setTimeout(function () {
pc._dispatchEvent('track', trackEvent);
});
}
var RTCPeerConnection = function RTCPeerConnection(config) {
var pc = this;
var _eventTarget = document.createDocumentFragment();
['addEventListener', 'removeEventListener', 'dispatchEvent'].forEach(function (method) {
pc[method] = _eventTarget[method].bind(_eventTarget);
});
this.canTrickleIceCandidates = null;
this.needNegotiation = false;
this.localStreams = [];
this.remoteStreams = [];
this._localDescription = null;
this._remoteDescription = null;
this.signalingState = 'stable';
this.iceConnectionState = 'new';
this.connectionState = 'new';
this.iceGatheringState = 'new';
config = JSON.parse(JSON.stringify(config || {}));
this.usingBundle = config.bundlePolicy === 'max-bundle';
if (config.rtcpMuxPolicy === 'negotiate') {
throw makeError('NotSupportedError', 'rtcpMuxPolicy \'negotiate\' is not supported');
} else if (!config.rtcpMuxPolicy) {
config.rtcpMuxPolicy = 'require';
}
switch (config.iceTransportPolicy) {
case 'all':
case 'relay':
break;
default:
config.iceTransportPolicy = 'all';
break;
}
switch (config.bundlePolicy) {
case 'balanced':
case 'max-compat':
case 'max-bundle':
break;
default:
config.bundlePolicy = 'balanced';
break;
}
config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);
this._iceGatherers = [];
if (config.iceCandidatePoolSize) {
for (var i = config.iceCandidatePoolSize; i > 0; i--) {
this._iceGatherers.push(new window.RTCIceGatherer({
iceServers: config.iceServers,
gatherPolicy: config.iceTransportPolicy
}));
}
} else {
config.iceCandidatePoolSize = 0;
}
this._config = config; // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
// everything that is needed to describe a SDP m-line.
this.transceivers = [];
this._sdpSessionId = SDPUtils.generateSessionId();
this._sdpSessionVersion = 0;
this._dtlsRole = undefined; // role for a=setup to use in answers.
this._isClosed = false;
};
Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {
configurable: true,
get: function get() {
return this._localDescription;
}
});
Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {
configurable: true,
get: function get() {
return this._remoteDescription;
}
}); // set up event handlers on prototype
RTCPeerConnection.prototype.onicecandidate = null;
RTCPeerConnection.prototype.onaddstream = null;
RTCPeerConnection.prototype.ontrack = null;
RTCPeerConnection.prototype.onremovestream = null;
RTCPeerConnection.prototype.onsignalingstatechange = null;
RTCPeerConnection.prototype.oniceconnectionstatechange = null;
RTCPeerConnection.prototype.onconnectionstatechange = null;
RTCPeerConnection.prototype.onicegatheringstatechange = null;
RTCPeerConnection.prototype.onnegotiationneeded = null;
RTCPeerConnection.prototype.ondatachannel = null;
RTCPeerConnection.prototype._dispatchEvent = function (name, event) {
if (this._isClosed) {
return;
}
this.dispatchEvent(event);
if (typeof this['on' + name] === 'function') {
this['on' + name](event);
}
};
RTCPeerConnection.prototype._emitGatheringStateChange = function () {
var event = new Event('icegatheringstatechange');
this._dispatchEvent('icegatheringstatechange', event);
};
RTCPeerConnection.prototype.getConfiguration = function () {
return this._config;
};
RTCPeerConnection.prototype.getLocalStreams = function () {
return this.localStreams;
};
RTCPeerConnection.prototype.getRemoteStreams = function () {
return this.remoteStreams;
}; // internal helper to create a transceiver object.
// (which is not yet the same as the WebRTC 1.0 transceiver)
RTCPeerConnection.prototype._createTransceiver = function (kind, doNotAdd) {
var hasBundleTransport = this.transceivers.length > 0;
var transceiver = {
track: null,
iceGatherer: null,
iceTransport: null,
dtlsTransport: null,
localCapabilities: null,
remoteCapabilities: null,
rtpSender: null,
rtpReceiver: null,
kind: kind,
mid: null,
sendEncodingParameters: null,
recvEncodingParameters: null,
stream: null,
associatedRemoteMediaStreams: [],
wantReceive: true
};
if (this.usingBundle && hasBundleTransport) {
transceiver.iceTransport = this.transceivers[0].iceTransport;
transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
} else {
var transports = this._createIceAndDtlsTransports();
transceiver.iceTransport = transports.iceTransport;
transceiver.dtlsTransport = transports.dtlsTransport;
}
if (!doNotAdd) {
this.transceivers.push(transceiver);
}
return transceiver;
};
RTCPeerConnection.prototype.addTrack = function (track, stream) {
if (this._isClosed) {
throw makeError('InvalidStateError', 'Attempted to call addTrack on a closed peerconnection.');
}
var alreadyExists = this.transceivers.find(function (s) {
return s.track === track;
});
if (alreadyExists) {
throw makeError('InvalidAccessError', 'Track already exists.');
}
var transceiver;
for (var i = 0; i < this.transceivers.length; i++) {
if (!this.transceivers[i].track && this.transceivers[i].kind === track.kind) {
transceiver = this.transceivers[i];
}
}
if (!transceiver) {
transceiver = this._createTransceiver(track.kind);
}
this._maybeFireNegotiationNeeded();
if (this.localStreams.indexOf(stream) === -1) {
this.localStreams.push(stream);
}
transceiver.track = track;
transceiver.stream = stream;
transceiver.rtpSender = new window.RTCRtpSender(track, transceiver.dtlsTransport);
return transceiver.rtpSender;
};
RTCPeerConnection.prototype.addStream = function (stream) {
var pc = this;
if (edgeVersion >= 15025) {
stream.getTracks().forEach(function (track) {
pc.addTrack(track, stream);
});
} else {
// Clone is necessary for local demos mostly, attaching directly
// to two different senders does not work (build 10547).
// Fixed in 15025 (or earlier)
var clonedStream = stream.clone();
stream.getTracks().forEach(function (track, idx) {
var clonedTrack = clonedStream.getTracks()[idx];
track.addEventListener('enabled', function (event) {
clonedTrack.enabled = event.enabled;
});
});
clonedStream.getTracks().forEach(function (track) {
pc.addTrack(track, clonedStream);
});
}
};
RTCPeerConnection.prototype.removeTrack = function (sender) {
if (this._isClosed) {
throw makeError('InvalidStateError', 'Attempted to call removeTrack on a closed peerconnection.');
}
if (!(sender instanceof window.RTCRtpSender)) {
throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.');
}
var transceiver = this.transceivers.find(function (t) {
return t.rtpSender === sender;
});
if (!transceiver) {
throw makeError('InvalidAccessError', 'Sender was not created by this connection.');
}
var stream = transceiver.stream;
transceiver.rtpSender.stop();
transceiver.rtpSender = null;
transceiver.track = null;
transceiver.stream = null; // remove the stream from the set of local streams
var localStreams = this.transceivers.map(function (t) {
return t.stream;
});
if (localStreams.indexOf(stream) === -1 && this.localStreams.indexOf(stream) > -1) {
this.localStreams.splice(this.localStreams.indexOf(stream), 1);
}
this._maybeFireNegotiationNeeded();
};
RTCPeerConnection.prototype.removeStream = function (stream) {
var pc = this;
stream.getTracks().forEach(function (track) {
var sender = pc.getSenders().find(function (s) {
return s.track === track;
});
if (sender) {
pc.removeTrack(sender);
}
});
};
RTCPeerConnection.prototype.getSenders = function () {
return this.transceivers.filter(function (transceiver) {
return !!transceiver.rtpSender;
}).map(function (transceiver) {
return transceiver.rtpSender;
});
};
RTCPeerConnection.prototype.getReceivers = function () {
return this.transceivers.filter(function (transceiver) {
return !!transceiver.rtpReceiver;
}).map(function (transceiver) {
return transceiver.rtpReceiver;
});
};
RTCPeerConnection.prototype._createIceGatherer = function (sdpMLineIndex, usingBundle) {
var pc = this;
if (usingBundle && sdpMLineIndex > 0) {
return this.transceivers[0].iceGatherer;
} else if (this._iceGatherers.length) {
return this._iceGatherers.shift();
}
var iceGatherer = new window.RTCIceGatherer({
iceServers: this._config.iceServers,
gatherPolicy: this._config.iceTransportPolicy
});
Object.defineProperty(iceGatherer, 'state', {
value: 'new',
writable: true
});
this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];
this.transceivers[sdpMLineIndex].bufferCandidates = function (event) {
var end = !event.candidate || Object.keys(event.candidate).length === 0; // polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
iceGatherer.state = end ? 'completed' : 'gathering';
if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {
pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);
}
};
iceGatherer.addEventListener('localcandidate', this.transceivers[sdpMLineIndex].bufferCandidates);
return iceGatherer;
}; // start gathering from an RTCIceGatherer.
RTCPeerConnection.prototype._gather = function (mid, sdpMLineIndex) {
var pc = this;
var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
if (iceGatherer.onlocalcandidate) {
return;
}
var bufferedCandidateEvents = this.transceivers[sdpMLineIndex].bufferedCandidateEvents;
this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;
iceGatherer.removeEventListener('localcandidate', this.transceivers[sdpMLineIndex].bufferCandidates);
iceGatherer.onlocalcandidate = function (evt) {
if (pc.usingBundle && sdpMLineIndex > 0) {
// if we know that we use bundle we can drop candidates with
// ѕdpMLineIndex > 0. If we don't do this then our state gets
// confused since we dispose the extra ice gatherer.
return;
}
var event = new Event('icecandidate');
event.candidate = {
sdpMid: mid,
sdpMLineIndex: sdpMLineIndex
};
var cand = evt.candidate; // Edge emits an empty object for RTCIceCandidateComplete‥
var end = !cand || Object.keys(cand).length === 0;
if (end) {
// polyfill since RTCIceGatherer.state is not implemented in
// Edge 10547 yet.
if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {
iceGatherer.state = 'completed';
}
} else {
if (iceGatherer.state === 'new') {
iceGatherer.state = 'gathering';
} // RTCIceCandidate doesn't have a component, needs to be added
cand.component = 1; // also the usernameFragment. TODO: update SDP to take both variants.
cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;
var serializedCandidate = SDPUtils.writeCandidate(cand);
event.candidate = Object.assign(event.candidate, SDPUtils.parseCandidate(serializedCandidate));
event.candidate.candidate = serializedCandidate;
event.candidate.toJSON = function () {
return {
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex,
usernameFragment: event.candidate.usernameFragment
};
};
} // update local description.
var sections = SDPUtils.getMediaSections(pc._localDescription.sdp);
if (!end) {
sections[event.candidate.sdpMLineIndex] += 'a=' + event.candidate.candidate + '\r\n';
} else {
sections[event.candidate.sdpMLineIndex] += 'a=end-of-candidates\r\n';
}
pc._localDescription.sdp = SDPUtils.getDescription(pc._localDescription.sdp) + sections.join('');
var complete = pc.transceivers.every(function (transceiver) {
return transceiver.iceGatherer && transceiver.iceGatherer.state === 'completed';
});
if (pc.iceGatheringState !== 'gathering') {
pc.iceGatheringState = 'gathering';
pc._emitGatheringStateChange();
} // Emit candidate. Also emit null candidate when all gatherers are
// complete.
if (!end) {
pc._dispatchEvent('icecandidate', event);
}
if (complete) {
pc._dispatchEvent('icecandidate', new Event('icecandidate'));
pc.iceGatheringState = 'complete';
pc._emitGatheringStateChange();
}
}; // emit already gathered candidates.
window.setTimeout(function () {
bufferedCandidateEvents.forEach(function (e) {
iceGatherer.onlocalcandidate(e);
});
}, 0);
}; // Create ICE transport and DTLS transport.
RTCPeerConnection.prototype._createIceAndDtlsTransports = function () {
var pc = this;
var iceTransport = new window.RTCIceTransport(null);
iceTransport.onicestatechange = function () {
pc._updateIceConnectionState();
pc._updateConnectionState();
};
var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
dtlsTransport.ondtlsstatechange = function () {
pc._updateConnectionState();
};
dtlsTransport.onerror = function () {
// onerror does not set state to failed by itself.
Object.defineProperty(dtlsTransport, 'state', {
value: 'failed',
writable: true
});
pc._updateConnectionState();
};
return {
iceTransport: iceTransport,
dtlsTransport: dtlsTransport
};
}; // Destroy ICE gatherer, ICE transport and DTLS transport.
// Without triggering the callbacks.
RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function (sdpMLineIndex) {
var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
if (iceGatherer) {
delete iceGatherer.onlocalcandidate;
delete this.transceivers[sdpMLineIndex].iceGatherer;
}
var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
if (iceTransport) {
delete iceTransport.onicestatechange;
delete this.transceivers[sdpMLineIndex].iceTransport;
}
var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
if (dtlsTransport) {
delete dtlsTransport.ondtlsstatechange;
delete dtlsTransport.onerror;
delete this.transceivers[sdpMLineIndex].dtlsTransport;
}
}; // Start the RTP Sender and Receiver for a transceiver.
RTCPeerConnection.prototype._transceive = function (transceiver, send, recv) {
var params = getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
if (send && transceiver.rtpSender) {
params.encodings = transceiver.sendEncodingParameters;
params.rtcp = {
cname: SDPUtils.localCName,
compound: transceiver.rtcpParameters.compound
};
if (transceiver.recvEncodingParameters.length) {
params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
}
transceiver.rtpSender.send(params);
}
if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {
// remove RTX field in Edge 14942
if (transceiver.kind === 'video' && transceiver.recvEncodingParameters && edgeVersion < 15019) {
transceiver.recvEncodingParameters.forEach(function (p) {
delete p.rtx;
});
}
if (transceiver.recvEncodingParameters.length) {
params.encodings = transceiver.recvEncodingParameters;
} else {
params.encodings = [{}];
}
params.rtcp = {
compound: transceiver.rtcpParameters.compound
};
if (transceiver.rtcpParameters.cname) {
params.rtcp.cname = transceiver.rtcpParameters.cname;
}
if (transceiver.sendEncodingParameters.length) {
params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
}
transceiver.rtpReceiver.receive(params);
}
};
RTCPeerConnection.prototype.setLocalDescription = function (description) {
var pc = this; // Note: pranswer is not supported.
if (['offer', 'answer'].indexOf(description.type) === -1) {
return Promise.reject(makeError('TypeError', 'Unsupported type "' + description.type + '"'));
}
if (!isActionAllowedInSignalingState('setLocalDescription', description.type, pc.signalingState) || pc._isClosed) {
return Promise.reject(makeError('InvalidStateError', 'Can not set local ' + description.type + ' in state ' + pc.signalingState));
}
var sections;
var sessionpart;
if (description.type === 'offer') {
// VERY limited support for SDP munging. Limited to:
// * changing the order of codecs
sections = SDPUtils.splitSections(description.sdp);
sessionpart = sections.shift();
sections.forEach(function (mediaSection, sdpMLineIndex) {
var caps = SDPUtils.parseRtpParameters(mediaSection);
pc.transceivers[sdpMLineIndex].localCapabilities = caps;
});
pc.transceivers.forEach(function (transceiver, sdpMLineIndex) {
pc._gather(transceiver.mid, sdpMLineIndex);
});
} else if (description.type === 'answer') {
sections = SDPUtils.splitSections(pc._remoteDescription.sdp);
sessionpart = sections.shift();
var isIceLite = SDPUtils.matchPrefix(sessionpart, 'a=ice-lite').length > 0;
sections.forEach(function (mediaSection, sdpMLineIndex) {
var transceiver = pc.transceivers[sdpMLineIndex];
var iceGatherer = transceiver.iceGatherer;
var iceTransport = transceiver.iceTransport;
var dtlsTransport = transceiver.dtlsTransport;
var localCapabilities = transceiver.localCapabilities;
var remoteCapabilities = transceiver.remoteCapabilities; // treat bundle-only as not-rejected.
var rejected = SDPUtils.isRejected(mediaSection) && SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
if (!rejected && !transceiver.rejected) {
var remoteIceParameters = SDPUtils.getIceParameters(mediaSection, sessionpart);
var remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, sessionpart);
if (isIceLite) {
remoteDtlsParameters.role = 'server';
}
if (!pc.usingBundle || sdpMLineIndex === 0) {
pc._gather(transceiver.mid, sdpMLineIndex);
if (iceTransport.state === 'new') {
iceTransport.start(iceGatherer, remoteIceParameters, isIceLite ? 'controlling' : 'controlled');
}
if (dtlsTransport.state === 'new') {
dtlsTransport.start(remoteDtlsParameters);
}
} // Calculate intersection of capabilities.
var params = getCommonCapabilities(localCapabilities, remoteCapabilities); // Start the RTCRtpSender. The RTCRtpReceiver for this
// transceiver has already been started in setRemoteDescription.
pc._transceive(transceiver, params.codecs.length > 0, false);
}
});
}
pc._localDescription = {
type: description.type,
sdp: description.sdp
};
if (description.type === 'offer') {
pc._updateSignalingState('have-local-offer');
} else {
pc._updateSignalingState('stable');
}
return Promise.resolve();
};
RTCPeerConnection.prototype.setRemoteDescription = function (description) {
var pc = this; // Note: pranswer is not supported.
if (['offer', 'answer'].indexOf(description.type) === -1) {
return Promise.reject(makeError('TypeError', 'Unsupported type "' + description.type + '"'));
}
if (!isActionAllowedInSignalingState('setRemoteDescription', description.type, pc.signalingState) || pc._isClosed) {
return Promise.reject(makeError('InvalidStateError', 'Can not set remote ' + description.type + ' in state ' + pc.signalingState));
}
var streams = {};
pc.remoteStreams.forEach(function (stream) {
streams[stream.id] = stream;
});
var receiverList = [];
var sections = SDPUtils.splitSections(description.sdp);
var sessionpart = sections.shift();
var isIceLite = SDPUtils.matchPrefix(sessionpart, 'a=ice-lite').length > 0;
var usingBundle = SDPUtils.matchPrefix(sessionpart, 'a=group:BUNDLE ').length > 0;
pc.usingBundle = usingBundle;
var iceOptions = SDPUtils.matchPrefix(sessionpart, 'a=ice-options:')[0];
if (iceOptions) {
pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ').indexOf('trickle') >= 0;
} else {
pc.canTrickleIceCandidates = false;
}
sections.forEach(function (mediaSection, sdpMLineIndex) {
var lines = SDPUtils.splitLines(mediaSection);
var kind = SDPUtils.getKind(mediaSection); // treat bundle-only as not-rejected.
var rejected = SDPUtils.isRejected(mediaSection) && SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
var protocol = lines[0].substr(2).split(' ')[2];
var direction = SDPUtils.getDirection(mediaSection, sessionpart);
var remoteMsid = SDPUtils.parseMsid(mediaSection);
var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier(); // Reject datachannels which are not implemented yet.
if (rejected || kind === 'application' && (protocol === 'DTLS/SCTP' || protocol === 'UDP/DTLS/SCTP')) {
// TODO: this is dangerous in the case where a non-rejected m-line
// becomes rejected.
pc.transceivers[sdpMLineIndex] = {
mid: mid,
kind: kind,
protocol: protocol,
rejected: true
};
return;
}
if (!rejected && pc.transceivers[sdpMLineIndex] && pc.transceivers[sdpMLineIndex].rejected) {
// recycle a rejected transceiver.
pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);
}
var transceiver;
var iceGatherer;
var iceTransport;
var dtlsTransport;
var rtpReceiver;
var sendEncodingParameters;
var recvEncodingParameters;
var localCapabilities;
var track; // 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);
remoteDtlsParameters.role = 'client';
}
recvEncodingParameters = SDPUtils.parseRtpEncodingParameters(mediaSection);
var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
var isComplete = SDPUtils.matchPrefix(mediaSection, 'a=end-of-candidates', sessionpart).length > 0;
var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:').map(function (cand) {
return SDPUtils.parseCandidate(cand);
}).filter(function (cand) {
return cand.component === 1;
}); // Check if we can use BUNDLE and dispose transports.
if ((description.type === 'offer' || description.type === 'answer') && !rejected && usingBundle && sdpMLineIndex > 0 && pc.transceivers[sdpMLineIndex]) {
pc._disposeIceAndDtlsTransports(sdpMLineIndex);
pc.transceivers[sdpMLineIndex].iceGatherer = pc.transceivers[0].iceGatherer;
pc.transceivers[sdpMLineIndex].iceTransport = pc.transceivers[0].iceTransport;
pc.transceivers[sdpMLineIndex].dtlsTransport = pc.transceivers[0].dtlsTransport;
if (pc.transceivers[sdpMLineIndex].rtpSender) {
pc.transceivers[sdpMLineIndex].rtpSender.setTransport(pc.transceivers[0].dtlsTransport);
}
if (pc.transceivers[sdpMLineIndex].rtpReceiver) {
pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(pc.transceivers[0].dtlsTransport);
}
}
if (description.type === 'offer' && !rejected) {
transceiver = pc.transceivers[sdpMLineIndex] || pc._createTransceiver(kind);
transceiver.mid = mid;
if (!transceiver.iceGatherer) {
transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, usingBundle);
}
if (cands.length && transceiver.iceTransport.state === 'new') {
if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
transceiver.iceTransport.setRemoteCandidates(cands);
} else {
cands.forEach(function (candidate) {
maybeAddCandidate(transceiver.iceTransport, candidate);
});
}
}
localCapabilities = window.RTCRtpReceiver.getCapabilities(kind); // filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
if (edgeVersion < 15019) {
localCapabilities.codecs = localCapabilities.codecs.filter(function (codec) {
return codec.name !== 'rtx';
});
}
sendEncodingParameters = transceiver.sendEncodingParameters || [{
ssrc: (2 * sdpMLineIndex + 2) * 1001
}]; // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
var isNewTrack = false;
if (direction === 'sendrecv' || direction === 'sendonly') {
isNewTrack = !transceiver.rtpReceiver;
rtpReceiver = transceiver.rtpReceiver || new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);
if (isNewTrack) {
var stream;
track = rtpReceiver.track; // FIXME: does not work with Plan B.
if (remoteMsid && remoteMsid.stream === '-') {// no-op. a stream id of '-' means: no associated stream.
} else if (remoteMsid) {
if (!streams[remoteMsid.stream]) {
streams[remoteMsid.stream] = new window.MediaStream();
Object.defineProperty(streams[remoteMsid.stream], 'id', {
get: function get() {
return remoteMsid.stream;
}
});
}
Object.defineProperty(track, 'id', {
get: function get() {
return remoteMsid.track;
}
});
stream = streams[remoteMsid.stream];
} else {
if (!streams.default) {
streams.default = new window.MediaStream();
}
stream = streams.default;
}
if (stream) {
addTrackToStreamAndFireEvent(track, stream);
transceiver.associatedRemoteMediaStreams.push(stream);
}
receiverList.push([track, rtpReceiver, stream]);
}
} else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {
transceiver.associatedRemoteMediaStreams.forEach(function (s) {
var nativeTrack = s.getTracks().find(function (t) {
return t.id === transceiver.rtpReceiver.track.id;
});
if (nativeTrack) {
removeTrackFromStreamAndFireEvent(nativeTrack, s);
}
});
transceiver.associatedRemoteMediaStreams = [];
}
transceiver.localCapabilities = localCapabilities;
transceiver.remoteCapabilities = remoteCapabilities;
transceiver.rtpReceiver = rtpReceiver;
transceiver.rtcpParameters = rtcpParameters;
transceiver.sendEncodingParameters = sendEncodingParameters;
transceiver.recvEncodingParameters = recvEncodingParameters; // Start the RTCRtpReceiver now. The RTPSender is started in
// setLocalDescription.
pc._transceive(pc.transceivers[sdpMLineIndex], false, isNewTrack);
} else if (description.type === 'answer' && !rejected) {
transceiver = pc.transceivers[sdpMLineIndex];
iceGatherer = transceiver.iceGatherer;
iceTransport = transceiver.iceTransport;
dtlsTransport = transceiver.dtlsTransport;
rtpReceiver = transceiver.rtpReceiver;
sendEncodingParameters = transceiver.sendEncodingParameters;
localCapabilities = transceiver.localCapabilities;
pc.transceivers[sdpMLineIndex].recvEncodingParameters = recvEncodingParameters;
pc.transceivers[sdpMLineIndex].remoteCapabilities = remoteCapabilities;
pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
if (cands.length && iceTransport.state === 'new') {
if ((isIceLite || isComplete) && (!usingBundle || sdpMLineIndex === 0)) {
iceTransport.setRemoteCandidates(cands);
} else {
cands.forEach(function (candidate) {
maybeAddCandidate(transceiver.iceTransport, candidate);
});
}
}
if (!usingBundle || sdpMLineIndex === 0) {
if (iceTransport.state === 'new') {
iceTransport.start(iceGatherer, remoteIceParameters, 'controlling');
}
if (dtlsTransport.state === 'new') {
dtlsTransport.start(remoteDtlsParameters);
}
} // If the offer contained RTX but the answer did not,
// remove RTX from sendEncodingParameters.
var commonCapabilities = getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
var hasRtx = commonCapabilities.codecs.filter(function (c) {
return c.name.toLowerCase() === 'rtx';
}).length;
if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
delete transceiver.sendEncodingParameters[0].rtx;
}
pc._transceive(transceiver, direction === 'sendrecv' || direction === 'recvonly', direction === 'sendrecv' || direction === 'sendonly'); // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
if (rtpReceiver && (direction === 'sendrecv' || direction === 'sendonly')) {
track = rtpReceiver.track;
if (remoteMsid) {
if (!streams[remoteMsid.stream]) {
streams[remoteMsid.stream] = new window.MediaStream();
}
addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);
receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
} else {
if (!streams.default) {
streams.default = new window.MediaStream();
}
addTrackToStreamAndFireEvent(track, streams.default);
receiverList.push([track, rtpReceiver, streams.default]);
}
} else {
// FIXME: actually the receiver should be created later.
delete transceiver.rtpReceiver;
}
}
});
if (pc._dtlsRole === undefined) {
pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';
}
pc._remoteDescription = {
type: description.type,
sdp: description.sdp
};
if (description.type === 'offer') {
pc._updateSignalingState('have-remote-offer');
} else {
pc._updateSignalingState('stable');
}
Object.keys(streams).forEach(function (sid) {
var stream = streams[sid];
if (stream.getTracks().length) {
if (pc.remoteStreams.indexOf(stream) === -1) {
pc.remoteStreams.push(stream);
var event = new Event('addstream');
event.stream = stream;
window.setTimeout(function () {
pc._dispatchEvent('addstream', event);
});
}
receiverList.forEach(function (item) {
var track = item[0];
var receiver = item[1];
if (stream.id !== item[2].id) {
return;
}
fireAddTrack(pc, track, receiver, [stream]);
});
}
});
receiverList.forEach(function (item) {
if (item[2]) {
return;
}
fireAddTrack(pc, item[0], item[1], []);
}); // check whether addIceCandidate({}) was called within four seconds after
// setRemoteDescription.
window.setTimeout(function () {
if (!(pc && pc.transceivers)) {
return;
}
pc.transceivers.forEach(function (transceiver) {
if (transceiver.iceTransport && transceiver.iceTransport.state === 'new' && transceiver.iceTransport.getRemoteCandidates().length > 0) {
console.warn('Timeout for addRemoteCandidate. Consider sending ' + 'an end-of-candidates notification');
transceiver.iceTransport.addRemoteCandidate({});
}
});
}, 4000);
return Promise.resolve();
};
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._isClosed = true;
this._updateSignalingState('closed');
}; // Update the signaling state.
RTCPeerConnection.prototype._updateSignalingState = function (newState) {
this.signalingState = newState;
var event = new Event('signalingstatechange');
this._dispatchEvent('signalingstatechange', event);
}; // Determine whether to fire the negotiationneeded event.
RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function () {
var pc = this;
if (this.signalingState !== 'stable' || this.needNegotiation === true) {
return;
}
this.needNegotiation = true;
window.setTimeout(function () {
if (pc.needNegotiation) {
pc.needNegotiation = false;
var event = new Event('negotiationneeded');
pc._dispatchEvent('negotiationneeded', event);
}
}, 0);
}; // Update the ice connection state.
RTCPeerConnection.prototype._updateIceConnectionState = function () {
var newState;
var states = {
'new': 0,
closed: 0,
checking: 0,
connected: 0,
completed: 0,
disconnected: 0,
failed: 0
};
this.transceivers.forEach(function (transceiver) {
if (transceiver.iceTransport && !transceiver.rejected) {
states[transceiver.iceTransport.state]++;
}
});
newState = 'new';
if (states.failed > 0) {
newState = 'failed';
} else if (states.checking > 0) {
newState = 'checking';
} else if (states.disconnected > 0) {
newState = 'disconnected';
} else if (states.new > 0) {
newState = 'new';
} else if (states.connected > 0) {
newState = 'connected';
} else if (states.completed > 0) {
newState = 'completed';
}
if (newState !== this.iceConnectionState) {
this.iceConnectionState = newState;
var event = new Event('iceconnectionstatechange');
this._dispatchEvent('iceconnectionstatechange', event);
}
}; // Update the connection state.
RTCPeerConnection.prototype._updateConnectionState = function () {
var newState;
var states = {
'new': 0,
closed: 0,
connecting: 0,
connected: 0,
completed: 0,
disconnected: 0,
failed: 0
};
this.transceivers.forEach(function (transceiver) {
if (transceiver.iceTransport && transceiver.dtlsTransport && !transceiver.rejected) {
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) {
newState = 'connecting';
} else if (states.disconnected > 0) {
newState = 'disconnected';
} else if (states.new > 0) {
newState = 'new';
} else if (states.connected > 0) {
newState = 'connected';
}
if (newState !== this.connectionState) {
this.connectionState = newState;
var event = new Event('connectionstatechange');
this._dispatchEvent('connectionstatechange', event);
}
};
RTCPeerConnection.prototype.createOffer = function () {
var pc = this;
if (pc._isClosed) {
return Promise.reject(makeError('InvalidStateError', 'Can not call createOffer after close'));
}
var numAudioTracks = pc.transceivers.filter(function (t) {
return t.kind === 'audio';
}).length;
var numVideoTracks = pc.transceivers.filter(function (t) {
return t.kind === 'video';
}).length; // Determine number of audio and video tracks we need to send/recv.
var offerOptions = arguments[0];
if (offerOptions) {
// Reject Chrome legacy constraints.
if (offerOptions.mandatory || offerOptions.optional) {
throw new TypeError('Legacy mandatory/optional constraints not supported.');
}
if (offerOptions.offerToReceiveAudio !== undefined) {
if (offerOptions.offerToReceiveAudio === true) {
numAudioTracks = 1;
} else if (offerOptions.offerToReceiveAudio === false) {
numAudioTracks = 0;
} else {
numAudioTracks = offerOptions.offerToReceiveAudio;
}
}
if (offerOptions.offerToReceiveVideo !== undefined) {
if (offerOptions.offerToReceiveVideo === true) {
numVideoTracks = 1;
} else if (offerOptions.offerToReceiveVideo === false) {
numVideoTracks = 0;
} else {
numVideoTracks = offerOptions.offerToReceiveVideo;
}
}
}
pc.transceivers.forEach(function (transceiver) {
if (transceiver.kind === 'audio') {
numAudioTracks--;
if (numAudioTracks < 0) {
transceiver.wantReceive = false;
}
} else if (transceiver.kind === 'video') {
numVideoTracks--;
if (numVideoTracks < 0) {
transceiver.wantReceive = false;
}
}
}); // Create M-lines for recvonly streams.
while (numAudioTracks > 0 || numVideoTracks > 0) {
if (numAudioTracks > 0) {
pc._createTransceiver('audio');
numAudioTracks--;
}
if (numVideoTracks > 0) {
pc._createTransceiver('video');
numVideoTracks--;
}
}
var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId, pc._sdpSessionVersion++);
pc.transceivers.forEach(function (transceiver, sdpMLineIndex) {
// For each track, create an ice gatherer, ice transport,
// dtls transport, potentially rtpsender and rtpreceiver.
var track = transceiver.track;
var kind = transceiver.kind;
var mid = transceiver.mid || SDPUtils.generateIdentifier();
transceiver.mid = mid;
if (!transceiver.iceGatherer) {
transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, pc.usingBundle);
}
var localCapabilities = window.RTCRtpSender.getCapabilities(kind); // filter RTX until additional stuff needed for RTX is implemented
// in adapter.js
if (edgeVersion < 15019) {
localCapabilities.codecs = localCapabilities.codecs.filter(function (codec) {
return codec.name !== 'rtx';
});
}
localCapabilities.codecs.forEach(function (codec) {
// work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
// by adding level-asymmetry-allowed=1
if (codec.name === 'H264' && codec.parameters['level-asymmetry-allowed'] === undefined) {
codec.parameters['level-asymmetry-allowed'] = '1';
} // for subsequent offers, we might have to re-use the payload
// type of the last offer.
if (transceiver.remoteCapabilities && transceiver.remoteCapabilities.codecs) {
transceiver.remoteCapabilities.codecs.forEach(function (remoteCodec) {
if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() && codec.clockRate === remoteCodec.clockRate) {
codec.preferredPayloadType = remoteCodec.payloadType;
}
});
}
});
localCapabilities.headerExtensions.forEach(function (hdrExt) {
var remoteExtensions = transceiver.remoteCapabilities && transceiver.remoteCapabilities.headerExtensions || [];
remoteExtensions.forEach(function (rHdrExt) {
if (hdrExt.uri === rHdrExt.uri) {
hdrExt.id = rHdrExt.id;
}
});
}); // generate an ssrc now, to be used later in rtpSender.send
var sendEncodingParameters = transceiver.sendEncodingParameters || [{
ssrc: (2 * sdpMLineIndex + 1) * 1001
}];
if (track) {
// add RTX
if (edgeVersion >= 15019 && kind === 'video' && !sendEncodingParameters[0].rtx) {
sendEncodingParameters[0].rtx = {
ssrc: sendEncodingParameters[0].ssrc + 1
};
}
}
if (transceiver.wantReceive) {
transceiver.rtpReceiver = new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);
}
transceiver.localCapabilities = localCapabilities;
transceiver.sendEncodingParameters = sendEncodingParameters;
}); // always offer BUNDLE and dispose on return if not supported.
if (pc._config.bundlePolicy !== 'max-compat') {
sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function (t) {
return t.mid;
}).join(' ') + '\r\n';
}
sdp += 'a=ice-options:trickle\r\n';
pc.transceivers.forEach(function (transceiver, sdpMLineIndex) {
sdp += writeMediaSection(transceiver, transceiver.localCapabilities, 'offer', transceiver.stream, pc._dtlsRole);
sdp += 'a=rtcp-rsize\r\n';
if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' && (sdpMLineIndex === 0 || !pc.usingBundle)) {
transceiver.iceGatherer.getLocalCandidates().forEach(function (cand) {
cand.component = 1;
sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\r\n';
});
if (transceiver.iceGatherer.state === 'completed') {
sdp += 'a=end-of-candidates\r\n';
}
}
});
var desc = new window.RTCSessionDescription({
type: 'offer',
sdp: sdp
});
return Promise.resolve(desc);
};
RTCPeerConnection.prototype.createAnswer = function () {
var pc = this;
if (pc._isClosed) {
return Promise.reject(makeError('InvalidStateError', 'Can not call createAnswer after close'));
}
if (!(pc.signalingState === 'have-remote-offer' || pc.signalingState === 'have-local-pranswer')) {
return Promise.reject(makeError('InvalidStateError', 'Can not call createAnswer in signalingState ' + pc.signalingState));
}
var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId, pc._sdpSessionVersion++);
if (pc.usingBundle) {
sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function (t) {
return t.mid;
}).join(' ') + '\r\n';
}
sdp += 'a=ice-options:trickle\r\n';
var mediaSectionsInOffer = SDPUtils.getMediaSections(pc._remoteDescription.sdp).length;
pc.transceivers.forEach(function (transceiver, sdpMLineIndex) {
if (sdpMLineIndex + 1 > mediaSectionsInOffer) {
return;
}
if (transceiver.rejected) {
if (transceiver.kind === 'application') {
if (transceiver.protocol === 'DTLS/SCTP') {
// legacy fmt
sdp += 'm=application 0 DTLS/SCTP 5000\r\n';
} else {
sdp += 'm=application 0 ' + transceiver.protocol + ' webrtc-datachannel\r\n';
}
} else if (transceiver.kind === 'audio') {
sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' + 'a=rtpmap:0 PCMU/8000\r\n';
} else if (transceiver.kind === 'video') {
sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' + 'a=rtpmap:120 VP8/90000\r\n';
}
sdp += 'c=IN IP4 0.0.0.0\r\n' + 'a=inactive\r\n' + 'a=mid:' + transceiver.mid + '\r\n';
return;
} // FIXME: look at direction.
if (transceiver.stream) {
var localTrack;
if (transceiver.kind === 'audio') {
localTrack = transceiver.stream.getAudioTracks()[0];
} else if (transceiver.kind === 'video') {
localTrack = transceiver.stream.getVideoTracks()[0];
}
if (localTrack) {
// add RTX
if (edgeVersion >= 15019 && transceiver.kind === 'video' && !transceiver.sendEncodingParameters[0].rtx) {
transceiver.sendEncodingParameters[0].rtx = {
ssrc: transceiver.sendEncodingParameters[0].ssrc + 1
};
}
}
} // Calculate intersection of capabilities.
var commonCapabilities = getCommonCapabilities(transceiver.localCapabilities, transceiver.remoteCapabilities);
var hasRtx = commonCapabilities.codecs.filter(function (c) {
return c.name.toLowerCase() === 'rtx';
}).length;
if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
delete transceiver.sendEncodingParameters[0].rtx;
}
sdp += writeMediaSection(transceiver, commonCapabilities, 'answer', transceiver.stream, pc._dtlsRole);
if (transceiver.rtcpParameters && transceiver.rtcpParameters.reducedSize) {
sdp += 'a=rtcp-rsize\r\n';
}
});
var desc = new window.RTCSessionDescription({
type: 'answer',
sdp: sdp
});
return Promise.resolve(desc);
};
RTCPeerConnection.prototype.addIceCandidate = function (candidate) {
var pc = this;
var sections;
if (candidate && !(candidate.sdpMLineIndex !== undefined || candidate.sdpMid)) {
return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));
} // TODO: needs to go into ops queue.
return new Promise(function (resolve, reject) {
if (!pc._remoteDescription) {
return reject(makeError('InvalidStateError', 'Can not add ICE candidate without a remote description'));
} else if (!candidate || candidate.candidate === '') {
for (var j = 0; j < pc.transceivers.length; j++) {
if (pc.transceivers[j].rejected) {
continue;
}
pc.transceivers[j].iceTransport.addRemoteCandidate({});
sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);
sections[j] += 'a=end-of-candidates\r\n';
pc._remoteDescription.sdp = SDPUtils.getDescription(pc._remoteDescription.sdp) + sections.join('');
if (pc.usingBundle) {
break;
}
}
} else {
var sdpMLineIndex = candidate.sdpMLineIndex;
if (candidate.sdpMid) {
for (var i = 0; i < pc.transceivers.length; i++) {
if (pc.transceivers[i].mid === candidate.sdpMid) {
sdpMLineIndex = i;
break;
}
}
}
var transceiver = pc.transceivers[sdpMLineIndex];
if (transceiver) {
if (transceiver.rejected) {
return resolve();
}
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 || cand.port === 9)) {
return resolve();
} // Ignore RTCP candidates, we assume RTCP-MUX.
if (cand.component && cand.component !== 1) {
return resolve();
} // when using bundle, avoid adding candidates to the wrong
// ice transport. And avoid adding candidates added in the SDP.
if (sdpMLineIndex === 0 || sdpMLineIndex > 0 && transceiver.iceTransport !== pc.transceivers[0].iceTransport) {
if (!maybeAddCandidate(transceiver.iceTransport, cand)) {
return reject(makeError('OperationError', 'Can not add ICE candidate'));
}
} // update the remoteDescription.
var candidateString = candidate.candidate.trim();
if (candidateString.indexOf('a=') === 0) {
candidateString = candidateString.substr(2);
}
sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);
sections[sdpMLineIndex] += 'a=' + (cand.type ? candidateString : 'end-of-candidates') + '\r\n';
pc._remoteDescription.sdp = SDPUtils.getDescription(pc._remoteDescription.sdp) + sections.join('');
} else {
return reject(makeError('OperationError', 'Can not add ICE candidate'));
}
}
resolve();
});
};
RTCPeerConnection.prototype.getStats = function (selector) {
if (selector && selector instanceof window.MediaStreamTrack) {
var senderOrReceiver = null;
this.transceivers.forEach(function (transceiver) {
if (transceiver.rtpSender && transceiver.rtpSender.track === selector) {
senderOrReceiver = transceiver.rtpSender;
} else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track === selector) {
senderOrReceiver = transceiver.rtpReceiver;
}
});
if (!senderOrReceiver) {
throw makeError('InvalidAccessError', 'Invalid selector.');
}
return senderOrReceiver.getStats();
}
var promises = [];
this.transceivers.forEach(function (transceiver) {
['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', 'dtlsTransport'].forEach(function (method) {
if (transceiver[method]) {
promises.push(transceiver[method].getStats());
}
});
});
return Promise.all(promises).then(function (allStats) {
var results = new Map();
allStats.forEach(function (stats) {
stats.forEach(function (stat) {
results.set(stat.id, stat);
});
});
return results;
});
}; // fix low-level stat names and return Map instead of object.
var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer', 'RTCIceTransport', 'RTCDtlsTransport'];
ortcObjects.forEach(function (ortcObjectName) {
var obj = window[ortcObjectName];
if (obj && obj.prototype && obj.prototype.getStats) {
var nativeGetstats = obj.prototype.getStats;
obj.prototype.getStats = function () {
return nativeGetstats.apply(this).then(function (nativeStats) {
var mapStats = new Map();
Object.keys(nativeStats).forEach(function (id) {
nativeStats[id].type = fixStatsType(nativeStats[id]);
mapStats.set(id, nativeStats[id]);
});
return mapStats;
});
};
}
}); // legacy callback shims. Should be moved to adapter.js some days.
var methods = ['createOffer', 'createAnswer'];
methods.forEach(function (method) {
var nativeMethod = RTCPeerConnection.prototype[method];
RTCPeerConnection.prototype[method] = function () {
var args = arguments;
if (typeof args[0] === 'function' || typeof args[1] === 'function') {
// legacy
return nativeMethod.apply(this, [arguments[2]]).then(function (description) {
if (typeof args[0] === 'function') {
args[0].apply(null, [description]);
}
}, function (error) {
if (typeof args[1] === 'function') {
args[1].apply(null, [error]);
}
});
}
return nativeMethod.apply(this, arguments);
};
});
methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];
methods.forEach(function (method) {
var nativeMethod = RTCPeerConnection.prototype[method];
RTCPeerConnection.prototype[method] = function () {
var args = arguments;
if (typeof args[1] === 'function' || typeof args[2] === 'function') {
// legacy
return nativeMethod.apply(this, arguments).then(function () {
if (typeof args[1] === 'function') {
args[1].apply(null);
}
}, function (error) {
if (typeof args[2] === 'function') {
args[2].apply(null, [error]);
}
});
}
return nativeMethod.apply(this, arguments);
};
}); // getStats is special. It doesn't have a spec legacy method yet we support
// getStats(something, cb) without error callbacks.
['getStats'].forEach(function (method) {
var nativeMethod = RTCPeerConnection.prototype[method];
RTCPeerConnection.prototype[method] = function () {
var args = arguments;
if (typeof args[1] === 'function') {
return nativeMethod.apply(this, arguments).then(function () {
if (typeof args[1] === 'function') {
args[1].apply(null);
}
});
}
return nativeMethod.apply(this, arguments);
};
});
return RTCPeerConnection;
};
},{"sdp":14}],14:[function(require,module,exports){
/* eslint-env node */
'use strict'; // SDP helpers.
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var SDPUtils = {}; // Generate an alphanumeric identifier for cname or mids.
// TODO: use UUIDs instead? https://gist.github.com/jed/982883
SDPUtils.generateIdentifier = function () {
return Math.random().toString(36).substr(2, 10);
}; // The RTCP CNAME used by all peerconnections from the same JS.
SDPUtils.localCName = SDPUtils.generateIdentifier(); // Splits SDP into lines, dealing with both CRLF and LF.
SDPUtils.splitLines = function (blob) {
return blob.trim().split('\n').map(function (line) {
return line.trim();
});
}; // Splits SDP into sessionpart and mediasections. Ensures CRLF.
SDPUtils.splitSections = function (blob) {
var parts = blob.split('\nm=');
return parts.map(function (part, index) {
return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
});
}; // returns the session description.
SDPUtils.getDescription = function (blob) {
var sections = SDPUtils.splitSections(blob);
return sections && sections[0];
}; // returns the individual media sections.
SDPUtils.getMediaSections = function (blob) {
var sections = SDPUtils.splitSections(blob);
sections.shift();
return sections;
}; // Returns lines that start with a certain prefix.
SDPUtils.matchPrefix = function (blob, prefix) {
return SDPUtils.splitLines(blob).filter(function (line) {
return line.indexOf(prefix) === 0;
});
}; // Parses an ICE candidate line. Sample input:
// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
// rport 55996"
SDPUtils.parseCandidate = function (line) {
var parts; // Parse both variants.
if (line.indexOf('a=candidate:') === 0) {
parts = line.substring(12).split(' ');
} else {
parts = line.substring(10).split(' ');
}
var candidate = {
foundation: parts[0],
component: parseInt(parts[1], 10),
protocol: parts[2].toLowerCase(),
priority: parseInt(parts[3], 10),
ip: parts[4],
address: parts[4],
// address is an alias for ip.
port: parseInt(parts[5], 10),
// skip parts[6] == 'typ'
type: parts[7]
};
for (var i = 8; i < parts.length; i += 2) {
switch (parts[i]) {
case 'raddr':
candidate.relatedAddress = parts[i + 1];
break;
case 'rport':
candidate.relatedPort = parseInt(parts[i + 1], 10);
break;
case 'tcptype':
candidate.tcpType = parts[i + 1];
break;
case 'ufrag':
candidate.ufrag = parts[i + 1]; // for backward compability.
candidate.usernameFragment = parts[i + 1];
break;
default:
// extension handling, in particular ufrag
candidate[parts[i]] = parts[i + 1];
break;
}
}
return candidate;
}; // Translates a candidate object into SDP candidate attribute.
SDPUtils.writeCandidate = function (candidate) {
var sdp = [];
sdp.push(candidate.foundation);
sdp.push(candidate.component);
sdp.push(candidate.protocol.toUpperCase());
sdp.push(candidate.priority);
sdp.push(candidate.address || candidate.ip);
sdp.push(candidate.port);
var type = candidate.type;
sdp.push('typ');
sdp.push(type);
if (type !== 'host' && candidate.relatedAddress && candidate.relatedPort) {
sdp.push('raddr');
sdp.push(candidate.relatedAddress);
sdp.push('rport');
sdp.push(candidate.relatedPort);
}
if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
sdp.push('tcptype');
sdp.push(candidate.tcpType);
}
if (candidate.usernameFragment || candidate.ufrag) {
sdp.push('ufrag');
sdp.push(candidate.usernameFragment || candidate.ufrag);
}
return 'candidate:' + sdp.join(' ');
}; // Parses an ice-options line, returns an array of option tags.
// a=ice-options:foo bar
SDPUtils.parseIceOptions = function (line) {
return line.substr(14).split(' ');
}; // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
// a=rtpmap:111 opus/48000/2
SDPUtils.parseRtpMap = function (line) {
var parts = line.substr(9).split(' ');
var parsed = {
payloadType: parseInt(parts.shift(), 10) // was: id
};
parts = parts[0].split('/');
parsed.name = parts[0];
parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; // legacy alias, got renamed back to channels in ORTC.
parsed.numChannels = parsed.channels;
return parsed;
}; // Generate an a=rtpmap line from RTCRtpCodecCapability or
// RTCRtpCodecParameters.
SDPUtils.writeRtpMap = function (codec) {
var pt = codec.payloadType;
if (codec.preferredPayloadType !== undefined) {
pt = codec.preferredPayloadType;
}
var channels = codec.channels || codec.numChannels || 1;
return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n';
}; // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
SDPUtils.parseExtmap = function (line) {
var parts = line.substr(9).split(' ');
return {
id: parseInt(parts[0], 10),
direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
uri: parts[1]
};
}; // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
// RTCRtpHeaderExtension.
SDPUtils.writeExtmap = function (headerExtension) {
return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + ' ' + headerExtension.uri + '\r\n';
}; // Parses an ftmp line, returns dictionary. Sample input:
// a=fmtp:96 vbr=on;cng=on
// Also deals with vbr=on; cng=on
SDPUtils.parseFmtp = function (line) {
var parsed = {};
var kv;
var parts = line.substr(line.indexOf(' ') + 1).split(';');
for (var j = 0; j < parts.length; j++) {
kv = parts[j].trim().split('=');
parsed[kv[0].trim()] = kv[1];
}
return parsed;
}; // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils.writeFmtp = function (codec) {
var line = '';
var pt = codec.payloadType;
if (codec.preferredPayloadType !== undefined) {
pt = codec.preferredPayloadType;
}
if (codec.parameters && Object.keys(codec.parameters).length) {
var params = [];
Object.keys(codec.parameters).forEach(function (param) {
if (codec.parameters[param]) {
params.push(param + '=' + codec.parameters[param]);
} else {
params.push(param);
}
});
line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
}
return line;
}; // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
// a=rtcp-fb:98 nack rpsi
SDPUtils.parseRtcpFb = function (line) {
var parts = line.substr(line.indexOf(' ') + 1).split(' ');
return {
type: parts.shift(),
parameter: parts.join(' ')
};
}; // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
SDPUtils.writeRtcpFb = function (codec) {
var lines = '';
var pt = codec.payloadType;
if (codec.preferredPayloadType !== undefined) {
pt = codec.preferredPayloadType;
}
if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
// FIXME: special handling for trr-int?
codec.rtcpFeedback.forEach(function (fb) {
lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n';
});
}
return lines;
}; // Parses an RFC 5576 ssrc media attribute. Sample input:
// a=ssrc:3735928559 cname:something
SDPUtils.parseSsrcMedia = function (line) {
var sp = line.indexOf(' ');
var parts = {
ssrc: parseInt(line.substr(7, sp - 7), 10)
};
var colon = line.indexOf(':', sp);
if (colon > -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;
};
SDPUtils.parseSsrcGroup = function (line) {
var parts = line.substr(13).split(' ');
return {
semantics: parts.shift(),
ssrcs: parts.map(function (ssrc) {
return parseInt(ssrc, 10);
})
};
}; // Extracts the MID (RFC 5888) from a media section.
// returns the MID or undefined if no mid line was found.
SDPUtils.getMid = function (mediaSection) {
var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
if (mid) {
return mid.substr(6);
}
};
SDPUtils.parseFingerprint = function (line) {
var parts = line.substr(14).split(' ');
return {
algorithm: parts[0].toLowerCase(),
// algorithm is case-sensitive in Edge.
value: parts[1]
};
}; // 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.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:'); // Note: a=setup line is ignored since we use the 'auto' role.
// Note2: 'algorithm' is not case sensitive except in Edge.
return {
role: 'auto',
fingerprints: lines.map(SDPUtils.parseFingerprint)
};
}; // 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); // Search in session part, too.
lines = lines.concat(SDPUtils.splitLines(sessionpart));
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:<pt> 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); // parse FEC mechanisms from rtpmap lines.
switch (codec.name.toUpperCase()) {
case 'RED':
case 'ULPFEC':
description.fecMechanisms.push(codec.name.toUpperCase());
break;
default:
// only RED and ULPFEC are recognized as FEC mechanisms.
break;
}
}
}
SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function (line) {
description.headerExtensions.push(SDPUtils.parseExtmap(line));
}); // FIXME: parse 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.writeFmtp(codec);
sdp += SDPUtils.writeRtcpFb(codec);
});
var maxptime = 0;
caps.codecs.forEach(function (codec) {
if (codec.maxptime > maxptime) {
maxptime = codec.maxptime;
}
});
if (maxptime > 0) {
sdp += 'a=maxptime:' + maxptime + '\r\n';
}
sdp += 'a=rtcp-mux\r\n';
if (caps.headerExtensions) {
caps.headerExtensions.forEach(function (extension) {
sdp += SDPUtils.writeExtmap(extension);
});
} // FIXME: write fecMechanisms.
return sdp;
}; // Parses the SDP media section and returns an array of
// RTCRtpEncodingParameters.
SDPUtils.parseRtpEncodingParameters = function (mediaSection) {
var encodingParameters = [];
var description = SDPUtils.parseRtpParameters(mediaSection);
var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; // filter a=ssrc:... cname:, ignore PlanB-msid
var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
return SDPUtils.parseSsrcMedia(line);
}).filter(function (parts) {
return parts.attribute === 'cname';
});
var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
var secondarySsrc;
var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID').map(function (line) {
var parts = line.substr(17).split(' ');
return parts.map(function (part) {
return parseInt(part, 10);
});
});
if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
secondarySsrc = flows[0][1];
}
description.codecs.forEach(function (codec) {
if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
var encParam = {
ssrc: primarySsrc,
codecPayloadType: parseInt(codec.parameters.apt, 10)
};
if (primarySsrc && secondarySsrc) {
encParam.rtx = {
ssrc: secondarySsrc
};
}
encodingParameters.push(encParam);
if (hasRed) {
encParam = JSON.parse(JSON.stringify(encParam));
encParam.fec = {
ssrc: primarySsrc,
mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
};
encodingParameters.push(encParam);
}
}
});
if (encodingParameters.length === 0 && primarySsrc) {
encodingParameters.push({
ssrc: primarySsrc
});
} // we support both b=AS and b=TIAS but interpret AS as TIAS.
var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
if (bandwidth.length) {
if (bandwidth[0].indexOf('b=TIAS:') === 0) {
bandwidth = parseInt(bandwidth[0].substr(7), 10);
} else if (bandwidth[0].indexOf('b=AS:') === 0) {
// use formula from JSEP to convert b=AS to TIAS value.
bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 - 50 * 40 * 8;
} else {
bandwidth = undefined;
}
encodingParameters.forEach(function (params) {
params.maxBitrate = bandwidth;
});
}
return encodingParameters;
}; // parses http://draft.ortc.org/#rtcrtcpparameters*
SDPUtils.parseRtcpParameters = function (mediaSection) {
var rtcpParameters = {}; // Gets the first SSRC. Note tha 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) {
rtcpParameters.cname = remoteSsrc.value;
rtcpParameters.ssrc = remoteSsrc.ssrc;
} // Edge uses the compound attribute instead of reducedSize
// compound is !reducedSize
var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
rtcpParameters.reducedSize = rsize.length > 0;
rtcpParameters.compound = rsize.length === 0; // parses the rtcp-mux attrіbute.
// Note that Edge does not support unmuxed RTCP.
var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
rtcpParameters.mux = mux.length > 0;
return rtcpParameters;
}; // parses either a=msid: or a=ssrc:... msid lines and returns
// the id of the MediaStream and MediaStreamTrack.
SDPUtils.parseMsid = function (mediaSection) {
var parts;
var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
if (spec.length === 1) {
parts = spec[0].substr(7).split(' ');
return {
stream: parts[0],
track: parts[1]
};
}
var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
return SDPUtils.parseSsrcMedia(line);
}).filter(function (msidParts) {
return msidParts.attribute === 'msid';
});
if (planB.length > 0) {
parts = planB[0].value.split(' ');
return {
stream: parts[0],
track: parts[1]
};
}
}; // Generate a session ID for SDP.
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
// recommends using a cryptographically random +ve 64-bit value
// but right now this should be acceptable and within the right range
SDPUtils.generateSessionId = function () {
return Math.random().toString().substr(2, 21);
}; // Write boilder plate for start of SDP
// sessId argument is optional - if not supplied it will
// be generated randomly
// sessVersion is optional and defaults to 2
// sessUser is optional and defaults to 'thisisadapterortc'
SDPUtils.writeSessionBoilerplate = function (sessId, sessVer, sessUser) {
var sessionId;
var version = sessVer !== undefined ? sessVer : 2;
if (sessId) {
sessionId = sessId;
} else {
sessionId = SDPUtils.generateSessionId();
}
var user = sessUser || 'thisisadapterortc'; // FIXME: sess-id should be an NTP timestamp.
return 'v=0\r\n' + 'o=' + user + ' ' + sessionId + ' ' + version + ' 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.direction) {
sdp += 'a=' + transceiver.direction + '\r\n';
} else 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';
}
if (transceiver.rtpSender) {
// spec.
var msid = 'msid:' + stream.id + ' ' + transceiver.rtpSender.track.id + '\r\n';
sdp += 'a=' + msid; // for Chrome.
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + ' ' + msid;
if (transceiver.sendEncodingParameters[0].rtx) {
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + ' ' + msid;
sdp += 'a=ssrc-group:FID ' + transceiver.sendEncodingParameters[0].ssrc + ' ' + transceiver.sendEncodingParameters[0].rtx.ssrc + '\r\n';
}
} // FIXME: this should be written by writeRtpDescription.
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + ' cname:' + SDPUtils.localCName + '\r\n';
if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + ' cname:' + SDPUtils.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);
default: // FIXME: What should happen here?
}
}
if (sessionpart) {
return SDPUtils.getDirection(sessionpart);
}
return 'sendrecv';
};
SDPUtils.getKind = function (mediaSection) {
var lines = SDPUtils.splitLines(mediaSection);
var mline = lines[0].split(' ');
return mline[0].substr(2);
};
SDPUtils.isRejected = function (mediaSection) {
return mediaSection.split(' ', 2)[1] === '0';
};
SDPUtils.parseMLine = function (mediaSection) {
var lines = SDPUtils.splitLines(mediaSection);
var parts = lines[0].substr(2).split(' ');
return {
kind: parts[0],
port: parseInt(parts[1], 10),
protocol: parts[2],
fmt: parts.slice(3).join(' ')
};
};
SDPUtils.parseOLine = function (mediaSection) {
var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
var parts = line.substr(2).split(' ');
return {
username: parts[0],
sessionId: parts[1],
sessionVersion: parseInt(parts[2], 10),
netType: parts[3],
addressType: parts[4],
address: parts[5]
};
}; // a very naive interpretation of a valid SDP.
SDPUtils.isValidSDP = function (blob) {
if (typeof blob !== 'string' || blob.length === 0) {
return false;
}
var lines = SDPUtils.splitLines(blob);
for (var i = 0; i < lines.length; i++) {
if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
return false;
} // TODO: check the modifier a bit more.
}
return true;
}; // Expose public methods.
if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === 'object') {
module.exports = SDPUtils;
}
},{}],15:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _adapter_factory = require('./adapter_factory.js');
var adapter = (0, _adapter_factory.adapterFactory)({
window: window
});
exports.default = adapter;
module.exports = exports.default;
},{"./adapter_factory.js":16}],16:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.adapterFactory = adapterFactory;
var _utils = require('./utils');
var utils = _interopRequireWildcard(_utils);
var _chrome_shim = require('./chrome/chrome_shim');
var chromeShim = _interopRequireWildcard(_chrome_shim);
var _edge_shim = require('./edge/edge_shim');
var edgeShim = _interopRequireWildcard(_edge_shim);
var _firefox_shim = require('./firefox/firefox_shim');
var firefoxShim = _interopRequireWildcard(_firefox_shim);
var _safari_shim = require('./safari/safari_shim');
var safariShim = _interopRequireWildcard(_safari_shim);
var _common_shim = require('./common_shim');
var commonShim = _interopRequireWildcard(_common_shim);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
} // Shimming starts here.
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
function adapterFactory() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
window = _ref.window;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
shimChrome: true,
shimFirefox: true,
shimEdge: true,
shimSafari: true
}; // Utils.
var logging = utils.log;
var browserDetails = utils.detectBrowser(window);
var adapter = {
browserDetails: browserDetails,
commonShim: commonShim,
extractVersion: utils.extractVersion,
disableLog: utils.disableLog,
disableWarnings: utils.disableWarnings
}; // Shim browser if found.
switch (browserDetails.browser) {
case 'chrome':
if (!chromeShim || !chromeShim.shimPeerConnection || !options.shimChrome) {
logging('Chrome shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming chrome.'); // Export to the adapter global object visible in the browser.
adapter.browserShim = chromeShim;
chromeShim.shimGetUserMedia(window);
chromeShim.shimMediaStream(window);
chromeShim.shimPeerConnection(window);
chromeShim.shimOnTrack(window);
chromeShim.shimAddTrackRemoveTrack(window);
chromeShim.shimGetSendersWithDtmf(window);
chromeShim.shimGetStats(window);
chromeShim.shimSenderReceiverGetStats(window);
chromeShim.fixNegotiationNeeded(window);
commonShim.shimRTCIceCandidate(window);
commonShim.shimConnectionState(window);
commonShim.shimMaxMessageSize(window);
commonShim.shimSendThrowTypeError(window);
commonShim.removeAllowExtmapMixed(window);
break;
case 'firefox':
if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) {
logging('Firefox shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming firefox.'); // Export to the adapter global object visible in the browser.
adapter.browserShim = firefoxShim;
firefoxShim.shimGetUserMedia(window);
firefoxShim.shimPeerConnection(window);
firefoxShim.shimOnTrack(window);
firefoxShim.shimRemoveStream(window);
firefoxShim.shimSenderGetStats(window);
firefoxShim.shimReceiverGetStats(window);
firefoxShim.shimRTCDataChannel(window);
commonShim.shimRTCIceCandidate(window);
commonShim.shimConnectionState(window);
commonShim.shimMaxMessageSize(window);
commonShim.shimSendThrowTypeError(window);
break;
case 'edge':
if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {
logging('MS edge shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming edge.'); // Export to the adapter global object visible in the browser.
adapter.browserShim = edgeShim;
edgeShim.shimGetUserMedia(window);
edgeShim.shimGetDisplayMedia(window);
edgeShim.shimPeerConnection(window);
edgeShim.shimReplaceTrack(window); // the edge shim implements the full RTCIceCandidate object.
commonShim.shimMaxMessageSize(window);
commonShim.shimSendThrowTypeError(window);
break;
case 'safari':
if (!safariShim || !options.shimSafari) {
logging('Safari shim is not included in this adapter release.');
return adapter;
}
logging('adapter.js shimming safari.'); // Export to the adapter global object visible in the browser.
adapter.browserShim = safariShim;
safariShim.shimRTCIceServerUrls(window);
safariShim.shimCreateOfferLegacy(window);
safariShim.shimCallbacksAPI(window);
safariShim.shimLocalStreamsAPI(window);
safariShim.shimRemoteStreamsAPI(window);
safariShim.shimTrackEventTransceiver(window);
safariShim.shimGetUserMedia(window);
commonShim.shimRTCIceCandidate(window);
commonShim.shimMaxMessageSize(window);
commonShim.shimSendThrowTypeError(window);
commonShim.removeAllowExtmapMixed(window);
break;
default:
logging('Unsupported browser!');
break;
}
return adapter;
} // Browser shims.
},{"./chrome/chrome_shim":17,"./common_shim":20,"./edge/edge_shim":21,"./firefox/firefox_shim":25,"./safari/safari_shim":28,"./utils":29}],17:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
var _getusermedia = require('./getusermedia');
Object.defineProperty(exports, 'shimGetUserMedia', {
enumerable: true,
get: function get() {
return _getusermedia.shimGetUserMedia;
}
});
var _getdisplaymedia = require('./getdisplaymedia');
Object.defineProperty(exports, 'shimGetDisplayMedia', {
enumerable: true,
get: function get() {
return _getdisplaymedia.shimGetDisplayMedia;
}
});
exports.shimMediaStream = shimMediaStream;
exports.shimOnTrack = shimOnTrack;
exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
exports.shimGetStats = shimGetStats;
exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;
exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;
exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack;
exports.shimPeerConnection = shimPeerConnection;
exports.fixNegotiationNeeded = fixNegotiationNeeded;
var _utils = require('../utils.js');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function shimMediaStream(window) {
window.MediaStream = window.MediaStream || window.webkitMediaStream;
}
function shimOnTrack(window) {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
get: function get() {
return this._ontrack;
},
set: function set(f) {
if (this._ontrack) {
this.removeEventListener('track', this._ontrack);
}
this.addEventListener('track', this._ontrack = f);
},
enumerable: true,
configurable: true
});
var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
var _this = this;
if (!this._ontrackpoly) {
this._ontrackpoly = function (e) {
// onaddstream does not fire when a track is added to an existing
// stream. But stream.onaddtrack is implemented so we use that.
e.stream.addEventListener('addtrack', function (te) {
var receiver = void 0;
if (window.RTCPeerConnection.prototype.getReceivers) {
receiver = _this.getReceivers().find(function (r) {
return r.track && r.track.id === te.track.id;
});
} else {
receiver = {
track: te.track
};
}
var event = new Event('track');
event.track = te.track;
event.receiver = receiver;
event.transceiver = {
receiver: receiver
};
event.streams = [e.stream];
_this.dispatchEvent(event);
});
e.stream.getTracks().forEach(function (track) {
var receiver = void 0;
if (window.RTCPeerConnection.prototype.getReceivers) {
receiver = _this.getReceivers().find(function (r) {
return r.track && r.track.id === track.id;
});
} else {
receiver = {
track: track
};
}
var event = new Event('track');
event.track = track;
event.receiver = receiver;
event.transceiver = {
receiver: receiver
};
event.streams = [e.stream];
_this.dispatchEvent(event);
});
};
this.addEventListener('addstream', this._ontrackpoly);
}
return origSetRemoteDescription.apply(this, arguments);
};
} else {
// even if RTCRtpTransceiver is in window, it is only used and
// emitted in unified-plan. Unfortunately this means we need
// to unconditionally wrap the event.
utils.wrapPeerConnectionEvent(window, 'track', function (e) {
if (!e.transceiver) {
Object.defineProperty(e, 'transceiver', {
value: {
receiver: e.receiver
}
});
}
return e;
});
}
}
function shimGetSendersWithDtmf(window) {
// Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) {
var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) {
return {
track: track,
get dtmf() {
if (this._dtmf === undefined) {
if (track.kind === 'audio') {
this._dtmf = pc.createDTMFSender(track);
} else {
this._dtmf = null;
}
}
return this._dtmf;
},
_pc: pc
};
}; // augment addTrack when getSenders is not available.
if (!window.RTCPeerConnection.prototype.getSenders) {
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
this._senders = this._senders || [];
return this._senders.slice(); // return a copy of the internal state.
};
var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
var sender = origAddTrack.apply(this, arguments);
if (!sender) {
sender = shimSenderWithDtmf(this, track);
this._senders.push(sender);
}
return sender;
};
var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
origRemoveTrack.apply(this, arguments);
var idx = this._senders.indexOf(sender);
if (idx !== -1) {
this._senders.splice(idx, 1);
}
};
}
var origAddStream = window.RTCPeerConnection.prototype.addStream;
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
var _this2 = this;
this._senders = this._senders || [];
origAddStream.apply(this, [stream]);
stream.getTracks().forEach(function (track) {
_this2._senders.push(shimSenderWithDtmf(_this2, track));
});
};
var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
var _this3 = this;
this._senders = this._senders || [];
origRemoveStream.apply(this, [stream]);
stream.getTracks().forEach(function (track) {
var sender = _this3._senders.find(function (s) {
return s.track === track;
});
if (sender) {
// remove sender
_this3._senders.splice(_this3._senders.indexOf(sender), 1);
}
});
};
} else if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
var _this4 = this;
var senders = origGetSenders.apply(this, []);
senders.forEach(function (sender) {
return sender._pc = _this4;
});
return senders;
};
Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
get: function get() {
if (this._dtmf === undefined) {
if (this.track.kind === 'audio') {
this._dtmf = this._pc.createDTMFSender(this.track);
} else {
this._dtmf = null;
}
}
return this._dtmf;
}
});
}
}
function shimGetStats(window) {
if (!window.RTCPeerConnection) {
return;
}
var origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function getStats() {
var _this5 = this;
var _arguments = Array.prototype.slice.call(arguments),
selector = _arguments[0],
onSucc = _arguments[1],
onErr = _arguments[2]; // If selector is a function then we are in the old style stats so just
// pass back the original getStats format to avoid breaking old users.
if (arguments.length > 0 && typeof selector === 'function') {
return origGetStats.apply(this, arguments);
} // When spec-style getStats is supported, return those when called with
// either no arguments or the selector argument is null.
if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) {
return origGetStats.apply(this, []);
}
var fixChromeStats_ = function fixChromeStats_(response) {
var standardReport = {};
var reports = response.result();
reports.forEach(function (report) {
var standardStats = {
id: report.id,
timestamp: report.timestamp,
type: {
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
}[report.type] || report.type
};
report.names().forEach(function (name) {
standardStats[name] = report.stat(name);
});
standardReport[standardStats.id] = standardStats;
});
return standardReport;
}; // shim getStats with maplike support
var makeMapStats = function makeMapStats(stats) {
return new Map(Object.keys(stats).map(function (key) {
return [key, stats[key]];
}));
};
if (arguments.length >= 2) {
var successCallbackWrapper_ = function successCallbackWrapper_(response) {
onSucc(makeMapStats(fixChromeStats_(response)));
};
return origGetStats.apply(this, [successCallbackWrapper_, selector]);
} // promise-support
return new Promise(function (resolve, reject) {
origGetStats.apply(_this5, [function (response) {
resolve(makeMapStats(fixChromeStats_(response)));
}, reject]);
}).then(onSucc, onErr);
};
}
function shimSenderReceiverGetStats(window) {
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {
return;
} // shim sender stats.
if (!('getStats' in window.RTCRtpSender.prototype)) {
var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
if (origGetSenders) {
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
var _this6 = this;
var senders = origGetSenders.apply(this, []);
senders.forEach(function (sender) {
return sender._pc = _this6;
});
return senders;
};
}
var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
if (origAddTrack) {
window.RTCPeerConnection.prototype.addTrack = function addTrack() {
var sender = origAddTrack.apply(this, arguments);
sender._pc = this;
return sender;
};
}
window.RTCRtpSender.prototype.getStats = function getStats() {
var sender = this;
return this._pc.getStats().then(function (result) {
return (
/* Note: this will include stats of all senders that
* send a track with the same id as sender.track as
* it is not possible to identify the RTCRtpSender.
*/
utils.filterStats(result, sender.track, true)
);
});
};
} // shim receiver stats.
if (!('getStats' in window.RTCRtpReceiver.prototype)) {
var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
if (origGetReceivers) {
window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
var _this7 = this;
var receivers = origGetReceivers.apply(this, []);
receivers.forEach(function (receiver) {
return receiver._pc = _this7;
});
return receivers;
};
}
utils.wrapPeerConnectionEvent(window, 'track', function (e) {
e.receiver._pc = e.srcElement;
return e;
});
window.RTCRtpReceiver.prototype.getStats = function getStats() {
var receiver = this;
return this._pc.getStats().then(function (result) {
return utils.filterStats(result, receiver.track, false);
});
};
}
if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) {
return;
} // shim RTCPeerConnection.getStats(track).
var origGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function getStats() {
if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) {
var track = arguments[0];
var sender = void 0;
var receiver = void 0;
var err = void 0;
this.getSenders().forEach(function (s) {
if (s.track === track) {
if (sender) {
err = true;
} else {
sender = s;
}
}
});
this.getReceivers().forEach(function (r) {
if (r.track === track) {
if (receiver) {
err = true;
} else {
receiver = r;
}
}
return r.track === track;
});
if (err || sender && receiver) {
return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError'));
} else if (sender) {
return sender.getStats();
} else if (receiver) {
return receiver.getStats();
}
return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError'));
}
return origGetStats.apply(this, arguments);
};
}
function shimAddTrackRemoveTrackWithNative(window) {
// shim addTrack/removeTrack with native variants in order to make
// the interactions with legacy getLocalStreams behave as in other browsers.
// Keeps a mapping stream.id => [stream, rtpsenders...]
window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
var _this8 = this;
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
return Object.keys(this._shimmedLocalStreams).map(function (streamId) {
return _this8._shimmedLocalStreams[streamId][0];
});
};
var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
if (!stream) {
return origAddTrack.apply(this, arguments);
}
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
var sender = origAddTrack.apply(this, arguments);
if (!this._shimmedLocalStreams[stream.id]) {
this._shimmedLocalStreams[stream.id] = [stream, sender];
} else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
this._shimmedLocalStreams[stream.id].push(sender);
}
return sender;
};
var origAddStream = window.RTCPeerConnection.prototype.addStream;
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
var _this9 = this;
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
stream.getTracks().forEach(function (track) {
var alreadyExists = _this9.getSenders().find(function (s) {
return s.track === track;
});
if (alreadyExists) {
throw new DOMException('Track already exists.', 'InvalidAccessError');
}
});
var existingSenders = this.getSenders();
origAddStream.apply(this, arguments);
var newSenders = this.getSenders().filter(function (newSender) {
return existingSenders.indexOf(newSender) === -1;
});
this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
};
var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
delete this._shimmedLocalStreams[stream.id];
return origRemoveStream.apply(this, arguments);
};
var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
var _this10 = this;
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
if (sender) {
Object.keys(this._shimmedLocalStreams).forEach(function (streamId) {
var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender);
if (idx !== -1) {
_this10._shimmedLocalStreams[streamId].splice(idx, 1);
}
if (_this10._shimmedLocalStreams[streamId].length === 1) {
delete _this10._shimmedLocalStreams[streamId];
}
});
}
return origRemoveTrack.apply(this, arguments);
};
}
function shimAddTrackRemoveTrack(window) {
if (!window.RTCPeerConnection) {
return;
}
var browserDetails = utils.detectBrowser(window); // shim addTrack and removeTrack.
if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) {
return shimAddTrackRemoveTrackWithNative(window);
} // also shim pc.getLocalStreams when addTrack is shimmed
// to return the original streams.
var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams;
window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
var _this11 = this;
var nativeStreams = origGetLocalStreams.apply(this);
this._reverseStreams = this._reverseStreams || {};
return nativeStreams.map(function (stream) {
return _this11._reverseStreams[stream.id];
});
};
var origAddStream = window.RTCPeerConnection.prototype.addStream;
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
var _this12 = this;
this._streams = this._streams || {};
this._reverseStreams = this._reverseStreams || {};
stream.getTracks().forEach(function (track) {
var alreadyExists = _this12.getSenders().find(function (s) {
return s.track === track;
});
if (alreadyExists) {
throw new DOMException('Track already exists.', 'InvalidAccessError');
}
}); // Add identity mapping for consistency with addTrack.
// Unless this is being used with a stream from addTrack.
if (!this._reverseStreams[stream.id]) {
var newStream = new window.MediaStream(stream.getTracks());
this._streams[stream.id] = newStream;
this._reverseStreams[newStream.id] = stream;
stream = newStream;
}
origAddStream.apply(this, [stream]);
};
var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
this._streams = this._streams || {};
this._reverseStreams = this._reverseStreams || {};
origRemoveStream.apply(this, [this._streams[stream.id] || stream]);
delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id];
delete this._streams[stream.id];
};
window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
var _this13 = this;
if (this.signalingState === 'closed') {
throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
}
var streams = [].slice.call(arguments, 1);
if (streams.length !== 1 || !streams[0].getTracks().find(function (t) {
return t === track;
})) {
// this is not fully correct but all we can manage without
// [[associated MediaStreams]] internal slot.
throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError');
}
var alreadyExists = this.getSenders().find(function (s) {
return s.track === track;
});
if (alreadyExists) {
throw new DOMException('Track already exists.', 'InvalidAccessError');
}
this._streams = this._streams || {};
this._reverseStreams = this._reverseStreams || {};
var oldStream = this._streams[stream.id];
if (oldStream) {
// this is using odd Chrome behaviour, use with caution:
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
// Note: we rely on the high-level addTrack/dtmf shim to
// create the sender with a dtmf sender.
oldStream.addTrack(track); // Trigger ONN async.
Promise.resolve().then(function () {
_this13.dispatchEvent(new Event('negotiationneeded'));
});
} else {
var newStream = new window.MediaStream([track]);
this._streams[stream.id] = newStream;
this._reverseStreams[newStream.id] = stream;
this.addStream(newStream);
}
return this.getSenders().find(function (s) {
return s.track === track;
});
}; // replace the internal stream id with the external one and
// vice versa.
function replaceInternalStreamId(pc, description) {
var sdp = description.sdp;
Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
var externalStream = pc._reverseStreams[internalId];
var internalStream = pc._streams[externalStream.id];
sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id);
});
return new RTCSessionDescription({
type: description.type,
sdp: sdp
});
}
function replaceExternalStreamId(pc, description) {
var sdp = description.sdp;
Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
var externalStream = pc._reverseStreams[internalId];
var internalStream = pc._streams[externalStream.id];
sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id);
});
return new RTCSessionDescription({
type: description.type,
sdp: sdp
});
}
['createOffer', 'createAnswer'].forEach(function (method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
var methodObj = _defineProperty({}, method, function () {
var _this14 = this;
var args = arguments;
var isLegacyCall = arguments.length && typeof arguments[0] === 'function';
if (isLegacyCall) {
return nativeMethod.apply(this, [function (description) {
var desc = replaceInternalStreamId(_this14, description);
args[0].apply(null, [desc]);
}, function (err) {
if (args[1]) {
args[1].apply(null, err);
}
}, arguments[2]]);
}
return nativeMethod.apply(this, arguments).then(function (description) {
return replaceInternalStreamId(_this14, description);
});
});
window.RTCPeerConnection.prototype[method] = methodObj[method];
});
var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
if (!arguments.length || !arguments[0].type) {
return origSetLocalDescription.apply(this, arguments);
}
arguments[0] = replaceExternalStreamId(this, arguments[0]);
return origSetLocalDescription.apply(this, arguments);
}; // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription');
Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', {
get: function get() {
var description = origLocalDescription.get.apply(this);
if (description.type === '') {
return description;
}
return replaceInternalStreamId(this, description);
}
});
window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
var _this15 = this;
if (this.signalingState === 'closed') {
throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
} // We can not yet check for sender instanceof RTCRtpSender
// since we shim RTPSender. So we check if sender._pc is set.
if (!sender._pc) {
throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError');
}
var isLocal = sender._pc === this;
if (!isLocal) {
throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError');
} // Search for the native stream the senders track belongs to.
this._streams = this._streams || {};
var stream = void 0;
Object.keys(this._streams).forEach(function (streamid) {
var hasTrack = _this15._streams[streamid].getTracks().find(function (track) {
return sender.track === track;
});
if (hasTrack) {
stream = _this15._streams[streamid];
}
});
if (stream) {
if (stream.getTracks().length === 1) {
// if this is the last track of the stream, remove the stream. This
// takes care of any shimmed _senders.
this.removeStream(this._reverseStreams[stream.id]);
} else {
// relying on the same odd chrome behaviour as above.
stream.removeTrack(sender.track);
}
this.dispatchEvent(new Event('negotiationneeded'));
}
};
}
function shimPeerConnection(window) {
var browserDetails = utils.detectBrowser(window);
if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
// very basic support for old versions.
window.RTCPeerConnection = window.webkitRTCPeerConnection;
}
if (!window.RTCPeerConnection) {
return;
} // shim implicit creation of RTCSessionDescription/RTCIceCandidate
if (browserDetails.version < 53) {
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
var methodObj = _defineProperty({}, method, function () {
arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
});
window.RTCPeerConnection.prototype[method] = methodObj[method];
});
} // support for addIceCandidate(null or undefined)
var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() {
if (!arguments[0]) {
if (arguments[1]) {
arguments[1].apply(null);
}
return Promise.resolve();
} // Firefox 68+ emits and processes {candidate: "", ...}, ignore
// in older versions. Native support planned for Chrome M77.
if (browserDetails.version < 78 && arguments[0] && arguments[0].candidate === '') {
return Promise.resolve();
}
return nativeAddIceCandidate.apply(this, arguments);
};
}
function fixNegotiationNeeded(window) {
utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) {
var pc = e.target;
if (pc.signalingState !== 'stable') {
return;
}
return e;
});
}
},{"../utils.js":29,"./getdisplaymedia":18,"./getusermedia":19}],18:[function(require,module,exports){
/*
* Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetDisplayMedia = shimGetDisplayMedia;
function shimGetDisplayMedia(window, getSourceId) {
if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
return;
}
if (!window.navigator.mediaDevices) {
return;
} // getSourceId is a function that returns a promise resolving with
// the sourceId of the screen/window/tab to be shared.
if (typeof getSourceId !== 'function') {
console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function');
return;
}
window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
return getSourceId(constraints).then(function (sourceId) {
var widthSpecified = constraints.video && constraints.video.width;
var heightSpecified = constraints.video && constraints.video.height;
var frameRateSpecified = constraints.video && constraints.video.frameRate;
constraints.video = {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId,
maxFrameRate: frameRateSpecified || 3
}
};
if (widthSpecified) {
constraints.video.mandatory.maxWidth = widthSpecified;
}
if (heightSpecified) {
constraints.video.mandatory.maxHeight = heightSpecified;
}
return window.navigator.mediaDevices.getUserMedia(constraints);
});
};
}
},{}],19:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
exports.shimGetUserMedia = shimGetUserMedia;
var _utils = require('../utils.js');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
var logging = utils.log;
function shimGetUserMedia(window) {
var navigator = window && window.navigator;
if (!navigator.mediaDevices) {
return;
}
var browserDetails = utils.detectBrowser(window);
var constraintsToChrome_ = function constraintsToChrome_(c) {
if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) !== 'object' || c.mandatory || c.optional) {
return c;
}
var cc = {};
Object.keys(c).forEach(function (key) {
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
return;
}
var r = _typeof(c[key]) === 'object' ? c[key] : {
ideal: c[key]
};
if (r.exact !== undefined && typeof r.exact === 'number') {
r.min = r.max = r.exact;
}
var oldname_ = function oldname_(prefix, name) {
if (prefix) {
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
}
return name === 'deviceId' ? 'sourceId' : name;
};
if (r.ideal !== undefined) {
cc.optional = cc.optional || [];
var oc = {};
if (typeof r.ideal === 'number') {
oc[oldname_('min', key)] = r.ideal;
cc.optional.push(oc);
oc = {};
oc[oldname_('max', key)] = r.ideal;
cc.optional.push(oc);
} else {
oc[oldname_('', key)] = r.ideal;
cc.optional.push(oc);
}
}
if (r.exact !== undefined && typeof r.exact !== 'number') {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname_('', key)] = r.exact;
} else {
['min', 'max'].forEach(function (mix) {
if (r[mix] !== undefined) {
cc.mandatory = cc.mandatory || {};
cc.mandatory[oldname_(mix, key)] = r[mix];
}
});
}
});
if (c.advanced) {
cc.optional = (cc.optional || []).concat(c.advanced);
}
return cc;
};
var shimConstraints_ = function shimConstraints_(constraints, func) {
if (browserDetails.version >= 61) {
return func(constraints);
}
constraints = JSON.parse(JSON.stringify(constraints));
if (constraints && _typeof(constraints.audio) === 'object') {
var remap = function remap(obj, a, b) {
if (a in obj && !(b in obj)) {
obj[b] = obj[a];
delete obj[a];
}
};
constraints = JSON.parse(JSON.stringify(constraints));
remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
constraints.audio = constraintsToChrome_(constraints.audio);
}
if (constraints && _typeof(constraints.video) === 'object') {
// Shim facingMode for mobile & surface pro.
var face = constraints.video.facingMode;
face = face && ((typeof face === 'undefined' ? 'undefined' : _typeof(face)) === 'object' ? face : {
ideal: face
});
var getSupportedFacingModeLies = browserDetails.version < 66;
if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) {
delete constraints.video.facingMode;
var matches = void 0;
if (face.exact === 'environment' || face.ideal === 'environment') {
matches = ['back', 'rear'];
} else if (face.exact === 'user' || face.ideal === 'user') {
matches = ['front'];
}
if (matches) {
// Look for matches in label, or use last cam for back (typical).
return navigator.mediaDevices.enumerateDevices().then(function (devices) {
devices = devices.filter(function (d) {
return d.kind === 'videoinput';
});
var dev = devices.find(function (d) {
return matches.some(function (match) {
return d.label.toLowerCase().includes(match);
});
});
if (!dev && devices.length && matches.includes('back')) {
dev = devices[devices.length - 1]; // more likely the back cam
}
if (dev) {
constraints.video.deviceId = face.exact ? {
exact: dev.deviceId
} : {
ideal: dev.deviceId
};
}
constraints.video = constraintsToChrome_(constraints.video);
logging('chrome: ' + JSON.stringify(constraints));
return func(constraints);
});
}
}
constraints.video = constraintsToChrome_(constraints.video);
}
logging('chrome: ' + JSON.stringify(constraints));
return func(constraints);
};
var shimError_ = function shimError_(e) {
if (browserDetails.version >= 64) {
return e;
}
return {
name: {
PermissionDeniedError: 'NotAllowedError',
PermissionDismissedError: 'NotAllowedError',
InvalidStateError: 'NotAllowedError',
DevicesNotFoundError: 'NotFoundError',
ConstraintNotSatisfiedError: 'OverconstrainedError',
TrackStartError: 'NotReadableError',
MediaDeviceFailedDueToShutdown: 'NotAllowedError',
MediaDeviceKillSwitchOn: 'NotAllowedError',
TabCaptureError: 'AbortError',
ScreenCaptureError: 'AbortError',
DeviceCaptureError: 'AbortError'
}[e.name] || e.name,
message: e.message,
constraint: e.constraint || e.constraintName,
toString: function toString() {
return this.name + (this.message && ': ') + this.message;
}
};
};
var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) {
shimConstraints_(constraints, function (c) {
navigator.webkitGetUserMedia(c, onSuccess, function (e) {
if (onError) {
onError(shimError_(e));
}
});
});
};
navigator.getUserMedia = getUserMedia_.bind(navigator); // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
// function which returns a Promise, it does not accept spec-style
// constraints.
if (navigator.mediaDevices.getUserMedia) {
var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function (cs) {
return shimConstraints_(cs, function (c) {
return origGetUserMedia(c).then(function (stream) {
if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
stream.getTracks().forEach(function (track) {
track.stop();
});
throw new DOMException('', 'NotFoundError');
}
return stream;
}, function (e) {
return Promise.reject(shimError_(e));
});
});
};
}
}
},{"../utils.js":29}],20:[function(require,module,exports){
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
exports.shimRTCIceCandidate = shimRTCIceCandidate;
exports.shimMaxMessageSize = shimMaxMessageSize;
exports.shimSendThrowTypeError = shimSendThrowTypeError;
exports.shimConnectionState = shimConnectionState;
exports.removeAllowExtmapMixed = removeAllowExtmapMixed;
var _sdp = require('sdp');
var _sdp2 = _interopRequireDefault(_sdp);
var _utils = require('./utils');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function shimRTCIceCandidate(window) {
// foundation is arbitrarily chosen as an indicator for full support for
// https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) {
return;
}
var NativeRTCIceCandidate = window.RTCIceCandidate;
window.RTCIceCandidate = function RTCIceCandidate(args) {
// Remove the a= which shouldn't be part of the candidate string.
if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) {
args = JSON.parse(JSON.stringify(args));
args.candidate = args.candidate.substr(2);
}
if (args.candidate && args.candidate.length) {
// Augment the native candidate with the parsed fields.
var nativeCandidate = new NativeRTCIceCandidate(args);
var parsedCandidate = _sdp2.default.parseCandidate(args.candidate);
var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate); // Add a serializer that does not serialize the extra attributes.
augmentedCandidate.toJSON = function toJSON() {
return {
candidate: augmentedCandidate.candidate,
sdpMid: augmentedCandidate.sdpMid,
sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
usernameFragment: augmentedCandidate.usernameFragment
};
};
return augmentedCandidate;
}
return new NativeRTCIceCandidate(args);
};
window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype; // Hook up the augmented candidate in onicecandidate and
// addEventListener('icecandidate', ...)
utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) {
if (e.candidate) {
Object.defineProperty(e, 'candidate', {
value: new window.RTCIceCandidate(e.candidate),
writable: 'false'
});
}
return e;
});
}
function shimMaxMessageSize(window) {
if (!window.RTCPeerConnection) {
return;
}
var browserDetails = utils.detectBrowser(window);
if (!('sctp' in window.RTCPeerConnection.prototype)) {
Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
get: function get() {
return typeof this._sctp === 'undefined' ? null : this._sctp;
}
});
}
var sctpInDescription = function sctpInDescription(description) {
if (!description || !description.sdp) {
return false;
}
var sections = _sdp2.default.splitSections(description.sdp);
sections.shift();
return sections.some(function (mediaSection) {
var mLine = _sdp2.default.parseMLine(mediaSection);
return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1;
});
};
var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) {
// TODO: Is there a better solution for detecting Firefox?
var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
if (match === null || match.length < 2) {
return -1;
}
var version = parseInt(match[1], 10); // Test for NaN (yes, this is ugly)
return version !== version ? -1 : version;
};
var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) {
// Every implementation we know can send at least 64 KiB.
// Note: Although Chrome is technically able to send up to 256 KiB, the
// data does not reach the other peer reliably.
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
var canSendMaxMessageSize = 65536;
if (browserDetails.browser === 'firefox') {
if (browserDetails.version < 57) {
if (remoteIsFirefox === -1) {
// FF < 57 will send in 16 KiB chunks using the deprecated PPID
// fragmentation.
canSendMaxMessageSize = 16384;
} else {
// However, other FF (and RAWRTC) can reassemble PPID-fragmented
// messages. Thus, supporting ~2 GiB when sending.
canSendMaxMessageSize = 2147483637;
}
} else if (browserDetails.version < 60) {
// Currently, all FF >= 57 will reset the remote maximum message size
// to the default value when a data channel is created at a later
// stage. :(
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536;
} else {
// FF >= 60 supports sending ~2 GiB
canSendMaxMessageSize = 2147483637;
}
}
return canSendMaxMessageSize;
};
var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) {
// Note: 65536 bytes is the default value from the SDP spec. Also,
// every implementation we know supports receiving 65536 bytes.
var maxMessageSize = 65536; // FF 57 has a slightly incorrect default remote max message size, so
// we need to adjust it here to avoid a failure when sending.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
if (browserDetails.browser === 'firefox' && browserDetails.version === 57) {
maxMessageSize = 65535;
}
var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:');
if (match.length > 0) {
maxMessageSize = parseInt(match[0].substr(19), 10);
} else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) {
// If the maximum message size is not present in the remote SDP and
// both local and remote are Firefox, the remote peer can receive
// ~2 GiB.
maxMessageSize = 2147483637;
}
return maxMessageSize;
};
var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
this._sctp = null; // Chrome decided to not expose .sctp in plan-b mode.
// As usual, adapter.js has to do an 'ugly worakaround'
// to cover up the mess.
if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
var _getConfiguration = this.getConfiguration(),
sdpSemantics = _getConfiguration.sdpSemantics;
if (sdpSemantics === 'plan-b') {
Object.defineProperty(this, 'sctp', {
get: function get() {
return typeof this._sctp === 'undefined' ? null : this._sctp;
},
enumerable: true,
configurable: true
});
}
}
if (sctpInDescription(arguments[0])) {
// Check if the remote is FF.
var isFirefox = getRemoteFirefoxVersion(arguments[0]); // Get the maximum message size the local peer is capable of sending
var canSendMMS = getCanSendMaxMessageSize(isFirefox); // Get the maximum message size of the remote peer.
var remoteMMS = getMaxMessageSize(arguments[0], isFirefox); // Determine final maximum message size
var maxMessageSize = void 0;
if (canSendMMS === 0 && remoteMMS === 0) {
maxMessageSize = Number.POSITIVE_INFINITY;
} else if (canSendMMS === 0 || remoteMMS === 0) {
maxMessageSize = Math.max(canSendMMS, remoteMMS);
} else {
maxMessageSize = Math.min(canSendMMS, remoteMMS);
} // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
// attribute.
var sctp = {};
Object.defineProperty(sctp, 'maxMessageSize', {
get: function get() {
return maxMessageSize;
}
});
this._sctp = sctp;
}
return origSetRemoteDescription.apply(this, arguments);
};
}
function shimSendThrowTypeError(window) {
if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) {
return;
} // Note: Although Firefox >= 57 has a native implementation, the maximum
// message size can be reset for all data channels at a later stage.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
function wrapDcSend(dc, pc) {
var origDataChannelSend = dc.send;
dc.send = function send() {
var data = arguments[0];
var length = data.length || data.size || data.byteLength;
if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) {
throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)');
}
return origDataChannelSend.apply(dc, arguments);
};
}
var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel;
window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() {
var dataChannel = origCreateDataChannel.apply(this, arguments);
wrapDcSend(dataChannel, this);
return dataChannel;
};
utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) {
wrapDcSend(e.channel, e.target);
return e;
});
}
/* shims RTCConnectionState by pretending it is the same as iceConnectionState.
* See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
* for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
* since DTLS failures would be hidden. See
* https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
* for the Firefox tracking bug.
*/
function shimConnectionState(window) {
if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) {
return;
}
var proto = window.RTCPeerConnection.prototype;
Object.defineProperty(proto, 'connectionState', {
get: function get() {
return {
completed: 'connected',
checking: 'connecting'
}[this.iceConnectionState] || this.iceConnectionState;
},
enumerable: true,
configurable: true
});
Object.defineProperty(proto, 'onconnectionstatechange', {
get: function get() {
return this._onconnectionstatechange || null;
},
set: function set(cb) {
if (this._onconnectionstatechange) {
this.removeEventListener('connectionstatechange', this._onconnectionstatechange);
delete this._onconnectionstatechange;
}
if (cb) {
this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb);
}
},
enumerable: true,
configurable: true
});
['setLocalDescription', 'setRemoteDescription'].forEach(function (method) {
var origMethod = proto[method];
proto[method] = function () {
if (!this._connectionstatechangepoly) {
this._connectionstatechangepoly = function (e) {
var pc = e.target;
if (pc._lastConnectionState !== pc.connectionState) {
pc._lastConnectionState = pc.connectionState;
var newEvent = new Event('connectionstatechange', e);
pc.dispatchEvent(newEvent);
}
return e;
};
this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly);
}
return origMethod.apply(this, arguments);
};
});
}
function removeAllowExtmapMixed(window) {
/* remove a=extmap-allow-mixed for Chrome < M71 */
if (!window.RTCPeerConnection) {
return;
}
var browserDetails = utils.detectBrowser(window);
if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
return;
}
var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) {
if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
desc.sdp = desc.sdp.split('\n').filter(function (line) {
return line.trim() !== 'a=extmap-allow-mixed';
}).join('\n');
}
return nativeSRD.apply(this, arguments);
};
}
},{"./utils":29,"sdp":14}],21:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
var _getusermedia = require('./getusermedia');
Object.defineProperty(exports, 'shimGetUserMedia', {
enumerable: true,
get: function get() {
return _getusermedia.shimGetUserMedia;
}
});
var _getdisplaymedia = require('./getdisplaymedia');
Object.defineProperty(exports, 'shimGetDisplayMedia', {
enumerable: true,
get: function get() {
return _getdisplaymedia.shimGetDisplayMedia;
}
});
exports.shimPeerConnection = shimPeerConnection;
exports.shimReplaceTrack = shimReplaceTrack;
var _utils = require('../utils');
var utils = _interopRequireWildcard(_utils);
var _filtericeservers = require('./filtericeservers');
var _rtcpeerconnectionShim = require('rtcpeerconnection-shim');
var _rtcpeerconnectionShim2 = _interopRequireDefault(_rtcpeerconnectionShim);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function shimPeerConnection(window) {
var browserDetails = utils.detectBrowser(window);
if (window.RTCIceGatherer) {
if (!window.RTCIceCandidate) {
window.RTCIceCandidate = function RTCIceCandidate(args) {
return args;
};
}
if (!window.RTCSessionDescription) {
window.RTCSessionDescription = function RTCSessionDescription(args) {
return args;
};
} // this adds an additional event listener to MediaStrackTrack that signals
// when a tracks enabled property was changed. Workaround for a bug in
// addStream, see below. No longer required in 15025+
if (browserDetails.version < 15025) {
var origMSTEnabled = Object.getOwnPropertyDescriptor(window.MediaStreamTrack.prototype, 'enabled');
Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
set: function set(value) {
origMSTEnabled.set.call(this, value);
var ev = new Event('enabled');
ev.enabled = value;
this.dispatchEvent(ev);
}
});
}
} // ORTC defines the DTMF sender a bit different.
// https://github.com/w3c/ortc/issues/714
if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
get: function get() {
if (this._dtmf === undefined) {
if (this.track.kind === 'audio') {
this._dtmf = new window.RTCDtmfSender(this);
} else if (this.track.kind === 'video') {
this._dtmf = null;
}
}
return this._dtmf;
}
});
} // Edge currently only implements the RTCDtmfSender, not the
// RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*
if (window.RTCDtmfSender && !window.RTCDTMFSender) {
window.RTCDTMFSender = window.RTCDtmfSender;
}
var RTCPeerConnectionShim = (0, _rtcpeerconnectionShim2.default)(window, browserDetails.version);
window.RTCPeerConnection = function RTCPeerConnection(config) {
if (config && config.iceServers) {
config.iceServers = (0, _filtericeservers.filterIceServers)(config.iceServers, browserDetails.version);
utils.log('ICE servers after filtering:', config.iceServers);
}
return new RTCPeerConnectionShim(config);
};
window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype;
}
function shimReplaceTrack(window) {
// ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
if (window.RTCRtpSender && !('replaceTrack' in window.RTCRtpSender.prototype)) {
window.RTCRtpSender.prototype.replaceTrack = window.RTCRtpSender.prototype.setTrack;
}
}
},{"../utils":29,"./filtericeservers":22,"./getdisplaymedia":23,"./getusermedia":24,"rtcpeerconnection-shim":13}],22:[function(require,module,exports){
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.filterIceServers = filterIceServers;
var _utils = require('../utils');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
} // Edge does not like
// 1) stun: filtered after 14393 unless ?transport=udp is present
// 2) turn: that does not have all of turn:host:port?transport=udp
// 3) turn: with ipv6 addresses
// 4) turn: occurring muliple times
function filterIceServers(iceServers, edgeVersion) {
var hasTurn = false;
iceServers = JSON.parse(JSON.stringify(iceServers));
return iceServers.filter(function (server) {
if (server && (server.urls || server.url)) {
var urls = server.urls || server.url;
if (server.url && !server.urls) {
utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
}
var isString = typeof urls === 'string';
if (isString) {
urls = [urls];
}
urls = urls.filter(function (url) {
// filter STUN unconditionally.
if (url.indexOf('stun:') === 0) {
return false;
}
var validTurn = url.startsWith('turn') && !url.startsWith('turn:[') && url.includes('transport=udp');
if (validTurn && !hasTurn) {
hasTurn = true;
return true;
}
return validTurn && !hasTurn;
});
delete server.url;
server.urls = isString ? urls[0] : urls;
return !!urls.length;
}
});
}
},{"../utils":29}],23:[function(require,module,exports){
/*
* Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetDisplayMedia = shimGetDisplayMedia;
function shimGetDisplayMedia(window) {
if (!('getDisplayMedia' in window.navigator)) {
return;
}
if (!window.navigator.mediaDevices) {
return;
}
if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
return;
}
window.navigator.mediaDevices.getDisplayMedia = window.navigator.getDisplayMedia.bind(window.navigator);
}
},{}],24:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetUserMedia = shimGetUserMedia;
function shimGetUserMedia(window) {
var navigator = window && window.navigator;
var shimError_ = function shimError_(e) {
return {
name: {
PermissionDeniedError: 'NotAllowedError'
}[e.name] || e.name,
message: e.message,
constraint: e.constraint,
toString: function toString() {
return this.name;
}
};
}; // getUserMedia error shim.
var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function (c) {
return origGetUserMedia(c).catch(function (e) {
return Promise.reject(shimError_(e));
});
};
}
},{}],25:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
var _getusermedia = require('./getusermedia');
Object.defineProperty(exports, 'shimGetUserMedia', {
enumerable: true,
get: function get() {
return _getusermedia.shimGetUserMedia;
}
});
var _getdisplaymedia = require('./getdisplaymedia');
Object.defineProperty(exports, 'shimGetDisplayMedia', {
enumerable: true,
get: function get() {
return _getdisplaymedia.shimGetDisplayMedia;
}
});
exports.shimOnTrack = shimOnTrack;
exports.shimPeerConnection = shimPeerConnection;
exports.shimSenderGetStats = shimSenderGetStats;
exports.shimReceiverGetStats = shimReceiverGetStats;
exports.shimRemoveStream = shimRemoveStream;
exports.shimRTCDataChannel = shimRTCDataChannel;
var _utils = require('../utils');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function shimOnTrack(window) {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
get: function get() {
return {
receiver: this.receiver
};
}
});
}
}
function shimPeerConnection(window) {
var browserDetails = utils.detectBrowser(window);
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
return; // probably media.peerconnection.enabled=false in about:config
}
if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
// very basic support for old versions.
window.RTCPeerConnection = window.mozRTCPeerConnection;
}
if (browserDetails.version < 53) {
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
var nativeMethod = window.RTCPeerConnection.prototype[method];
var methodObj = _defineProperty({}, method, function () {
arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
return nativeMethod.apply(this, arguments);
});
window.RTCPeerConnection.prototype[method] = methodObj[method];
});
} // support for addIceCandidate(null or undefined)
var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() {
if (!arguments[0]) {
if (arguments[1]) {
arguments[1].apply(null);
}
return Promise.resolve();
} // Firefox 68+ emits and processes {candidate: "", ...}, ignore
// in older versions.
if (browserDetails.version < 68 && arguments[0] && arguments[0].candidate === '') {
return Promise.resolve();
}
return nativeAddIceCandidate.apply(this, arguments);
};
var modernStatsTypes = {
inboundrtp: 'inbound-rtp',
outboundrtp: 'outbound-rtp',
candidatepair: 'candidate-pair',
localcandidate: 'local-candidate',
remotecandidate: 'remote-candidate'
};
var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
window.RTCPeerConnection.prototype.getStats = function getStats() {
var _arguments = Array.prototype.slice.call(arguments),
selector = _arguments[0],
onSucc = _arguments[1],
onErr = _arguments[2];
return nativeGetStats.apply(this, [selector || null]).then(function (stats) {
if (browserDetails.version < 53 && !onSucc) {
// Shim only promise getStats with spec-hyphens in type names
// Leave callback version alone; misc old uses of forEach before Map
try {
stats.forEach(function (stat) {
stat.type = modernStatsTypes[stat.type] || stat.type;
});
} catch (e) {
if (e.name !== 'TypeError') {
throw e;
} // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
stats.forEach(function (stat, i) {
stats.set(i, Object.assign({}, stat, {
type: modernStatsTypes[stat.type] || stat.type
}));
});
}
}
return stats;
}).then(onSucc, onErr);
};
}
function shimSenderGetStats(window) {
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
return;
}
if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
return;
}
var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
if (origGetSenders) {
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
var _this = this;
var senders = origGetSenders.apply(this, []);
senders.forEach(function (sender) {
return sender._pc = _this;
});
return senders;
};
}
var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
if (origAddTrack) {
window.RTCPeerConnection.prototype.addTrack = function addTrack() {
var sender = origAddTrack.apply(this, arguments);
sender._pc = this;
return sender;
};
}
window.RTCRtpSender.prototype.getStats = function getStats() {
return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map());
};
}
function shimReceiverGetStats(window) {
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
return;
}
if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
return;
}
var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
if (origGetReceivers) {
window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
var _this2 = this;
var receivers = origGetReceivers.apply(this, []);
receivers.forEach(function (receiver) {
return receiver._pc = _this2;
});
return receivers;
};
}
utils.wrapPeerConnectionEvent(window, 'track', function (e) {
e.receiver._pc = e.srcElement;
return e;
});
window.RTCRtpReceiver.prototype.getStats = function getStats() {
return this._pc.getStats(this.track);
};
}
function shimRemoveStream(window) {
if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) {
return;
}
window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
var _this3 = this;
utils.deprecated('removeStream', 'removeTrack');
this.getSenders().forEach(function (sender) {
if (sender.track && stream.getTracks().includes(sender.track)) {
_this3.removeTrack(sender);
}
});
};
}
function shimRTCDataChannel(window) {
// rename DataChannel to RTCDataChannel (native fix in FF60):
// https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
if (window.DataChannel && !window.RTCDataChannel) {
window.RTCDataChannel = window.DataChannel;
}
}
},{"../utils":29,"./getdisplaymedia":26,"./getusermedia":27}],26:[function(require,module,exports){
/*
* Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.shimGetDisplayMedia = shimGetDisplayMedia;
function shimGetDisplayMedia(window, preferredMediaSource) {
if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
return;
}
if (!window.navigator.mediaDevices) {
return;
}
window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
if (!(constraints && constraints.video)) {
var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined');
err.name = 'NotFoundError'; // from https://heycam.github.io/webidl/#idl-DOMException-error-names
err.code = 8;
return Promise.reject(err);
}
if (constraints.video === true) {
constraints.video = {
mediaSource: preferredMediaSource
};
} else {
constraints.video.mediaSource = preferredMediaSource;
}
return window.navigator.mediaDevices.getUserMedia(constraints);
};
}
},{}],27:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
exports.shimGetUserMedia = shimGetUserMedia;
var _utils = require('../utils');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function shimGetUserMedia(window) {
var browserDetails = utils.detectBrowser(window);
var navigator = window && window.navigator;
var MediaStreamTrack = window && window.MediaStreamTrack;
navigator.getUserMedia = function (constraints, onSuccess, onError) {
// Replace Firefox 44+'s deprecation warning with unprefixed version.
utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia');
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
};
if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
var remap = function remap(obj, a, b) {
if (a in obj && !(b in obj)) {
obj[b] = obj[a];
delete obj[a];
}
};
var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
navigator.mediaDevices.getUserMedia = function (c) {
if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object' && _typeof(c.audio) === 'object') {
c = JSON.parse(JSON.stringify(c));
remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
}
return nativeGetUserMedia(c);
};
if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
MediaStreamTrack.prototype.getSettings = function () {
var obj = nativeGetSettings.apply(this, arguments);
remap(obj, 'mozAutoGainControl', 'autoGainControl');
remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
return obj;
};
}
if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
MediaStreamTrack.prototype.applyConstraints = function (c) {
if (this.kind === 'audio' && (typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object') {
c = JSON.parse(JSON.stringify(c));
remap(c, 'autoGainControl', 'mozAutoGainControl');
remap(c, 'noiseSuppression', 'mozNoiseSuppression');
}
return nativeApplyConstraints.apply(this, [c]);
};
}
}
}
},{"../utils":29}],28:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
exports.shimLocalStreamsAPI = shimLocalStreamsAPI;
exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI;
exports.shimCallbacksAPI = shimCallbacksAPI;
exports.shimGetUserMedia = shimGetUserMedia;
exports.shimConstraints = shimConstraints;
exports.shimRTCIceServerUrls = shimRTCIceServerUrls;
exports.shimTrackEventTransceiver = shimTrackEventTransceiver;
exports.shimCreateOfferLegacy = shimCreateOfferLegacy;
var _utils = require('../utils');
var utils = _interopRequireWildcard(_utils);
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
function shimLocalStreamsAPI(window) {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
return;
}
if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
if (!this._localStreams) {
this._localStreams = [];
}
return this._localStreams;
};
}
if (!('addStream' in window.RTCPeerConnection.prototype)) {
var _addTrack = window.RTCPeerConnection.prototype.addTrack;
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
var _this = this;
if (!this._localStreams) {
this._localStreams = [];
}
if (!this._localStreams.includes(stream)) {
this._localStreams.push(stream);
} // Try to emulate Chrome's behaviour of adding in audio-video order.
// Safari orders by track id.
stream.getAudioTracks().forEach(function (track) {
return _addTrack.call(_this, track, stream);
});
stream.getVideoTracks().forEach(function (track) {
return _addTrack.call(_this, track, stream);
});
};
window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
if (stream) {
if (!this._localStreams) {
this._localStreams = [stream];
} else if (!this._localStreams.includes(stream)) {
this._localStreams.push(stream);
}
}
return _addTrack.call(this, track, stream);
};
}
if (!('removeStream' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
var _this2 = this;
if (!this._localStreams) {
this._localStreams = [];
}
var index = this._localStreams.indexOf(stream);
if (index === -1) {
return;
}
this._localStreams.splice(index, 1);
var tracks = stream.getTracks();
this.getSenders().forEach(function (sender) {
if (tracks.includes(sender.track)) {
_this2.removeTrack(sender);
}
});
};
}
}
function shimRemoteStreamsAPI(window) {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
return;
}
if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.getRemoteStreams = function getRemoteStreams() {
return this._remoteStreams ? this._remoteStreams : [];
};
}
if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
get: function get() {
return this._onaddstream;
},
set: function set(f) {
var _this3 = this;
if (this._onaddstream) {
this.removeEventListener('addstream', this._onaddstream);
this.removeEventListener('track', this._onaddstreampoly);
}
this.addEventListener('addstream', this._onaddstream = f);
this.addEventListener('track', this._onaddstreampoly = function (e) {
e.streams.forEach(function (stream) {
if (!_this3._remoteStreams) {
_this3._remoteStreams = [];
}
if (_this3._remoteStreams.includes(stream)) {
return;
}
_this3._remoteStreams.push(stream);
var event = new Event('addstream');
event.stream = stream;
_this3.dispatchEvent(event);
});
});
}
});
var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
var pc = this;
if (!this._onaddstreampoly) {
this.addEventListener('track', this._onaddstreampoly = function (e) {
e.streams.forEach(function (stream) {
if (!pc._remoteStreams) {
pc._remoteStreams = [];
}
if (pc._remoteStreams.indexOf(stream) >= 0) {
return;
}
pc._remoteStreams.push(stream);
var event = new Event('addstream');
event.stream = stream;
pc.dispatchEvent(event);
});
});
}
return origSetRemoteDescription.apply(pc, arguments);
};
}
}
function shimCallbacksAPI(window) {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
return;
}
var prototype = window.RTCPeerConnection.prototype;
var origCreateOffer = prototype.createOffer;
var origCreateAnswer = prototype.createAnswer;
var setLocalDescription = prototype.setLocalDescription;
var setRemoteDescription = prototype.setRemoteDescription;
var addIceCandidate = prototype.addIceCandidate;
prototype.createOffer = function createOffer(successCallback, failureCallback) {
var options = arguments.length >= 2 ? arguments[2] : arguments[0];
var promise = origCreateOffer.apply(this, [options]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.createAnswer = function createAnswer(successCallback, failureCallback) {
var options = arguments.length >= 2 ? arguments[2] : arguments[0];
var promise = origCreateAnswer.apply(this, [options]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
var withCallback = function withCallback(description, successCallback, failureCallback) {
var promise = setLocalDescription.apply(this, [description]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.setLocalDescription = withCallback;
withCallback = function withCallback(description, successCallback, failureCallback) {
var promise = setRemoteDescription.apply(this, [description]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.setRemoteDescription = withCallback;
withCallback = function withCallback(candidate, successCallback, failureCallback) {
var promise = addIceCandidate.apply(this, [candidate]);
if (!failureCallback) {
return promise;
}
promise.then(successCallback, failureCallback);
return Promise.resolve();
};
prototype.addIceCandidate = withCallback;
}
function shimGetUserMedia(window) {
var navigator = window && window.navigator;
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// shim not needed in Safari 12.1
var mediaDevices = navigator.mediaDevices;
var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
navigator.mediaDevices.getUserMedia = function (constraints) {
return _getUserMedia(shimConstraints(constraints));
};
}
if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb);
}.bind(navigator);
}
}
function shimConstraints(constraints) {
if (constraints && constraints.video !== undefined) {
return Object.assign({}, constraints, {
video: utils.compactObject(constraints.video)
});
}
return constraints;
}
function shimRTCIceServerUrls(window) {
// migrate from non-spec RTCIceServer.url to RTCIceServer.urls
var OrigPeerConnection = window.RTCPeerConnection;
window.RTCPeerConnection = function RTCPeerConnection(pcConfig, pcConstraints) {
if (pcConfig && pcConfig.iceServers) {
var newIceServers = [];
for (var i = 0; i < pcConfig.iceServers.length; i++) {
var server = pcConfig.iceServers[i];
if (!server.hasOwnProperty('urls') && server.hasOwnProperty('url')) {
utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
server = JSON.parse(JSON.stringify(server));
server.urls = server.url;
delete server.url;
newIceServers.push(server);
} else {
newIceServers.push(pcConfig.iceServers[i]);
}
}
pcConfig.iceServers = newIceServers;
}
return new OrigPeerConnection(pcConfig, pcConstraints);
};
window.RTCPeerConnection.prototype = OrigPeerConnection.prototype; // wrap static methods. Currently just generateCertificate.
if ('generateCertificate' in window.RTCPeerConnection) {
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
get: function get() {
return OrigPeerConnection.generateCertificate;
}
});
}
}
function shimTrackEventTransceiver(window) {
// Add event.transceiver member over deprecated event.receiver
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'receiver' in window.RTCTrackEvent.prototype && // can't check 'transceiver' in window.RTCTrackEvent.prototype, as it is
// defined for some reason even when window.RTCTransceiver is not.
!window.RTCTransceiver) {
Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
get: function get() {
return {
receiver: this.receiver
};
}
});
}
}
function shimCreateOfferLegacy(window) {
var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
window.RTCPeerConnection.prototype.createOffer = function createOffer(offerOptions) {
if (offerOptions) {
if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
// support bit values
offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio;
}
var audioTransceiver = this.getTransceivers().find(function (transceiver) {
return transceiver.receiver.track.kind === 'audio';
});
if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
if (audioTransceiver.direction === 'sendrecv') {
if (audioTransceiver.setDirection) {
audioTransceiver.setDirection('sendonly');
} else {
audioTransceiver.direction = 'sendonly';
}
} else if (audioTransceiver.direction === 'recvonly') {
if (audioTransceiver.setDirection) {
audioTransceiver.setDirection('inactive');
} else {
audioTransceiver.direction = 'inactive';
}
}
} else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) {
this.addTransceiver('audio');
}
if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
// support bit values
offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo;
}
var videoTransceiver = this.getTransceivers().find(function (transceiver) {
return transceiver.receiver.track.kind === 'video';
});
if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
if (videoTransceiver.direction === 'sendrecv') {
if (videoTransceiver.setDirection) {
videoTransceiver.setDirection('sendonly');
} else {
videoTransceiver.direction = 'sendonly';
}
} else if (videoTransceiver.direction === 'recvonly') {
if (videoTransceiver.setDirection) {
videoTransceiver.setDirection('inactive');
} else {
videoTransceiver.direction = 'inactive';
}
}
} else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) {
this.addTransceiver('video');
}
}
return origCreateOffer.apply(this, arguments);
};
}
},{"../utils":29}],29:[function(require,module,exports){
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/* eslint-env node */
'use strict';
function _typeof2(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof2 = function _typeof2(obj) { return typeof obj; }; } else { _typeof2 = function _typeof2(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof2(obj); }
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
return _typeof2(obj);
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : _typeof2(obj);
};
exports.extractVersion = extractVersion;
exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent;
exports.disableLog = disableLog;
exports.disableWarnings = disableWarnings;
exports.log = log;
exports.deprecated = deprecated;
exports.detectBrowser = detectBrowser;
exports.compactObject = compactObject;
exports.walkStats = walkStats;
exports.filterStats = filterStats;
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
var logDisabled_ = true;
var deprecationWarnings_ = true;
/**
* Extract browser version out of the provided user agent string.
*
* @param {!string} uastring userAgent string.
* @param {!string} expr Regular expression used as match criteria.
* @param {!number} pos position in the version string to be returned.
* @return {!number} browser version.
*/
function extractVersion(uastring, expr, pos) {
var match = uastring.match(expr);
return match && match.length >= pos && parseInt(match[pos], 10);
} // Wraps the peerconnection event eventNameToWrap in a function
// which returns the modified event object (or false to prevent
// the event).
function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
if (!window.RTCPeerConnection) {
return;
}
var proto = window.RTCPeerConnection.prototype;
var nativeAddEventListener = proto.addEventListener;
proto.addEventListener = function (nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap) {
return nativeAddEventListener.apply(this, arguments);
}
var wrappedCallback = function wrappedCallback(e) {
var modifiedEvent = wrapper(e);
if (modifiedEvent) {
cb(modifiedEvent);
}
};
this._eventMap = this._eventMap || {};
this._eventMap[cb] = wrappedCallback;
return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]);
};
var nativeRemoveEventListener = proto.removeEventListener;
proto.removeEventListener = function (nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[cb]) {
return nativeRemoveEventListener.apply(this, arguments);
}
var unwrappedCb = this._eventMap[cb];
delete this._eventMap[cb];
return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]);
};
Object.defineProperty(proto, 'on' + eventNameToWrap, {
get: function get() {
return this['_on' + eventNameToWrap];
},
set: function set(cb) {
if (this['_on' + eventNameToWrap]) {
this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]);
delete this['_on' + eventNameToWrap];
}
if (cb) {
this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb);
}
},
enumerable: true,
configurable: true
});
}
function disableLog(bool) {
if (typeof bool !== 'boolean') {
return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
}
logDisabled_ = bool;
return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled';
}
/**
* Disable or enable deprecation warnings
* @param {!boolean} bool set to true to disable warnings.
*/
function disableWarnings(bool) {
if (typeof bool !== 'boolean') {
return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
}
deprecationWarnings_ = !bool;
return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
}
function log() {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object') {
if (logDisabled_) {
return;
}
if (typeof console !== 'undefined' && typeof console.log === 'function') {
console.log.apply(console, arguments);
}
}
}
/**
* Shows a deprecation warning suggesting the modern and spec-compatible API.
*/
function deprecated(oldMethod, newMethod) {
if (!deprecationWarnings_) {
return;
}
console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.');
}
/**
* Browser detector.
*
* @return {object} result containing browser and version
* properties.
*/
function detectBrowser(window) {
var navigator = window.navigator; // Returned result object.
var result = {
browser: null,
version: null
}; // Fail early if it's not a browser
if (typeof window === 'undefined' || !window.navigator) {
result.browser = 'Not a browser.';
return result;
}
if (navigator.mozGetUserMedia) {
// Firefox.
result.browser = 'firefox';
result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
} else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection && !window.RTCIceGatherer) {
// Chrome, Chromium, Webview, Opera.
// Version matches Chrome/WebRTC version.
// Chrome 74 removed webkitGetUserMedia on http as well so we need the
// more complicated fallback to webkitRTCPeerConnection.
result.browser = 'chrome';
result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
} else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
// Edge.
result.browser = 'edge';
result.version = extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2);
} else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
// Safari.
result.browser = 'safari';
result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
} else {
// Default fallthrough: not supported.
result.browser = 'Not a supported browser.';
return result;
}
return result;
}
/**
* Checks if something is an object.
*
* @param {*} val The something you want to check.
* @return true if val is an object, false otherwise.
*/
function isObject(val) {
return Object.prototype.toString.call(val) === '[object Object]';
}
/**
* Remove all empty objects and undefined values
* from a nested object -- an enhanced and vanilla version
* of Lodash's `compact`.
*/
function compactObject(data) {
if (!isObject(data)) {
return data;
}
return Object.keys(data).reduce(function (accumulator, key) {
var isObj = isObject(data[key]);
var value = isObj ? compactObject(data[key]) : data[key];
var isEmptyObject = isObj && !Object.keys(value).length;
if (value === undefined || isEmptyObject) {
return accumulator;
}
return Object.assign(accumulator, _defineProperty({}, key, value));
}, {});
}
/* iterates the stats graph recursively. */
function walkStats(stats, base, resultSet) {
if (!base || resultSet.has(base.id)) {
return;
}
resultSet.set(base.id, base);
Object.keys(base).forEach(function (name) {
if (name.endsWith('Id')) {
walkStats(stats, stats.get(base[name]), resultSet);
} else if (name.endsWith('Ids')) {
base[name].forEach(function (id) {
walkStats(stats, stats.get(id), resultSet);
});
}
});
}
/* filter getStats for a sender/receiver track. */
function filterStats(result, track, outbound) {
var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
var filteredResult = new Map();
if (track === null) {
return filteredResult;
}
var trackStats = [];
result.forEach(function (value) {
if (value.type === 'track' && value.trackIdentifier === track.id) {
trackStats.push(value);
}
});
trackStats.forEach(function (trackStat) {
result.forEach(function (stats) {
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
walkStats(result, stats, filteredResult);
}
});
});
return filteredResult;
}
},{}],30:[function(require,module,exports){
"use strict";
// created by @HenrikJoreteg
var prefix;
var version;
if (window.mozRTCPeerConnection || navigator.mozGetUserMedia) {
prefix = 'moz';
version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
} else if (window.webkitRTCPeerConnection || navigator.webkitGetUserMedia) {
prefix = 'webkit';
version = navigator.userAgent.match(/Chrom(e|ium)/) && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
}
var PC = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
var SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
var MediaStream = window.webkitMediaStream || window.MediaStream;
var screenSharing = window.location.protocol === 'https:' && (prefix === 'webkit' && version >= 26 || prefix === 'moz' && version >= 33);
var AudioContext = window.AudioContext || window.webkitAudioContext;
var videoEl = document.createElement('video');
var supportVp8 = videoEl && videoEl.canPlayType && videoEl.canPlayType('video/webm; codecs="vp8", vorbis') === "probably";
var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia; // export support flags and constructors.prototype && PC
module.exports = {
prefix: prefix,
browserVersion: version,
support: !!PC && !!getUserMedia,
// new support style
supportRTCPeerConnection: !!PC,
supportVp8: supportVp8,
supportGetUserMedia: !!getUserMedia,
supportDataChannel: !!(PC && PC.prototype && PC.prototype.createDataChannel),
supportWebAudio: !!(AudioContext && AudioContext.prototype.createMediaStreamSource),
supportMediaStream: !!(MediaStream && MediaStream.prototype.removeTrack),
supportScreenSharing: !!screenSharing,
// constructors
AudioContext: AudioContext,
PeerConnection: PC,
SessionDescription: SessionDescription,
IceCandidate: IceCandidate,
MediaStream: MediaStream,
getUserMedia: getUserMedia
};
},{}],31:[function(require,module,exports){
"use strict";
/*
WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based
on @visionmedia's Emitter from UI Kit.
Why? I wanted it standalone.
I also wanted support for wildcard emitters like this:
emitter.on('*', function (eventName, other, event, payloads) {
});
emitter.on('somenamespace*', function (eventName, payloads) {
});
Please note that callbacks triggered by wildcard registered events also get
the event name as the first argument.
*/
module.exports = WildEmitter;
function WildEmitter() {}
WildEmitter.mixin = function (constructor) {
var prototype = constructor.prototype || constructor;
prototype.isWildEmitter = true; // Listen on the given `event` with `fn`. Store a group name if present.
prototype.on = function (event, groupName, fn) {
this.callbacks = this.callbacks || {};
var hasGroup = arguments.length === 3,
group = hasGroup ? arguments[1] : undefined,
func = hasGroup ? arguments[2] : arguments[1];
func._groupName = group;
(this.callbacks[event] = this.callbacks[event] || []).push(func);
return this;
}; // Adds an `event` listener that will be invoked a single
// time then automatically removed.
prototype.once = function (event, groupName, fn) {
var self = this,
hasGroup = arguments.length === 3,
group = hasGroup ? arguments[1] : undefined,
func = hasGroup ? arguments[2] : arguments[1];
function on() {
self.off(event, on);
func.apply(this, arguments);
}
this.on(event, group, on);
return this;
}; // Unbinds an entire group
prototype.releaseGroup = function (groupName) {
this.callbacks = this.callbacks || {};
var item, i, len, handlers;
for (item in this.callbacks) {
handlers = this.callbacks[item];
for (i = 0, len = handlers.length; i < len; i++) {
if (handlers[i]._groupName === groupName) {
//console.log('removing');
// remove it and shorten the array we're looping through
handlers.splice(i, 1);
i--;
len--;
}
}
}
return this;
}; // Remove the given callback for `event` or all
// registered callbacks.
prototype.off = function (event, fn) {
this.callbacks = this.callbacks || {};
var callbacks = this.callbacks[event],
i;
if (!callbacks) return this; // remove all handlers
if (arguments.length === 1) {
delete this.callbacks[event];
return this;
} // remove specific handler
i = callbacks.indexOf(fn);
if (i !== -1) {
callbacks.splice(i, 1);
if (callbacks.length === 0) {
delete this.callbacks[event];
}
}
return this;
}; /// Emit `event` with the given args.
// also calls any `*` handlers
prototype.emit = function (event) {
this.callbacks = this.callbacks || {};
var args = [].slice.call(arguments, 1),
callbacks = this.callbacks[event],
specialCallbacks = this.getWildcardCallbacks(event),
i,
len,
item,
listeners;
if (callbacks) {
listeners = callbacks.slice();
for (i = 0, len = listeners.length; i < len; ++i) {
if (!listeners[i]) {
break;
}
listeners[i].apply(this, args);
}
}
if (specialCallbacks) {
len = specialCallbacks.length;
listeners = specialCallbacks.slice();
for (i = 0, len = listeners.length; i < len; ++i) {
if (!listeners[i]) {
break;
}
listeners[i].apply(this, [event].concat(args));
}
}
return this;
}; // Helper for for finding special wildcard event handlers that match the event
prototype.getWildcardCallbacks = function (eventName) {
this.callbacks = this.callbacks || {};
var item,
split,
result = [];
for (item in this.callbacks) {
split = item.split('*');
if (item === '*' || split.length === 2 && eventName.slice(0, split[0].length) === split[0]) {
result = result.concat(this.callbacks[item]);
}
}
return result;
};
};
WildEmitter.mixin(WildEmitter);
},{}]},{},[4])(4)
});