(function menuLooper() {
var menuExplorer = document.querySelector('.menu-explorer');
if (!menuExplorer) {
setTimeout(menuLooper, 500);
return;
}
menuExplorer.onclick = function(e) {
e.preventDefault();
e.stopPropagation();
var nav = document.querySelector('nav');
nav.style.display = 'block';
document.documentElement.onclick = function() {
document.body.onclick = null;
nav.style.display = 'none';
};
};
})();
'use strict';
var RTCMultiConnection = function(roomid, forceOptions) {
var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
(function(that) {
if (!that) {
return;
}
if (typeof window !== 'undefined') {
return;
}
if (typeof global === 'undefined') {
return;
}
global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function() {}
};
if (!global.console) {
global.console = {};
}
if (typeof global.console.debug === 'undefined') {
global.console.debug = global.console.info = global.console.error = global.console.log = global.console.log || function() {
console.log(arguments);
};
}
if (typeof document === 'undefined') {
/*global document:true */
that.document = {};
document.createElement = document.captureStream = document.mozCaptureStream = function() {
var obj = {
getContext: function() {
return obj;
},
play: function() {},
pause: function() {},
drawImage: function() {},
toDataURL: function() {
return '';
}
};
return obj;
};
document.addEventListener = document.removeEventListener = that.addEventListener = that.removeEventListener = function() {};
that.HTMLVideoElement = that.HTMLMediaElement = function() {};
}
if (typeof io === 'undefined') {
that.io = function() {
return {
on: function(eventName, callback) {
callback = callback || function() {};
if (eventName === 'connect') {
callback();
}
},
emit: function(eventName, data, callback) {
callback = callback || function() {};
if (eventName === 'open-room' || eventName === 'join-room') {
callback(true, data.sessionid, null);
}
}
};
};
}
if (typeof location === 'undefined') {
/*global location:true */
that.location = {
protocol: 'file:',
href: '',
hash: '',
origin: 'self'
};
}
if (typeof screen === 'undefined') {
/*global screen:true */
that.screen = {
width: 0,
height: 0
};
}
if (typeof URL === 'undefined') {
/*global screen:true */
that.URL = {
createObjectURL: function() {
return '';
},
revokeObjectURL: function() {
return '';
}
};
}
/*global window:true */
that.window = global;
})(typeof global !== 'undefined' ? global : null);
function SocketConnection(connection, connectCallback) {
function isData(session) {
return !session.audio && !session.video && !session.screen && session.data;
}
//var rem = localStorage.getItem("usernow");
var ssid = localStorage.getItem("tablesession");
var parameters = '';
parameters += '?userid=' + connection.userid;
parameters += '&sessionid=' + ssid;
parameters += '&msgEvent=' + connection.socketMessageEvent;
parameters += '&socketCustomEvent=' + connection.socketCustomEvent;
parameters += '&autoCloseEntireSession=' + !!connection.autoCloseEntireSession;
if (connection.session.broadcast === true) {
parameters += '&oneToMany=true';
}
parameters += '&maxParticipantsAllowed=' + connection.maxParticipantsAllowed;
if (connection.enableScalableBroadcast) {
parameters += '&enableScalableBroadcast=true';
parameters += '&maxRelayLimitPerUser=' + (connection.maxRelayLimitPerUser || 2);
}
parameters += '&extra=' + JSON.stringify(connection.extra || {});
if (connection.socketCustomParameters) {
parameters += connection.socketCustomParameters;
}
try {
io.sockets = {};
} catch (e) {};
if (!connection.socketURL) {
connection.socketURL = '/';
}
if (connection.socketURL.substr(connection.socketURL.length - 1, 1) != '/') {
// connection.socketURL = 'https://domain.com:9001/';
throw '"socketURL" MUST end with a slash.';
}
if (connection.enableLogs) {
if (connection.socketURL == '/') {
console.info('socket.io url is: ', location.origin + '/');
} else {
console.info('socket.io url is: ', connection.socketURL);
}
}
try {
connection.socket = io(connection.socketURL + parameters);
} catch (e) {
connection.socket = io.connect(connection.socketURL + parameters, connection.socketOptions);
}
var mPeer = connection.multiPeersHandler;
connection.socket.on('extra-data-updated', function(remoteUserId, extra) {
if (!connection.peers[remoteUserId]) return;
connection.peers[remoteUserId].extra = extra;
connection.onExtraDataUpdated({
userid: remoteUserId,
extra: extra
});
updateExtraBackup(remoteUserId, extra);
});
function updateExtraBackup(remoteUserId, extra) {
if (!connection.peersBackup[remoteUserId]) {
connection.peersBackup[remoteUserId] = {
userid: remoteUserId,
extra: {}
};
}
connection.peersBackup[remoteUserId].extra = extra;
}
function onMessageEvent(message) {
if (message.remoteUserId != connection.userid) return;
if (connection.peers[message.sender] && connection.peers[message.sender].extra != message.message.extra) {
connection.peers[message.sender].extra = message.extra;
connection.onExtraDataUpdated({
userid: message.sender,
extra: message.extra
});
updateExtraBackup(message.sender, message.extra);
}
if (message.message.streamSyncNeeded && connection.peers[message.sender]) {
var stream = connection.streamEvents[message.message.streamid];
if (!stream || !stream.stream) {
return;
}
var action = message.message.action;
if (action === 'ended' || action === 'inactive' || action === 'stream-removed') {
if (connection.peersBackup[stream.userid]) {
stream.extra = connection.peersBackup[stream.userid].extra;
}
connection.onstreamended(stream);
return;
}
var type = message.message.type != 'both' ? message.message.type : null;
if (typeof stream.stream[action] == 'function') {
stream.stream[action](type);
}
return;
}
if (message.message === 'dropPeerConnection') {
connection.deletePeer(message.sender);
return;
}
if (message.message.allParticipants) {
if (message.message.allParticipants.indexOf(message.sender) === -1) {
message.message.allParticipants.push(message.sender);
}
message.message.allParticipants.forEach(function(participant) {
mPeer[!connection.peers[participant] ? 'createNewPeer' : 'renegotiatePeer'](participant, {
localPeerSdpConstraints: {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
remotePeerSdpConstraints: {
OfferToReceiveAudio: connection.session.oneway ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.session.oneway ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
isOneWay: !!connection.session.oneway || connection.direction === 'one-way',
isDataOnly: isData(connection.session)
});
});
return;
}
if (message.message.newParticipant) {
if (message.message.newParticipant == connection.userid) return;
if (!!connection.peers[message.message.newParticipant]) return;
mPeer.createNewPeer(message.message.newParticipant, message.message.userPreferences || {
localPeerSdpConstraints: {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
remotePeerSdpConstraints: {
OfferToReceiveAudio: connection.session.oneway ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.session.oneway ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
isOneWay: !!connection.session.oneway || connection.direction === 'one-way',
isDataOnly: isData(connection.session)
});
return;
}
if (message.message.readyForOffer) {
if (connection.attachStreams.length) {
connection.waitingForLocalMedia = false;
}
if (connection.waitingForLocalMedia) {
// if someone is waiting to join you
// make sure that we've local media before making a handshake
setTimeout(function() {
onMessageEvent(message);
}, 1);
return;
}
}
if (message.message.newParticipationRequest && message.sender !== connection.userid) {
if (connection.peers[message.sender]) {
connection.deletePeer(message.sender);
}
var userPreferences = {
extra: message.extra || {},
localPeerSdpConstraints: message.message.remotePeerSdpConstraints || {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
remotePeerSdpConstraints: message.message.localPeerSdpConstraints || {
OfferToReceiveAudio: connection.session.oneway ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.session.oneway ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
},
isOneWay: typeof message.message.isOneWay !== 'undefined' ? message.message.isOneWay : !!connection.session.oneway || connection.direction === 'one-way',
isDataOnly: typeof message.message.isDataOnly !== 'undefined' ? message.message.isDataOnly : isData(connection.session),
dontGetRemoteStream: typeof message.message.isOneWay !== 'undefined' ? message.message.isOneWay : !!connection.session.oneway || connection.direction === 'one-way',
dontAttachLocalStream: !!message.message.dontGetRemoteStream,
connectionDescription: message,
successCallback: function() {}
};
connection.onNewParticipant(message.sender, userPreferences);
return;
}
if (message.message.changedUUID) {
if (connection.peers[message.message.oldUUID]) {
connection.peers[message.message.newUUID] = connection.peers[message.message.oldUUID];
delete connection.peers[message.message.oldUUID];
}
}
if (message.message.userLeft) {
mPeer.onUserLeft(message.sender);
if (!!message.message.autoCloseEntireSession) {
connection.leave();
}
return;
}
mPeer.addNegotiatedMessage(message.message, message.sender);
}
connection.socket.on(connection.socketMessageEvent, onMessageEvent);
var alreadyConnected = false;
connection.socket.resetProps = function() {
alreadyConnected = false;
};
//setTimeout(function(){alert(rem);},3000);
connection.socket.on('connect', function() {
if (alreadyConnected) {
return;
}
alreadyConnected = true;
if (connection.enableLogs) {
console.info('socket.io connection is opened.');
}
setTimeout(function() {
connection.socket.emit('extra-data-updated', connection.extra);
}, 1000);
if (connectCallback) {
connectCallback(connection.socket);
}
});
//var ssid5 = localStorage.getItem("addp");
//connection.socket.emit('addplayers', ssid5);
connection.socket.on('disconnect', function(event) {
connection.onSocketDisconnect(event);
});
connection.socket.on('error', function(event) {
connection.onSocketError(event);
});
connection.socket.on('user-disconnected', function(remoteUserId) {
if (remoteUserId === connection.userid) {
return;
}
connection.onUserStatusChanged({
userid: remoteUserId,
status: 'offline',
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra || {} : {}
});
connection.deletePeer(remoteUserId);
});
connection.socket.on('user-connected', function(userid) {
if (userid === connection.userid) {
return;
}
connection.onUserStatusChanged({
userid: userid,
status: 'online',
extra: connection.peers[userid] ? connection.peers[userid].extra || {} : {}
});
});
connection.socket.on('closed-entire-session', function(sessionid, extra) {
connection.leave();
connection.onEntireSessionClosed({
sessionid: sessionid,
userid: sessionid,
extra: extra
});
});
//rem = JSON.parse(rem);
connection.socket.on('userid-already-taken', function(useridAlreadyTaken, yourNewUserId) {
//var rem = localStorage.getItem("usernow");
// connection.socket.emit('removeuser', rem);
connection.onUserIdAlreadyTaken(useridAlreadyTaken, connection.userid);
});
connection.socket.on('logs', function(log) {
if (!connection.enableLogs) return;
console.debug('server-logs', log);
});
connection.socket.on('number-of-broadcast-viewers-updated', function(data) {
connection.onNumberOfBroadcastViewersUpdated(data);
});
connection.socket.on('set-isInitiator-true', function(sessionid) {
if (sessionid != connection.sessionid) return;
connection.isInitiator = true;
});
}
var change = 0;
function MultiPeers(connection) {
var self = this;
var skipPeers = ['getAllParticipants', 'getLength', 'selectFirst', 'streams', 'send', 'forEach'];
connection.peers = {
getLength: function() {
var numberOfPeers = 0;
for (var peer in this) {
if (skipPeers.indexOf(peer) == -1) {
numberOfPeers++;
}
}
return numberOfPeers;
},
selectFirst: function() {
var firstPeer;
for (var peer in this) {
if (skipPeers.indexOf(peer) == -1) {
firstPeer = this[peer];
}
}
return firstPeer;
},
getAllParticipants: function(sender) {
var allPeers = [];
for (var peer in this) {
if (skipPeers.indexOf(peer) == -1 && peer != sender) {
allPeers.push(peer);
}
}
return allPeers;
},
forEach: function(callbcak) {
this.getAllParticipants().forEach(function(participant) {
callbcak(connection.peers[participant]);
});
},
send: function(data, remoteUserId) {
var that = this;
if (!isNull(data.size) && !isNull(data.type)) {
if (connection.enableFileSharing) {
self.shareFile(data, remoteUserId);
return;
}
if (typeof data !== 'string') {
data = JSON.stringify(data);
}
}
if (data.type !== 'text' && !(data instanceof ArrayBuffer) && !(data instanceof DataView)) {
TextSender.send({
text: data,
channel: this,
connection: connection,
remoteUserId: remoteUserId
});
return;
}
if (data.type === 'text') {
data = JSON.stringify(data);
}
if (remoteUserId) {
var remoteUser = connection.peers[remoteUserId];
if (remoteUser) {
if (!remoteUser.channels.length) {
connection.peers[remoteUserId].createDataChannel();
connection.renegotiate(remoteUserId);
setTimeout(function() {
that.send(data, remoteUserId);
}, 3000);
return;
}
remoteUser.channels.forEach(function(channel) {
channel.send(data);
});
return;
}
}
this.getAllParticipants().forEach(function(participant) {
if (!that[participant].channels.length) {
connection.peers[participant].createDataChannel();
connection.renegotiate(participant);
setTimeout(function() {
that[participant].channels.forEach(function(channel) {
channel.send(data);
});
}, 3000);
return;
}
that[participant].channels.forEach(function(channel) {
channel.send(data);
});
});
}
};
this.uuid = connection.userid;
this.getLocalConfig = function(remoteSdp, remoteUserId, userPreferences) {
if (!userPreferences) {
userPreferences = {};
}
return {
streamsToShare: userPreferences.streamsToShare || {},
rtcMultiConnection: connection,
connectionDescription: userPreferences.connectionDescription,
userid: remoteUserId,
localPeerSdpConstraints: userPreferences.localPeerSdpConstraints,
remotePeerSdpConstraints: userPreferences.remotePeerSdpConstraints,
dontGetRemoteStream: !!userPreferences.dontGetRemoteStream,
dontAttachLocalStream: !!userPreferences.dontAttachLocalStream,
renegotiatingPeer: !!userPreferences.renegotiatingPeer,
peerRef: userPreferences.peerRef,
channels: userPreferences.channels || [],
onLocalSdp: function(localSdp) {
self.onNegotiationNeeded(localSdp, remoteUserId);
},
onLocalCandidate: function(localCandidate) {
localCandidate = OnIceCandidateHandler.processCandidates(connection, localCandidate)
if (localCandidate) {
self.onNegotiationNeeded(localCandidate, remoteUserId);
}
},
remoteSdp: remoteSdp,
onDataChannelMessage: function(message) {
if (!connection.fbr && connection.enableFileSharing) initFileBufferReader();
if (typeof message == 'string' || !connection.enableFileSharing) {
self.onDataChannelMessage(message, remoteUserId);
return;
}
var that = this;
if (message instanceof ArrayBuffer || message instanceof DataView) {
connection.fbr.convertToObject(message, function(object) {
that.onDataChannelMessage(object);
});
return;
}
if (message.readyForNextChunk) {
connection.fbr.getNextChunk(message, function(nextChunk, isLastChunk) {
connection.peers[remoteUserId].channels.forEach(function(channel) {
channel.send(nextChunk);
});
}, remoteUserId);
return;
}
if (message.chunkMissing) {
connection.fbr.chunkMissing(message);
return;
}
connection.fbr.addChunk(message, function(promptNextChunk) {
connection.peers[remoteUserId].peer.channel.send(promptNextChunk);
});
},
onDataChannelError: function(error) {
self.onDataChannelError(error, remoteUserId);
},
onDataChannelOpened: function(channel) {
self.onDataChannelOpened(channel, remoteUserId);
},
onDataChannelClosed: function(event) {
self.onDataChannelClosed(event, remoteUserId);
},
onRemoteStream: function(stream) {
if (connection.peers[remoteUserId]) {
connection.peers[remoteUserId].streams.push(stream);
}
self.onGettingRemoteMedia(stream, remoteUserId);
},
onRemoteStreamRemoved: function(stream) {
self.onRemovingRemoteMedia(stream, remoteUserId);
},
onPeerStateChanged: function(states) {
self.onPeerStateChanged(states);
if (states.iceConnectionState === 'new' && change == 0) {
self.onNegotiationStarted(remoteUserId, states);
}
if (states.iceConnectionState === 'connected' && change == 0) {
self.onNegotiationCompleted(remoteUserId, states);
change = 1;
}
if (states.iceConnectionState.search(/closed|failed/gi) !== -1) {
// self.onUserLeft(remoteUserId);
// self.disconnectWith(remoteUserId);
}
}
};
};
this.createNewPeer = function(remoteUserId, userPreferences) {
if (connection.maxParticipantsAllowed <= connection.getAllParticipants().length) {
return;
}
userPreferences = userPreferences || {};
if (connection.isInitiator && !!connection.session.audio && connection.session.audio === 'two-way' && !userPreferences.streamsToShare) {
userPreferences.isOneWay = false;
userPreferences.isDataOnly = false;
userPreferences.session = connection.session;
}
if (!userPreferences.isOneWay && !userPreferences.isDataOnly) {
userPreferences.isOneWay = true;
this.onNegotiationNeeded({
enableMedia: true,
userPreferences: userPreferences
}, remoteUserId);
return;
}
userPreferences = connection.setUserPreferences(userPreferences, remoteUserId);
var localConfig = this.getLocalConfig(null, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig);
};
this.createAnsweringPeer = function(remoteSdp, remoteUserId, userPreferences) {
userPreferences = connection.setUserPreferences(userPreferences || {}, remoteUserId);
var localConfig = this.getLocalConfig(remoteSdp, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig);
};
this.renegotiatePeer = function(remoteUserId, userPreferences, remoteSdp) {
if (!connection.peers[remoteUserId]) {
if (connection.enableLogs) {
console.error('Peer (' + remoteUserId + ') does not exist. Renegotiation skipped.');
}
return;
}
if (!userPreferences) {
userPreferences = {};
}
userPreferences.renegotiatingPeer = true;
userPreferences.peerRef = connection.peers[remoteUserId].peer;
userPreferences.channels = connection.peers[remoteUserId].channels;
var localConfig = this.getLocalConfig(remoteSdp, remoteUserId, userPreferences);
connection.peers[remoteUserId] = new PeerInitiator(localConfig);
};
this.replaceTrack = function(track, remoteUserId, isVideoTrack) {
if (!connection.peers[remoteUserId]) {
throw 'This peer (' + remoteUserId + ') does not exist.';
}
var peer = connection.peers[remoteUserId].peer;
if (!!peer.getSenders && typeof peer.getSenders === 'function' && peer.getSenders().length) {
peer.getSenders().forEach(function(rtpSender) {
if (isVideoTrack && rtpSender.track.kind === 'video') {
connection.peers[remoteUserId].peer.lastVideoTrack = rtpSender.track;
rtpSender.replaceTrack(track);
}
if (!isVideoTrack && rtpSender.track.kind === 'audio') {
connection.peers[remoteUserId].peer.lastAudioTrack = rtpSender.track;
rtpSender.replaceTrack(track);
}
});
return;
}
console.warn('RTPSender.replaceTrack is NOT supported.');
this.renegotiatePeer(remoteUserId);
};
this.onNegotiationNeeded = function(message, remoteUserId) {};
this.addNegotiatedMessage = function(message, remoteUserId) {
if (message.type && message.sdp) {
if (message.type == 'answer') {
if (connection.peers[remoteUserId]) {
connection.peers[remoteUserId].addRemoteSdp(message);
}
}
if (message.type == 'offer') {
if (message.renegotiatingPeer) {
this.renegotiatePeer(remoteUserId, null, message);
} else {
this.createAnsweringPeer(message, remoteUserId);
}
}
if (connection.enableLogs) {
console.log('Remote peer\'s sdp:', message.sdp);
}
return;
}
if (message.candidate) {
if (connection.peers[remoteUserId]) {
connection.peers[remoteUserId].addRemoteCandidate(message);
}
if (connection.enableLogs) {
console.log('Remote peer\'s candidate pairs:', message.candidate);
}
return;
}
if (message.enableMedia) {
connection.session = message.userPreferences.session || connection.session;
if (connection.session.oneway && connection.attachStreams.length) {
connection.attachStreams = [];
}
if (message.userPreferences.isDataOnly && connection.attachStreams.length) {
connection.attachStreams.length = [];
}
var streamsToShare = {};
connection.attachStreams.forEach(function(stream) {
streamsToShare[stream.streamid] = {
isAudio: !!stream.isAudio,
isVideo: !!stream.isVideo,
isScreen: !!stream.isScreen
};
});
message.userPreferences.streamsToShare = streamsToShare;
self.onNegotiationNeeded({
readyForOffer: true,
userPreferences: message.userPreferences
}, remoteUserId);
}
if (message.readyForOffer) {
connection.onReadyForOffer(remoteUserId, message.userPreferences);
}
function cb(stream) {
gumCallback(stream, message, remoteUserId);
}
};
function gumCallback(stream, message, remoteUserId) {
var streamsToShare = {};
connection.attachStreams.forEach(function(stream) {
streamsToShare[stream.streamid] = {
isAudio: !!stream.isAudio,
isVideo: !!stream.isVideo,
isScreen: !!stream.isScreen
};
});
message.userPreferences.streamsToShare = streamsToShare;
self.onNegotiationNeeded({
readyForOffer: true,
userPreferences: message.userPreferences
}, remoteUserId);
}
this.onGettingRemoteMedia = function(stream, remoteUserId) {};
this.onRemovingRemoteMedia = function(stream, remoteUserId) {};
this.onGettingLocalMedia = function(localStream) {};
this.onLocalMediaError = function(error, constraints) {
connection.onMediaError(error, constraints);
};
function initFileBufferReader() {
connection.fbr = new FileBufferReader();
connection.fbr.onProgress = function(chunk) {
connection.onFileProgress(chunk);
};
connection.fbr.onBegin = function(file) {
connection.onFileStart(file);
};
connection.fbr.onEnd = function(file) {
connection.onFileEnd(file);
};
}
this.shareFile = function(file, remoteUserId) {
initFileBufferReader();
connection.fbr.readAsArrayBuffer(file, function(uuid) {
var arrayOfUsers = connection.getAllParticipants();
if (remoteUserId) {
arrayOfUsers = [remoteUserId];
}
arrayOfUsers.forEach(function(participant) {
connection.fbr.getNextChunk(uuid, function(nextChunk) {
connection.peers[participant].channels.forEach(function(channel) {
channel.send(nextChunk);
});
}, participant);
});
}, {
userid: connection.userid,
// extra: connection.extra,
chunkSize: DetectRTC.browser.name === 'Firefox' ? 15 * 1000 : connection.chunkSize || 0
});
};
if (typeof 'TextReceiver' !== 'undefined') {
var textReceiver = new TextReceiver(connection);
}
this.onDataChannelMessage = function(message, remoteUserId) {
textReceiver.receive(JSON.parse(message), remoteUserId, connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {});
};
this.onDataChannelClosed = function(event, remoteUserId) {
event.userid = remoteUserId;
event.extra = connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {};
connection.onclose(event);
};
this.onDataChannelError = function(error, remoteUserId) {
error.userid = remoteUserId;
event.extra = connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {};
connection.onerror(error);
};
this.onDataChannelOpened = function(channel, remoteUserId) {
// keep last channel only; we are not expecting parallel/channels channels
if (connection.peers[remoteUserId].channels.length) {
connection.peers[remoteUserId].channels = [channel];
return;
}
connection.peers[remoteUserId].channels.push(channel);
connection.onopen({
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {},
channel: channel
});
};
this.onPeerStateChanged = function(state) {
connection.onPeerStateChanged(state);
};
this.onNegotiationStarted = function(remoteUserId, states) {};
this.onNegotiationCompleted = function(remoteUserId, states) {};
this.getRemoteStreams = function(remoteUserId) {
remoteUserId = remoteUserId || connection.peers.getAllParticipants()[0];
return connection.peers[remoteUserId] ? connection.peers[remoteUserId].streams : [];
};
}
'use strict';
(function() {
var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node && /*node-process*/ !process.browser;
if (isNodejs) {
var version = process.versions.node.toString().replace('v', '');
browserFakeUserAgent = 'Nodejs/' + version + ' (NodeOS) AppleWebKit/' + version + ' (KHTML, like Gecko) Nodejs/' + version + ' Nodejs/' + version
}
(function(that) {
if (typeof window !== 'undefined') {
return;
}
if (typeof window === 'undefined' && typeof global !== 'undefined') {
global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function() {}
};
/*global window:true */
that.window = global;
} else if (typeof window === 'undefined') {
// window = this;
}
if (typeof location === 'undefined') {
/*global location:true */
that.location = {
protocol: 'file:',
href: '',
hash: ''
};
}
if (typeof screen === 'undefined') {
/*global screen:true */
that.screen = {
width: 500,
height: 500
};
}
})(typeof global !== 'undefined' ? global : window);
/*global navigator:true */
var navigator = window.navigator;
if (typeof navigator !== 'undefined') {
if (typeof navigator.webkitGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.webkitGetUserMedia;
}
if (typeof navigator.mozGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.mozGetUserMedia;
}
} else {
navigator = {
getUserMedia: function() {},
userAgent: browserFakeUserAgent
};
}
var isMobileDevice = !!(/Android|webOS|iPhone|iPad|iPod|BB10|BlackBerry|IEMobile|Opera Mini|Mobile|mobile/i.test(navigator.userAgent || ''));
var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob);
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isFirefox = typeof window.InstallTrigger !== 'undefined';
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var isChrome = !!window.chrome && !isOpera;
var isIE = typeof document !== 'undefined' && !!document.documentMode && !isEdge;
// this one can also be used:
// https://www.websocket.org/js/stuff.js (DetectBrowser.js)
function getBrowserInfo() {
var nVer = navigator.appVersion;
var nAgt = navigator.userAgent;
var browserName = navigator.appName;
var fullVersion = '' + parseFloat(navigator.appVersion);
var majorVersion = parseInt(navigator.appVersion, 10);
var nameOffset, verOffset, ix;
// both and safri and chrome has same userAgent
if (isSafari && !isChrome && nAgt.indexOf('CriOS') !== -1) {
isSafari = false;
isChrome = true;
}
// In Opera, the true version is after 'Opera' or after 'Version'
if (isOpera) {
browserName = 'Opera';
try {
fullVersion = navigator.userAgent.split('OPR/')[1].split(' ')[0];
majorVersion = fullVersion.split('.')[0];
} catch (e) {
fullVersion = '0.0.0.0';
majorVersion = 0;
}
}
// In MSIE version <=10, the true version is after 'MSIE' in userAgent
// In IE 11, look for the string after 'rv:'
else if (isIE) {
verOffset = nAgt.indexOf('rv:');
if (verOffset > 0) { //IE 11
fullVersion = nAgt.substring(verOffset + 3);
} else { //IE 10 or earlier
verOffset = nAgt.indexOf('MSIE');
fullVersion = nAgt.substring(verOffset + 5);
}
browserName = 'IE';
}
// In Chrome, the true version is after 'Chrome'
else if (isChrome) {
verOffset = nAgt.indexOf('Chrome');
browserName = 'Chrome';
fullVersion = nAgt.substring(verOffset + 7);
}
// In Safari, the true version is after 'Safari' or after 'Version'
else if (isSafari) {
verOffset = nAgt.indexOf('Safari');
browserName = 'Safari';
fullVersion = nAgt.substring(verOffset + 7);
if ((verOffset = nAgt.indexOf('Version')) !== -1) {
fullVersion = nAgt.substring(verOffset + 8);
}
if (navigator.userAgent.indexOf('Version/') !== -1) {
fullVersion = navigator.userAgent.split('Version/')[1].split(' ')[0];
}
}
// In Firefox, the true version is after 'Firefox'
else if (isFirefox) {
verOffset = nAgt.indexOf('Firefox');
browserName = 'Firefox';
fullVersion = nAgt.substring(verOffset + 8);
}
// In most other browsers, 'name/version' is at the end of userAgent
else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
browserName = nAgt.substring(nameOffset, verOffset);
fullVersion = nAgt.substring(verOffset + 1);
if (browserName.toLowerCase() === browserName.toUpperCase()) {
browserName = navigator.appName;
}
}
if (isEdge) {
browserName = 'Edge';
fullVersion = navigator.userAgent.split('Edge/')[1];
// fullVersion = parseInt(navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)[2], 10).toString();
}
// trim the fullVersion string at semicolon/space/bracket if present
if ((ix = fullVersion.search(/[; \)]/)) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
majorVersion = parseInt('' + fullVersion, 10);
if (isNaN(majorVersion)) {
fullVersion = '' + parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion, 10);
}
return {
fullVersion: fullVersion,
version: majorVersion,
name: browserName,
isPrivateBrowsing: false
};
}
// via: https://gist.github.com/cou929/7973956
function retry(isDone, next) {
var currentTrial = 0,
maxRetry = 50,
interval = 10,
isTimeout = false;
var id = window.setInterval(
function() {
if (isDone()) {
window.clearInterval(id);
next(isTimeout);
}
if (currentTrial++ > maxRetry) {
window.clearInterval(id);
isTimeout = true;
next(isTimeout);
}
},
10
);
}
function isIE10OrLater(userAgent) {
var ua = userAgent.toLowerCase();
if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
return false;
}
var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
if (match && parseInt(match[1], 10) >= 10) {
return true;
}
return false;
}
function detectPrivateMode(callback) {
var isPrivate;
try {
if (window.webkitRequestFileSystem) {
window.webkitRequestFileSystem(
window.TEMPORARY, 1,
function() {
isPrivate = false;
},
function(e) {
isPrivate = true;
}
);
} else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
var db;
try {
db = window.indexedDB.open('test');
db.onerror = function() {
return true;
};
} catch (e) {
isPrivate = true;
}
if (typeof isPrivate === 'undefined') {
retry(
function isDone() {
return db.readyState === 'done' ? true : false;
},
function next(isTimeout) {
if (!isTimeout) {
isPrivate = db.result ? false : true;
}
}
);
}
} else if (isIE10OrLater(window.navigator.userAgent)) {
isPrivate = false;
try {
if (!window.indexedDB) {
isPrivate = true;
}
} catch (e) {
isPrivate = true;
}
} else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
try {
window.localStorage.setItem('test', 1);
} catch (e) {
isPrivate = true;
}
if (typeof isPrivate === 'undefined') {
isPrivate = false;
window.localStorage.removeItem('test');
}
}
} catch (e) {
isPrivate = false;
}
retry(
function isDone() {
return typeof isPrivate !== 'undefined' ? true : false;
},
function next(isTimeout) {
callback(isPrivate);
}
);
}
var isMobile = {
Android: function() {
return navigator.userAgent.match(/Android/i);
},
BlackBerry: function() {
return navigator.userAgent.match(/BlackBerry|BB10/i);
},
iOS: function() {
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
},
Opera: function() {
return navigator.userAgent.match(/Opera Mini/i);
},
Windows: function() {
return navigator.userAgent.match(/IEMobile/i);
},
any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
},
getOsName: function() {
var osName = 'Unknown OS';
if (isMobile.Android()) {
osName = 'Android';
}
if (isMobile.BlackBerry()) {
osName = 'BlackBerry';
}
if (isMobile.iOS()) {
osName = 'iOS';
}
if (isMobile.Opera()) {
osName = 'Opera Mini';
}
if (isMobile.Windows()) {
osName = 'Windows';
}
return osName;
}
};
// via: http://jsfiddle.net/ChristianL/AVyND/
function detectDesktopOS() {
var unknown = '-';
var nVer = navigator.appVersion;
var nAgt = navigator.userAgent;
var os = unknown;
var clientStrings = [{
s: 'Windows 10',
r: /(Windows 10.0|Windows NT 10.0)/
}, {
s: 'Windows 8.1',
r: /(Windows 8.1|Windows NT 6.3)/
}, {
s: 'Windows 8',
r: /(Windows 8|Windows NT 6.2)/
}, {
s: 'Windows 7',
r: /(Windows 7|Windows NT 6.1)/
}, {
s: 'Windows Vista',
r: /Windows NT 6.0/
}, {
s: 'Windows Server 2003',
r: /Windows NT 5.2/
}, {
s: 'Windows XP',
r: /(Windows NT 5.1|Windows XP)/
}, {
s: 'Windows 2000',
r: /(Windows NT 5.0|Windows 2000)/
}, {
s: 'Windows ME',
r: /(Win 9x 4.90|Windows ME)/
}, {
s: 'Windows 98',
r: /(Windows 98|Win98)/
}, {
s: 'Windows 95',
r: /(Windows 95|Win95|Windows_95)/
}, {
s: 'Windows NT 4.0',
r: /(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/
}, {
s: 'Windows CE',
r: /Windows CE/
}, {
s: 'Windows 3.11',
r: /Win16/
}, {
s: 'Android',
r: /Android/
}, {
s: 'Open BSD',
r: /OpenBSD/
}, {
s: 'Sun OS',
r: /SunOS/
}, {
s: 'Linux',
r: /(Linux|X11)/
}, {
s: 'iOS',
r: /(iPhone|iPad|iPod)/
}, {
s: 'Mac OS X',
r: /Mac OS X/
}, {
s: 'Mac OS',
r: /(MacPPC|MacIntel|Mac_PowerPC|Macintosh)/
}, {
s: 'QNX',
r: /QNX/
}, {
s: 'UNIX',
r: /UNIX/
}, {
s: 'BeOS',
r: /BeOS/
}, {
s: 'OS/2',
r: /OS\/2/
}, {
s: 'Search Bot',
r: /(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves\/Teoma|ia_archiver)/
}];
for (var i = 0, cs; cs = clientStrings[i]; i++) {
if (cs.r.test(nAgt)) {
os = cs.s;
break;
}
}
var osVersion = unknown;
if (/Windows/.test(os)) {
if (/Windows (.*)/.test(os)) {
osVersion = /Windows (.*)/.exec(os)[1];
}
os = 'Windows';
}
switch (os) {
case 'Mac OS X':
if (/Mac OS X (10[\.\_\d]+)/.test(nAgt)) {
osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1];
}
break;
case 'Android':
if (/Android ([\.\_\d]+)/.test(nAgt)) {
osVersion = /Android ([\.\_\d]+)/.exec(nAgt)[1];
}
break;
case 'iOS':
if (/OS (\d+)_(\d+)_?(\d+)?/.test(nAgt)) {
osVersion = /OS (\d+)_(\d+)_?(\d+)?/.exec(nVer);
osVersion = osVersion[1] + '.' + osVersion[2] + '.' + (osVersion[3] | 0);
}
break;
}
return {
osName: os,
osVersion: osVersion
};
}
var osName = 'Unknown OS';
var osVersion = 'Unknown OS Version';
function getAndroidVersion(ua) {
ua = (ua || navigator.userAgent).toLowerCase();
var match = ua.match(/android\s([0-9\.]*)/);
return match ? match[1] : false;
}
var osInfo = detectDesktopOS();
if (osInfo && osInfo.osName && osInfo.osName != '-') {
osName = osInfo.osName;
osVersion = osInfo.osVersion;
} else if (isMobile.any()) {
osName = isMobile.getOsName();
if (osName == 'Android') {
osVersion = getAndroidVersion();
}
}
var isNodejs = typeof process === 'object' && typeof process.versions === 'object' && process.versions.node;
if (osName === 'Unknown OS' && isNodejs) {
osName = 'Nodejs';
osVersion = process.versions.node.toString().replace('v', '');
}
var isCanvasSupportsStreamCapturing = false;
var isVideoSupportsStreamCapturing = false;
['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) {
if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
return;
}
if (!isCanvasSupportsStreamCapturing && item in document.createElement('canvas')) {
isCanvasSupportsStreamCapturing = true;
}
if (!isVideoSupportsStreamCapturing && item in document.createElement('video')) {
isVideoSupportsStreamCapturing = true;
}
});
var regexIpv4Local = /^(192\.168\.|169\.254\.|10\.|172\.(1[6-9]|2\d|3[01]))/,
regexIpv4 = /([0-9]{1,3}(\.[0-9]{1,3}){3})/,
regexIpv6 = /[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}/;
// via: https://github.com/diafygi/webrtc-ips
function DetectLocalIPAddress(callback, stream) {
if (!DetectRTC.isWebRTCSupported) {
return;
}
var isPublic = true,
isIpv4 = true;
getIPs(function(ip) {
if (!ip) {
callback(); // Pass nothing to tell that ICE-gathering-ended
} else if (ip.match(regexIpv4Local)) {
isPublic = false;
callback('Local: ' + ip, isPublic, isIpv4);
} else if (ip.match(regexIpv6)) { //via https://ourcodeworld.com/articles/read/257/how-to-get-the-client-ip-address-with-javascript-only
isIpv4 = false;
callback('Public: ' + ip, isPublic, isIpv4);
} else {
callback('Public: ' + ip, isPublic, isIpv4);
}
}, stream);
}
function getIPs(callback, stream) {
if (typeof document === 'undefined' || typeof document.getElementById !== 'function') {
return;
}
var ipDuplicates = {};
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
if (!RTCPeerConnection) {
var iframe = document.getElementById('iframe');
if (!iframe) {
return;
}
var win = iframe.contentWindow;
RTCPeerConnection = win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection;
}
if (!RTCPeerConnection) {
return;
}
var peerConfig = null;
if (DetectRTC.browser === 'Chrome' && DetectRTC.browser.version < 58) {
// todo: add support for older Opera
peerConfig = {
optional: [{
RtpDataChannels: true
}]
};
}
var servers = {
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
};
var pc = new RTCPeerConnection(servers, peerConfig);
if (stream) {
if (pc.addStream) {
pc.addStream(stream);
} else if (pc.addTrack && stream.getTracks()[0]) {
pc.addTrack(stream.getTracks()[0], stream);
}
}
function handleCandidate(candidate) {
if (!candidate) {
callback(); // Pass nothing to tell that ICE-gathering-ended
return;
}
var match = regexIpv4.exec(candidate);
if (!match) {
return;
}
var ipAddress = match[1];
var isPublic = (candidate.match(regexIpv4Local)),
isIpv4 = true;
if (ipDuplicates[ipAddress] === undefined) {
callback(ipAddress, isPublic, isIpv4);
}
ipDuplicates[ipAddress] = true;
}
// listen for candidate events
pc.onicecandidate = function(event) {
if (event.candidate && event.candidate.candidate) {
handleCandidate(event.candidate.candidate);
} else {
handleCandidate(); // Pass nothing to tell that ICE-gathering-ended
}
};
// create data channel
if (!stream) {
try {
pc.createDataChannel('sctp', {});
} catch (e) {}
}
// create an offer sdp
if (DetectRTC.isPromisesSupported) {
pc.createOffer().then(function(result) {
pc.setLocalDescription(result).then(afterCreateOffer);
});
} else {
pc.createOffer(function(result) {
pc.setLocalDescription(result, afterCreateOffer, function() {});
}, function() {});
}
function afterCreateOffer() {
var lines = pc.localDescription.sdp.split('\n');
lines.forEach(function(line) {
if (line && line.indexOf('a=candidate:') === 0) {
handleCandidate(line);
}
});
}
}
var MediaDevices = [];
var audioInputDevices = [];
var audioOutputDevices = [];
var videoInputDevices = [];
if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
// Firefox 38+ seems having support of enumerateDevices
// Thanks @xdumaine/enumerateDevices
navigator.enumerateDevices = function(callback) {
var enumerateDevices = navigator.mediaDevices.enumerateDevices();
if (enumerateDevices && enumerateDevices.then) {
navigator.mediaDevices.enumerateDevices().then(callback).catch(function() {
callback([]);
});
} else {
callback([]);
}
};
}
// Media Devices detection
var canEnumerate = false;
/*global MediaStreamTrack:true */
if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) {
canEnumerate = true;
} else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) {
canEnumerate = true;
}
var hasMicrophone = false;
var hasSpeakers = false;
var hasWebcam = false;
var isWebsiteHasMicrophonePermissions = false;
var isWebsiteHasWebcamPermissions = false;
// http://dev.w3.org/2011/webrtc/editor/getusermedia.html#mediadevices
function checkDeviceSupport(callback) {
if (!canEnumerate) {
if (callback) {
callback();
}
return;
}
if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) {
navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack);
}
if (!navigator.enumerateDevices && navigator.enumerateDevices) {
navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator);
}
if (!navigator.enumerateDevices) {
if (callback) {
callback();
}
return;
}
MediaDevices = [];
audioInputDevices = [];
audioOutputDevices = [];
videoInputDevices = [];
hasMicrophone = false;
hasSpeakers = false;
hasWebcam = false;
isWebsiteHasMicrophonePermissions = false;
isWebsiteHasWebcamPermissions = false;
// to prevent duplication
var alreadyUsedDevices = {};
navigator.enumerateDevices(function(devices) {
devices.forEach(function(_device) {
var device = {};
for (var d in _device) {
try {
if (typeof _device[d] !== 'function') {
device[d] = _device[d];
}
} catch (e) {}
}
if (alreadyUsedDevices[device.deviceId + device.label + device.kind]) {
return;
}
// if it is MediaStreamTrack.getSources
if (device.kind === 'audio') {
device.kind = 'audioinput';
}
if (device.kind === 'video') {
device.kind = 'videoinput';
}
if (!device.deviceId) {
device.deviceId = device.id;
}
if (!device.id) {
device.id = device.deviceId;
}
if (!device.label) {
device.isCustomLabel = true;
if (device.kind === 'videoinput') {
device.label = 'Camera ' + (videoInputDevices.length + 1);
} else if (device.kind === 'audioinput') {
device.label = 'Microphone ' + (audioInputDevices.length + 1);
} else if (device.kind === 'audiooutput') {
device.label = 'Speaker ' + (audioOutputDevices.length + 1);
} else {
device.label = 'Please invoke getUserMedia once.';
}
if (typeof DetectRTC !== 'undefined' && DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1) {
device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.';
}
}
} else {
// Firefox on Android still returns empty label
if (device.kind === 'videoinput' && !isWebsiteHasWebcamPermissions) {
isWebsiteHasWebcamPermissions = true;
}
if (device.kind === 'audioinput' && !isWebsiteHasMicrophonePermissions) {
isWebsiteHasMicrophonePermissions = true;
}
}
if (device.kind === 'audioinput') {
hasMicrophone = true;
if (audioInputDevices.indexOf(device) === -1) {
audioInputDevices.push(device);
}
}
if (device.kind === 'audiooutput') {
hasSpeakers = true;
if (audioOutputDevices.indexOf(device) === -1) {
audioOutputDevices.push(device);
}
}
if (device.kind === 'videoinput') {
hasWebcam = true;
if (videoInputDevices.indexOf(device) === -1) {
videoInputDevices.push(device);
}
}
// there is no 'videoouput' in the spec.
MediaDevices.push(device);
alreadyUsedDevices[device.deviceId + device.label + device.kind] = device;
});
if (typeof DetectRTC !== 'undefined') {
// to sync latest outputs
DetectRTC.MediaDevices = MediaDevices;
DetectRTC.hasMicrophone = hasMicrophone;
DetectRTC.hasSpeakers = hasSpeakers;
DetectRTC.hasWebcam = hasWebcam;
DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions;
DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions;
DetectRTC.audioInputDevices = audioInputDevices;
DetectRTC.audioOutputDevices = audioOutputDevices;
DetectRTC.videoInputDevices = videoInputDevices;
}
if (callback) {
callback();
}
});
}
var DetectRTC = window.DetectRTC || {};
// ----------
// DetectRTC.browser.name || DetectRTC.browser.version || DetectRTC.browser.fullVersion
DetectRTC.browser = getBrowserInfo();
detectPrivateMode(function(isPrivateBrowsing) {
DetectRTC.browser.isPrivateBrowsing = !!isPrivateBrowsing;
});
// DetectRTC.isChrome || DetectRTC.isFirefox || DetectRTC.isEdge
DetectRTC.browser['is' + DetectRTC.browser.name] = true;
// -----------
DetectRTC.osName = osName;
DetectRTC.osVersion = osVersion;
var isNodeWebkit = typeof process === 'object' && typeof process.versions === 'object' && process.versions['node-webkit'];
// --------- Detect if system supports WebRTC 1.0 or WebRTC 1.1.
var isWebRTCSupported = false;
['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'].forEach(function(item) {
if (isWebRTCSupported) {
return;
}
if (item in window) {
isWebRTCSupported = true;
}
});
DetectRTC.isWebRTCSupported = isWebRTCSupported;
//-------
DetectRTC.isORTCSupported = typeof RTCIceGatherer !== 'undefined';
// --------- Detect if system supports screen capturing API
var isScreenCapturingSupported = false;
if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 35) {
isScreenCapturingSupported = true;
} else if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 34) {
isScreenCapturingSupported = true;
} else if (DetectRTC.browser.isEdge && DetectRTC.browser.version >= 17) {
isScreenCapturingSupported = true; // navigator.getDisplayMedia
} else if (DetectRTC.osName === 'Android' && DetectRTC.browser.isChrome) {
isScreenCapturingSupported = true;
}
if (!/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
var isNonLocalHost = typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1;
if (isNonLocalHost && (DetectRTC.browser.isChrome || DetectRTC.browser.isEdge || DetectRTC.browser.isOpera)) {
isScreenCapturingSupported = false;
} else if (DetectRTC.browser.isFirefox) {
isScreenCapturingSupported = false;
}
}
DetectRTC.isScreenCapturingSupported = isScreenCapturingSupported;
// --------- Detect if WebAudio API are supported
var webAudio = {
isSupported: false,
isCreateMediaStreamSourceSupported: false
};
['AudioContext', 'webkitAudioContext', 'mozAudioContext', 'msAudioContext'].forEach(function(item) {
if (webAudio.isSupported) {
return;
}
if (item in window) {
webAudio.isSupported = true;
if (window[item] && 'createMediaStreamSource' in window[item].prototype) {
webAudio.isCreateMediaStreamSourceSupported = true;
}
}
});
DetectRTC.isAudioContextSupported = webAudio.isSupported;
DetectRTC.isCreateMediaStreamSourceSupported = webAudio.isCreateMediaStreamSourceSupported;
// ---------- Detect if SCTP/RTP channels are supported.
var isRtpDataChannelsSupported = false;
if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 31) {
isRtpDataChannelsSupported = true;
}
DetectRTC.isRtpDataChannelsSupported = isRtpDataChannelsSupported;
var isSCTPSupportd = false;
if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 28) {
isSCTPSupportd = true;
} else if (DetectRTC.browser.isChrome && DetectRTC.browser.version > 25) {
isSCTPSupportd = true;
} else if (DetectRTC.browser.isOpera && DetectRTC.browser.version >= 11) {
isSCTPSupportd = true;
}
DetectRTC.isSctpDataChannelsSupported = isSCTPSupportd;
// ---------
DetectRTC.isMobileDevice = isMobileDevice; // "isMobileDevice" boolean is defined in "getBrowserInfo.js"
// ------
var isGetUserMediaSupported = false;
if (navigator.getUserMedia) {
isGetUserMediaSupported = true;
} else if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
isGetUserMediaSupported = true;
}
if (DetectRTC.browser.isChrome && DetectRTC.browser.version >= 46 && !/^(https:|chrome-extension:)$/g.test(location.protocol || '')) {
if (typeof document !== 'undefined' && typeof document.domain === 'string' && document.domain.search && document.domain.search(/localhost|127.0./g) === -1) {
isGetUserMediaSupported = 'Requires HTTPs';
}
}
if (DetectRTC.osName === 'Nodejs') {
isGetUserMediaSupported = false;
}
DetectRTC.isGetUserMediaSupported = isGetUserMediaSupported;
var displayResolution = '';
if (screen.width) {
var width = (screen.width) ? screen.width : '';
var height = (screen.height) ? screen.height : '';
displayResolution += '' + width + ' x ' + height;
}
DetectRTC.displayResolution = displayResolution;
function getAspectRatio(w, h) {
function gcd(a, b) {
return (b == 0) ? a : gcd(b, a % b);
}
var r = gcd(w, h);
return (w / r) / (h / r);
}
DetectRTC.displayAspectRatio = getAspectRatio(screen.width, screen.height).toFixed(2);
// ----------
DetectRTC.isCanvasSupportsStreamCapturing = isCanvasSupportsStreamCapturing;
DetectRTC.isVideoSupportsStreamCapturing = isVideoSupportsStreamCapturing;
if (DetectRTC.browser.name == 'Chrome' && DetectRTC.browser.version >= 53) {
if (!DetectRTC.isCanvasSupportsStreamCapturing) {
DetectRTC.isCanvasSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features';
}
if (!DetectRTC.isVideoSupportsStreamCapturing) {
DetectRTC.isVideoSupportsStreamCapturing = 'Requires chrome flag: enable-experimental-web-platform-features';
}
}
// ------
DetectRTC.DetectLocalIPAddress = DetectLocalIPAddress;
DetectRTC.isWebSocketsSupported = 'WebSocket' in window && 2 === window.WebSocket.CLOSING;
DetectRTC.isWebSocketsBlocked = !DetectRTC.isWebSocketsSupported;
if (DetectRTC.osName === 'Nodejs') {
DetectRTC.isWebSocketsSupported = true;
DetectRTC.isWebSocketsBlocked = false;
}
DetectRTC.checkWebSocketsSupport = function(callback) {
callback = callback || function() {};
try {
var starttime;
var websocket = new WebSocket('wss://echo.websocket.org:443/');
websocket.onopen = function() {
DetectRTC.isWebSocketsBlocked = false;
starttime = (new Date).getTime();
websocket.send('ping');
};
websocket.onmessage = function() {
DetectRTC.WebsocketLatency = (new Date).getTime() - starttime + 'ms';
callback();
websocket.close();
websocket = null;
};
websocket.onerror = function() {
DetectRTC.isWebSocketsBlocked = true;
callback();
};
} catch (e) {
DetectRTC.isWebSocketsBlocked = true;
callback();
}
};
// -------
DetectRTC.load = function(callback) {
callback = callback || function() {};
checkDeviceSupport(callback);
};
// check for microphone/camera support!
if (typeof checkDeviceSupport === 'function') {
// checkDeviceSupport();
}
if (typeof MediaDevices !== 'undefined') {
DetectRTC.MediaDevices = MediaDevices;
} else {
DetectRTC.MediaDevices = [];
}
DetectRTC.hasMicrophone = hasMicrophone;
DetectRTC.hasSpeakers = hasSpeakers;
DetectRTC.hasWebcam = hasWebcam;
DetectRTC.isWebsiteHasWebcamPermissions = isWebsiteHasWebcamPermissions;
DetectRTC.isWebsiteHasMicrophonePermissions = isWebsiteHasMicrophonePermissions;
DetectRTC.audioInputDevices = audioInputDevices;
DetectRTC.audioOutputDevices = audioOutputDevices;
DetectRTC.videoInputDevices = videoInputDevices;
// ------
var isSetSinkIdSupported = false;
if (typeof document !== 'undefined' && typeof document.createElement === 'function' && 'setSinkId' in document.createElement('video')) {
isSetSinkIdSupported = true;
}
DetectRTC.isSetSinkIdSupported = isSetSinkIdSupported;
// -----
var isRTPSenderReplaceTracksSupported = false;
if (DetectRTC.browser.isFirefox && typeof mozRTCPeerConnection !== 'undefined' /*&& DetectRTC.browser.version > 39*/ ) {
/*global mozRTCPeerConnection:true */
if ('getSenders' in mozRTCPeerConnection.prototype) {
isRTPSenderReplaceTracksSupported = true;
}
} else if (DetectRTC.browser.isChrome && typeof webkitRTCPeerConnection !== 'undefined') {
/*global webkitRTCPeerConnection:true */
if ('getSenders' in webkitRTCPeerConnection.prototype) {
isRTPSenderReplaceTracksSupported = true;
}
}
DetectRTC.isRTPSenderReplaceTracksSupported = isRTPSenderReplaceTracksSupported;
//------
var isRemoteStreamProcessingSupported = false;
if (DetectRTC.browser.isFirefox && DetectRTC.browser.version > 38) {
isRemoteStreamProcessingSupported = true;
}
DetectRTC.isRemoteStreamProcessingSupported = isRemoteStreamProcessingSupported;
//-------
var isApplyConstraintsSupported = false;
/*global MediaStreamTrack:true */
if (typeof MediaStreamTrack !== 'undefined' && 'applyConstraints' in MediaStreamTrack.prototype) {
isApplyConstraintsSupported = true;
}
DetectRTC.isApplyConstraintsSupported = isApplyConstraintsSupported;
//-------
var isMultiMonitorScreenCapturingSupported = false;
if (DetectRTC.browser.isFirefox && DetectRTC.browser.version >= 43) {
// version 43 merely supports platforms for multi-monitors
// version 44 will support exact multi-monitor selection i.e. you can select any monitor for screen capturing.
isMultiMonitorScreenCapturingSupported = true;
}
DetectRTC.isMultiMonitorScreenCapturingSupported = isMultiMonitorScreenCapturingSupported;
DetectRTC.isPromisesSupported = !!('Promise' in window);
// version is generated by "grunt"
DetectRTC.version = '1.3.9';
if (typeof DetectRTC === 'undefined') {
window.DetectRTC = {};
}
var MediaStream = window.MediaStream;
if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
MediaStream = webkitMediaStream;
}
if (typeof MediaStream !== 'undefined' && typeof MediaStream === 'function') {
DetectRTC.MediaStream = Object.keys(MediaStream.prototype);
} else DetectRTC.MediaStream = false;
if (typeof MediaStreamTrack !== 'undefined') {
DetectRTC.MediaStreamTrack = Object.keys(MediaStreamTrack.prototype);
} else DetectRTC.MediaStreamTrack = false;
var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
if (typeof RTCPeerConnection !== 'undefined') {
DetectRTC.RTCPeerConnection = Object.keys(RTCPeerConnection.prototype);
} else DetectRTC.RTCPeerConnection = false;
window.DetectRTC = DetectRTC;
if (typeof module !== 'undefined' /* && !!module.exports*/ ) {
module.exports = DetectRTC;
}
if (typeof define === 'function' && define.amd) {
define('DetectRTC', [], function() {
return DetectRTC;
});
}
})();
// globals.js
if (typeof cordova !== 'undefined') {
DetectRTC.isMobileDevice = true;
DetectRTC.browser.name = 'Chrome';
}
if (navigator && navigator.userAgent && navigator.userAgent.indexOf('Crosswalk') !== -1) {
DetectRTC.isMobileDevice = true;
DetectRTC.browser.name = 'Chrome';
}
function fireEvent(obj, eventName, args) {
if (typeof CustomEvent === 'undefined') {
return;
}
var eventDetail = {
arguments: args,
__exposedProps__: args
};
var event = new CustomEvent(eventName, eventDetail);
obj.dispatchEvent(event);
}
function setHarkEvents(connection, streamEvent) {
if (!streamEvent.stream || !getTracks(streamEvent.stream, 'audio').length) return;
if (!connection || !streamEvent) {
throw 'Both arguments are required.';
}
if (!connection.onspeaking || !connection.onsilence) {
return;
}
if (typeof hark === 'undefined') {
throw 'hark.js not found.';
}
hark(streamEvent.stream, {
onspeaking: function() {
connection.onspeaking(streamEvent);
},
onsilence: function() {
connection.onsilence(streamEvent);
},
onvolumechange: function(volume, threshold) {
if (!connection.onvolumechange) {
return;
}
connection.onvolumechange(merge({
volume: volume,
threshold: threshold
}, streamEvent));
}
});
}
function setMuteHandlers(connection, streamEvent) {
if (!streamEvent.stream || !streamEvent.stream || !streamEvent.stream.addEventListener) return;
streamEvent.stream.addEventListener('mute', function(event) {
event = connection.streamEvents[streamEvent.streamid];
event.session = {
audio: event.muteType === 'audio',
video: event.muteType === 'video'
};
connection.onmute(event);
}, false);
streamEvent.stream.addEventListener('unmute', function(event) {
event = connection.streamEvents[streamEvent.streamid];
event.session = {
audio: event.unmuteType === 'audio',
video: event.unmuteType === 'video'
};
connection.onunmute(event);
}, false);
}
function getRandomString() {
if (window.crypto && window.crypto.getRandomValues && navigator.userAgent.indexOf('Safari') === -1) {
var a = window.crypto.getRandomValues(new Uint32Array(3)),
token = '';
for (var i = 0, l = a.length; i < l; i++) {
token += a[i].toString(36);
}
return token;
} else {
return (Math.random() * new Date().getTime()).toString(36).replace(/\./g, '');
}
}
// Get HTMLAudioElement/HTMLVideoElement accordingly
// todo: add API documentation for connection.autoCreateMediaElement
function getRMCMediaElement(stream, callback, connection) {
if (!connection.autoCreateMediaElement) {
callback({});
return;
}
var isAudioOnly = false;
if (!getTracks(stream, 'video').length && !stream.isVideo && !stream.isScreen) {
isAudioOnly = true;
}
if (DetectRTC.browser.name === 'Firefox') {
if (connection.session.video || connection.session.screen) {
isAudioOnly = false;
}
}
var mediaElement = document.createElement(isAudioOnly ? 'audio' : 'video');
mediaElement.srcObject = stream;
mediaElement.setAttribute('autoplay', true);
mediaElement.setAttribute('playsinline', true);
mediaElement.setAttribute('controls', true);
mediaElement.setAttribute('muted', false);
mediaElement.setAttribute('volume', 1);
// http://goo.gl/WZ5nFl
// Firefox don't yet support onended for any stream (remote/local)
if (DetectRTC.browser.name === 'Firefox') {
var streamEndedEvent = 'ended';
if ('oninactive' in mediaElement) {
streamEndedEvent = 'inactive';
}
mediaElement.addEventListener(streamEndedEvent, function() {
// fireEvent(stream, streamEndedEvent, stream);
currentUserMediaRequest.remove(stream.idInstance);
if (stream.type === 'local') {
streamEndedEvent = 'ended';
if ('oninactive' in stream) {
streamEndedEvent = 'inactive';
}
StreamsHandler.onSyncNeeded(stream.streamid, streamEndedEvent);
connection.attachStreams.forEach(function(aStream, idx) {
if (stream.streamid === aStream.streamid) {
delete connection.attachStreams[idx];
}
});
var newStreamsArray = [];
connection.attachStreams.forEach(function(aStream) {
if (aStream) {
newStreamsArray.push(aStream);
}
});
connection.attachStreams = newStreamsArray;
var streamEvent = connection.streamEvents[stream.streamid];
if (streamEvent) {
connection.onstreamended(streamEvent);
return;
}
if (this.parentNode) {
this.parentNode.removeChild(this);
}
}
}, false);
}
var played = mediaElement.play();
if (typeof played !== 'undefined') {
var cbFired = false;
setTimeout(function() {
if (!cbFired) {
cbFired = true;
callback(mediaElement);
}
}, 1000);
played.then(function() {
if (cbFired) return;
cbFired = true;
callback(mediaElement);
}).catch(function(error) {
if (cbFired) return;
cbFired = true;
callback(mediaElement);
});
} else {
callback(mediaElement);
}
}
// if IE
if (!window.addEventListener) {
window.addEventListener = function(el, eventName, eventHandler) {
if (!el.attachEvent) {
return;
}
el.attachEvent('on' + eventName, eventHandler);
};
}
function listenEventHandler(eventName, eventHandler) {
window.removeEventListener(eventName, eventHandler);
window.addEventListener(eventName, eventHandler, false);
}
window.attachEventListener = function(video, type, listener, useCapture) {
video.addEventListener(type, listener, useCapture);
};
function removeNullEntries(array) {
var newArray = [];
array.forEach(function(item) {
if (item) {
newArray.push(item);
}
});
return newArray;
}
function isData(session) {
return !session.audio && !session.video && !session.screen && session.data;
}
function isNull(obj) {
return typeof obj === 'undefined';
}
function isString(obj) {
return typeof obj === 'string';
}
var MediaStream = window.MediaStream;
if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
MediaStream = webkitMediaStream;
}
/*global MediaStream:true */
if (typeof MediaStream !== 'undefined') {
if (!('stop' in MediaStream.prototype)) {
MediaStream.prototype.stop = function() {
this.getTracks().forEach(function(track) {
track.stop();
});
};
}
}
function isAudioPlusTab(connection, audioPlusTab) {
if (connection.session.audio && connection.session.audio === 'two-way') {
return false;
}
if (DetectRTC.browser.name === 'Firefox' && audioPlusTab !== false) {
return true;
}
if (DetectRTC.browser.name !== 'Chrome' || DetectRTC.browser.version < 50) return false;
if (typeof audioPlusTab === true) {
return true;
}
if (typeof audioPlusTab === 'undefined' && connection.session.audio && connection.session.screen && !connection.session.video) {
audioPlusTab = true;
return true;
}
return false;
}
function getAudioScreenConstraints(screen_constraints) {
if (DetectRTC.browser.name === 'Firefox') {
return true;
}
if (DetectRTC.browser.name !== 'Chrome') return false;
return {
mandatory: {
chromeMediaSource: screen_constraints.mandatory.chromeMediaSource,
chromeMediaSourceId: screen_constraints.mandatory.chromeMediaSourceId
}
};
}
window.iOSDefaultAudioOutputDevice = window.iOSDefaultAudioOutputDevice || 'speaker'; // earpiece or speaker
function getTracks(stream, kind) {
if (!stream || !stream.getTracks) {
return [];
}
return stream.getTracks().filter(function(t) {
return t.kind === (kind || 'audio');
});
}
function isUnifiedPlanSupportedDefault() {
var canAddTransceiver = false;
try {
if (typeof RTCRtpTransceiver === 'undefined') return false;
if (!('currentDirection' in RTCRtpTransceiver.prototype)) return false;
var tempPc = new RTCPeerConnection();
try {
tempPc.addTransceiver('audio');
canAddTransceiver = true;
} catch (e) {}
tempPc.close();
} catch (e) {
canAddTransceiver = false;
}
return canAddTransceiver && isUnifiedPlanSuppored();
}
function isUnifiedPlanSuppored() {
var isUnifiedPlanSupported = false;
try {
var pc = new RTCPeerConnection({
sdpSemantics: 'unified-plan'
});
try {
var config = pc.getConfiguration();
if (config.sdpSemantics == 'unified-plan')
isUnifiedPlanSupported = true;
else if (config.sdpSemantics == 'plan-b')
isUnifiedPlanSupported = false;
else
isUnifiedPlanSupported = false;
} catch (e) {
isUnifiedPlanSupported = false;
}
} catch (e) {
isUnifiedPlanSupported = false;
}
return isUnifiedPlanSupported;
}
// ios-hacks.js
function setCordovaAPIs() {
// if (DetectRTC.osName !== 'iOS') return;
if (typeof cordova === 'undefined' || typeof cordova.plugins === 'undefined' || typeof cordova.plugins.iosrtc === 'undefined') return;
var iosrtc = cordova.plugins.iosrtc;
window.webkitRTCPeerConnection = iosrtc.RTCPeerConnection;
window.RTCSessionDescription = iosrtc.RTCSessionDescription;
window.RTCIceCandidate = iosrtc.RTCIceCandidate;
window.MediaStream = iosrtc.MediaStream;
window.MediaStreamTrack = iosrtc.MediaStreamTrack;
navigator.getUserMedia = navigator.webkitGetUserMedia = iosrtc.getUserMedia;
iosrtc.debug.enable('iosrtc*');
if (typeof iosrtc.selectAudioOutput == 'function') {
iosrtc.selectAudioOutput(window.iOSDefaultAudioOutputDevice || 'speaker'); // earpiece or speaker
}
iosrtc.registerGlobals();
}
document.addEventListener('deviceready', setCordovaAPIs, false);
setCordovaAPIs();
// RTCPeerConnection.js
var defaults = {};
function setSdpConstraints(config) {
var sdpConstraints = {
OfferToReceiveAudio: !!config.OfferToReceiveAudio,
OfferToReceiveVideo: !!config.OfferToReceiveVideo
};
return sdpConstraints;
}
var RTCPeerConnection;
if (typeof window.RTCPeerConnection !== 'undefined') {
RTCPeerConnection = window.RTCPeerConnection;
} else if (typeof mozRTCPeerConnection !== 'undefined') {
RTCPeerConnection = mozRTCPeerConnection;
} else if (typeof webkitRTCPeerConnection !== 'undefined') {
RTCPeerConnection = webkitRTCPeerConnection;
}
var RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription;
var RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate;
var MediaStreamTrack = window.MediaStreamTrack;
function PeerInitiator(config) {
if (typeof window.RTCPeerConnection !== 'undefined') {
RTCPeerConnection = window.RTCPeerConnection;
} else if (typeof mozRTCPeerConnection !== 'undefined') {
RTCPeerConnection = mozRTCPeerConnection;
} else if (typeof webkitRTCPeerConnection !== 'undefined') {
RTCPeerConnection = webkitRTCPeerConnection;
}
RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription;
RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate;
MediaStreamTrack = window.MediaStreamTrack;
if (!RTCPeerConnection) {
throw 'WebRTC 1.0 (RTCPeerConnection) API are NOT available in this browser.';
}
var connection = config.rtcMultiConnection;
this.extra = config.remoteSdp ? config.remoteSdp.extra : connection.extra;
this.userid = config.userid;
this.streams = [];
this.channels = config.channels || [];
this.connectionDescription = config.connectionDescription;
this.addStream = function(session) {
connection.addStream(session, self.userid);
};
this.removeStream = function(streamid) {
connection.removeStream(streamid, self.userid);
};
var self = this;
if (config.remoteSdp) {
this.connectionDescription = config.remoteSdp.connectionDescription;
}
var allRemoteStreams = {};
defaults.sdpConstraints = setSdpConstraints({
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
});
var peer;
var renegotiatingPeer = !!config.renegotiatingPeer;
if (config.remoteSdp) {
renegotiatingPeer = !!config.remoteSdp.renegotiatingPeer;
}
var localStreams = [];
connection.attachStreams.forEach(function(stream) {
if (!!stream) {
localStreams.push(stream);
}
});
if (!renegotiatingPeer) {
var iceTransports = 'all';
if (connection.candidates.turn || connection.candidates.relay) {
if (!connection.candidates.stun && !connection.candidates.reflexive && !connection.candidates.host) {
iceTransports = 'relay';
}
}
try {
// ref: developer.mozilla.org/en-US/docs/Web/API/RTCConfiguration
var params = {
iceServers: connection.iceServers,
iceTransportPolicy: connection.iceTransportPolicy || iceTransports
};
if (typeof connection.iceCandidatePoolSize !== 'undefined') {
params.iceCandidatePoolSize = connection.iceCandidatePoolSize;
}
if (typeof connection.bundlePolicy !== 'undefined') {
params.bundlePolicy = connection.bundlePolicy;
}
if (typeof connection.rtcpMuxPolicy !== 'undefined') {
params.rtcpMuxPolicy = connection.rtcpMuxPolicy;
}
if (!!connection.sdpSemantics) {
params.sdpSemantics = connection.sdpSemantics || 'unified-plan';
}
if (!connection.iceServers || !connection.iceServers.length) {
params = null;
connection.optionalArgument = null;
}
peer = new RTCPeerConnection(params, connection.optionalArgument);
} catch (e) {
try {
var params = {
iceServers: connection.iceServers
};
peer = new RTCPeerConnection(params);
} catch (e) {
peer = new RTCPeerConnection();
}
}
} else {
peer = config.peerRef;
}
if (!peer.getRemoteStreams && peer.getReceivers) {
peer.getRemoteStreams = function() {
var stream = new MediaStream();
peer.getReceivers().forEach(function(receiver) {
stream.addTrack(receiver.track);
});
return [stream];
};
}
if (!peer.getLocalStreams && peer.getSenders) {
peer.getLocalStreams = function() {
var stream = new MediaStream();
peer.getSenders().forEach(function(sender) {
stream.addTrack(sender.track);
});
return [stream];
};
}
peer.onicecandidate = function(event) {
if (!event.candidate) {
if (!connection.trickleIce) {
var localSdp = peer.localDescription;
config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || false,
renegotiatingPeer: !!config.renegotiatingPeer || false,
connectionDescription: self.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare
});
}
return;
}
if (!connection.trickleIce) return;
config.onLocalCandidate({
candidate: event.candidate.candidate,
sdpMid: event.candidate.sdpMid,
sdpMLineIndex: event.candidate.sdpMLineIndex
});
};
localStreams.forEach(function(localStream) {
if (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.dontGetRemoteStream) {
return;
}
if (config.dontAttachLocalStream) {
return;
}
localStream = connection.beforeAddingStream(localStream, self);
if (!localStream) return;
peer.getLocalStreams().forEach(function(stream) {
if (localStream && stream.id == localStream.id) {
localStream = null;
}
});
if (localStream && localStream.getTracks) {
localStream.getTracks().forEach(function(track) {
try {
// last parameter is redundant for unified-plan
// starting from chrome version 72
peer.addTrack(track, localStream);
} catch (e) {}
});
}
});
/* peer.oniceconnectionstatechange = peer.onsignalingstatechange = function() {
var extra = self.extra;
if (connection.peers[self.userid]) {
extra = connection.peers[self.userid].extra || extra;
}
if (!peer) {
return;
}
config.onPeerStateChanged({
iceConnectionState: peer.iceConnectionState,
iceGatheringState: peer.iceGatheringState,
signalingState: peer.signalingState,
extra: extra,
userid: self.userid
});*/
/*if (peer && peer.iceConnectionState && peer.iceConnectionState.search(/closed|failed/gi) !== -1 && self.streams instanceof Array) {
self.streams.forEach(function(stream) {
var streamEvent = connection.streamEvents[stream.id] || {
streamid: stream.id,
stream: stream,
type: 'remote'
};
connection.onstreamended(streamEvent);
});
}*/
// };
var sdpConstraints = {
OfferToReceiveAudio: !!localStreams.length,
OfferToReceiveVideo: !!localStreams.length
};
if (config.localPeerSdpConstraints) sdpConstraints = config.localPeerSdpConstraints;
defaults.sdpConstraints = setSdpConstraints(sdpConstraints);
var streamObject;
var dontDuplicate = {};
peer.ontrack = function(event) {
if (!event || event.type !== 'track') return;
event.stream = event.streams[event.streams.length - 1];
if (!event.stream.id) {
event.stream.id = event.track.id;
}
if (dontDuplicate[event.stream.id] && DetectRTC.browser.name !== 'Safari') {
if (event.track) {
event.track.onended = function() { // event.track.onmute =
peer && peer.onremovestream(event);
};
}
return;
}
dontDuplicate[event.stream.id] = event.stream.id;
var streamsToShare = {};
if (config.remoteSdp && config.remoteSdp.streamsToShare) {
streamsToShare = config.remoteSdp.streamsToShare;
} else if (config.streamsToShare) {
streamsToShare = config.streamsToShare;
}
var streamToShare = streamsToShare[event.stream.id];
if (streamToShare) {
event.stream.isAudio = streamToShare.isAudio;
event.stream.isVideo = streamToShare.isVideo;
event.stream.isScreen = streamToShare.isScreen;
} else {
event.stream.isVideo = !!getTracks(event.stream, 'video').length;
event.stream.isAudio = !event.stream.isVideo;
event.stream.isScreen = false;
}
event.stream.streamid = event.stream.id;
allRemoteStreams[event.stream.id] = event.stream;
config.onRemoteStream(event.stream);
event.stream.getTracks().forEach(function(track) {
track.onended = function() { // track.onmute =
peer && peer.onremovestream(event);
};
});
event.stream.onremovetrack = function() {
peer && peer.onremovestream(event);
};
};
peer.onremovestream = function(event) {
// this event doesn't works anymore
event.stream.streamid = event.stream.id;
if (allRemoteStreams[event.stream.id]) {
delete allRemoteStreams[event.stream.id];
}
config.onRemoteStreamRemoved(event.stream);
};
if (typeof peer.removeStream !== 'function') {
// removeStream backward compatibility
peer.removeStream = function(stream) {
stream.getTracks().forEach(function(track) {
peer.removeTrack(track, stream);
});
};
}
this.addRemoteCandidate = function(remoteCandidate) {
peer.addIceCandidate(new RTCIceCandidate(remoteCandidate));
};
function oldAddRemoteSdp(remoteSdp, cb) {
cb = cb || function() {};
if (DetectRTC.browser.name !== 'Safari') {
remoteSdp.sdp = connection.processSdp(remoteSdp.sdp);
}
peer.setRemoteDescription(new RTCSessionDescription(remoteSdp), cb, function(error) {
if (!!connection.enableLogs) {
console.error('setRemoteDescription failed', '\n', error, '\n', remoteSdp.sdp);
/*var rem3 = localStorage.getItem("usernow");
var ssid3 = localStorage.getItem("tablesession");
ssid3 = JSON.parse(ssid3);
var myarray3 = rem3 + ',' + ssid3 ;
myarray3 = JSON.stringify(myarray3);
connection.socket.emit('rej',myarray3);*/
/*connection.socket.emit('join-room', {
sessionid: connection.sessionid,
session: connection.session,
mediaConstraints: connection.mediaConstraints,
sdpConstraints: connection.sdpConstraints,
streams: getStreamInfoForAdmin(),
extra: connection.extra,
password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : ''
});*/
/*setTimeout(function(){connection.closeSocket();
connection.attachStreams.forEach(function(stream) {
stream.stop();
});
var ssid3 = localStorage.getItem("tablesession");
ssid3 = JSON.parse(ssid3);
connection.openOrJoin(ssid3);}, 5000);*/
}
cb();
});
}
this.addRemoteSdp = function(remoteSdp, cb) {
cb = cb || function() {};
if (DetectRTC.browser.name !== 'Safari') {
remoteSdp.sdp = connection.processSdp(remoteSdp.sdp);
}
peer.setRemoteDescription(new RTCSessionDescription(remoteSdp)).then(cb, function(error) {
if (!!connection.enableLogs) {
console.error('setRemoteDescription failed', '\n', error, '\n', remoteSdp.sdp);
/*var rem4 = localStorage.getItem("usernow");
var ssid4 = localStorage.getItem("tablesession");
ssid4 = JSON.parse(ssid4);
var myarray4 = rem4 + ',' + ssid4 ;
myarray4 = JSON.stringify(myarray4);
connection.socket.emit('rej',myarray4);*/
/*connection.socket.emit('join-room', {
sessionid: connection.sessionid,
session: connection.session,
mediaConstraints: connection.mediaConstraints,
sdpConstraints: connection.sdpConstraints,
streams: getStreamInfoForAdmin(),
extra: connection.extra,
password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : ''
});*/
/*setTimeout(function(){connection.closeSocket();
connection.attachStreams.forEach(function(stream) {
stream.stop();
});
var ssid4 = localStorage.getItem("tablesession");
ssid4 = JSON.parse(ssid4);
connection.openOrJoin(ssid4);}, 5000);*/
}
cb();
}).catch(function(error) {
if (!!connection.enableLogs) {
console.error('setRemoteDescription failed', '\n', error, '\n', remoteSdp.sdp);
/*var rem5 = localStorage.getItem("usernow");
var ssid5 = localStorage.getItem("tablesession");
ssid5 = JSON.parse(ssid5);
var myarray5 = rem5 + ',' + ssid5 ;
myarray5 = JSON.stringify(myarray5);
connection.socket.emit('rej',myarray5);*/
/*connection.socket.emit('join-room', {
sessionid: connection.sessionid,
session: connection.session,
mediaConstraints: connection.mediaConstraints,
sdpConstraints: connection.sdpConstraints,
streams: getStreamInfoForAdmin(),
extra: connection.extra,
password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : ''
});*/
/*setTimeout(function(){connection.closeSocket();
connection.attachStreams.forEach(function(stream) {
stream.stop();
});
var ssid5 = localStorage.getItem("tablesession");
ssid5 = JSON.parse(ssid5);
connection.openOrJoin(ssid5);}, 5000);*/
}
cb();
});
};
var isOfferer = true;
if (config.remoteSdp) {
isOfferer = false;
}
this.createDataChannel = function() {
var channel = peer.createDataChannel('sctp', {});
setChannelEvents(channel);
};
if (connection.session.data === true && !renegotiatingPeer) {
if (!isOfferer) {
peer.ondatachannel = function(event) {
var channel = event.channel;
setChannelEvents(channel);
};
} else {
this.createDataChannel();
}
}
this.enableDisableVideoEncoding = function(enable) {
var rtcp;
peer.getSenders().forEach(function(sender) {
if (!rtcp && sender.track.kind === 'video') {
rtcp = sender;
}
});
if (!rtcp || !rtcp.getParameters) return;
var parameters = rtcp.getParameters();
parameters.encodings[1] && (parameters.encodings[1].active = !!enable);
parameters.encodings[2] && (parameters.encodings[2].active = !!enable);
rtcp.setParameters(parameters);
};
if (config.remoteSdp) {
if (config.remoteSdp.remotePeerSdpConstraints) {
sdpConstraints = config.remoteSdp.remotePeerSdpConstraints;
}
defaults.sdpConstraints = setSdpConstraints(sdpConstraints);
this.addRemoteSdp(config.remoteSdp, function() {
createOfferOrAnswer('createAnswer');
});
}
function setChannelEvents(channel) {
// force ArrayBuffer in Firefox; which uses "Blob" by default.
channel.binaryType = 'arraybuffer';
channel.onmessage = function(event) {
config.onDataChannelMessage(event.data);
};
channel.onopen = function() {
config.onDataChannelOpened(channel);
};
channel.onerror = function(error) {
config.onDataChannelError(error);
};
channel.onclose = function(event) {
config.onDataChannelClosed(event);
};
channel.internalSend = channel.send;
channel.send = function(data) {
if (channel.readyState !== 'open') {
return;
}
channel.internalSend(data);
};
peer.channel = channel;
}
if (connection.session.audio == 'two-way' || connection.session.video == 'two-way' || connection.session.screen == 'two-way') {
defaults.sdpConstraints = setSdpConstraints({
OfferToReceiveAudio: connection.session.audio == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio),
OfferToReceiveVideo: connection.session.video == 'two-way' || connection.session.screen == 'two-way' || (config.remoteSdp && config.remoteSdp.remotePeerSdpConstraints && config.remoteSdp.remotePeerSdpConstraints.OfferToReceiveAudio)
});
}
var streamsToShare = {};
peer.getLocalStreams().forEach(function(stream) {
streamsToShare[stream.streamid] = {
isAudio: !!stream.isAudio,
isVideo: !!stream.isVideo,
isScreen: !!stream.isScreen
};
});
function oldCreateOfferOrAnswer(_method) {
peer[_method](function(localSdp) {
if (DetectRTC.browser.name !== 'Safari') {
localSdp.sdp = connection.processSdp(localSdp.sdp);
}
peer.setLocalDescription(localSdp, function() {
if (!connection.trickleIce) return;
config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || false,
renegotiatingPeer: !!config.renegotiatingPeer || false,
connectionDescription: self.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare
});
connection.onSettingLocalDescription(self);
}, function(error) {
if (!!connection.enableLogs) {
console.error('setLocalDescription-error', error);
}
});
}, function(error) {
if (!!connection.enableLogs) {
console.error('sdp-' + _method + '-error', error);
}
}, defaults.sdpConstraints);
}
function createOfferOrAnswer(_method) {
peer[_method](defaults.sdpConstraints).then(function(localSdp) {
if (DetectRTC.browser.name !== 'Safari') {
localSdp.sdp = connection.processSdp(localSdp.sdp);
}
peer.setLocalDescription(localSdp).then(function() {
if (!connection.trickleIce) return;
config.onLocalSdp({
type: localSdp.type,
sdp: localSdp.sdp,
remotePeerSdpConstraints: config.remotePeerSdpConstraints || false,
renegotiatingPeer: !!config.renegotiatingPeer || false,
connectionDescription: self.connectionDescription,
dontGetRemoteStream: !!config.dontGetRemoteStream,
extra: connection ? connection.extra : {},
streamsToShare: streamsToShare
});
connection.onSettingLocalDescription(self);
}, function(error) {
if (!connection.enableLogs) return;
console.error('setLocalDescription error', error);
});
}, function(error) {
if (!!connection.enableLogs) {
console.error('sdp-error', error);
}
});
}
if (isOfferer) {
createOfferOrAnswer('createOffer');
}
peer.nativeClose = peer.close;
peer.close = function() {
if (!peer) {
return;
}
try {
if (peer.nativeClose !== peer.close) {
peer.nativeClose();
}
} catch (e) {}
peer = null;
self.peer = null;
};
this.peer = peer;
}
// CodecsHandler.js
var CodecsHandler = (function() {
function preferCodec(sdp, codecName) {
var info = splitLines(sdp);
if (!info.videoCodecNumbers) {
return sdp;
}
if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
return sdp;
}
sdp = preferCodecHelper(sdp, codecName, info);
return sdp;
}
function preferCodecHelper(sdp, codec, info, ignore) {
var preferCodecNumber = '';
if (codec === 'vp8') {
if (!info.vp8LineNumber) {
return sdp;
}
preferCodecNumber = info.vp8LineNumber;
}
if (codec === 'vp9') {
if (!info.vp9LineNumber) {
return sdp;
}
preferCodecNumber = info.vp9LineNumber;
}
if (codec === 'h264') {
if (!info.h264LineNumber) {
return sdp;
}
preferCodecNumber = info.h264LineNumber;
}
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
var newOrder = [preferCodecNumber];
if (ignore) {
newOrder = [];
}
info.videoCodecNumbers.forEach(function(codecNumber) {
if (codecNumber === preferCodecNumber) return;
newOrder.push(codecNumber);
});
newLine += newOrder.join(' ');
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
return sdp;
}
function splitLines(sdp) {
var info = {};
sdp.split('\n').forEach(function(line) {
if (line.indexOf('m=video') === 0) {
info.videoCodecNumbers = [];
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
codecNumber = codecNumber.trim();
if (!codecNumber || !codecNumber.length) return;
info.videoCodecNumbers.push(codecNumber);
info.videoCodecNumbersOriginal = line;
});
}
if (line.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
if (line.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
}
});
return info;
}
function removeVPX(sdp) {
var info = splitLines(sdp);
// last parameter below means: ignore these codecs
sdp = preferCodecHelper(sdp, 'vp9', info, true);
sdp = preferCodecHelper(sdp, 'vp8', info, true);
return sdp;
}
function disableNACK(sdp) {
if (!sdp || typeof sdp !== 'string') {
throw 'Invalid arguments.';
}
sdp = sdp.replace('a=rtcp-fb:126 nack\r\n', '');
sdp = sdp.replace('a=rtcp-fb:126 nack pli\r\n', 'a=rtcp-fb:126 pli\r\n');
sdp = sdp.replace('a=rtcp-fb:97 nack\r\n', '');
sdp = sdp.replace('a=rtcp-fb:97 nack pli\r\n', 'a=rtcp-fb:97 pli\r\n');
return sdp;
}
function prioritize(codecMimeType, peer) {
if (!peer || !peer.getSenders || !peer.getSenders().length) {
return;
}
if (!codecMimeType || typeof codecMimeType !== 'string') {
throw 'Invalid arguments.';
}
peer.getSenders().forEach(function(sender) {
var params = sender.getParameters();
for (var i = 0; i < params.codecs.length; i++) {
if (params.codecs[i].mimeType == codecMimeType) {
params.codecs.unshift(params.codecs.splice(i, 1));
break;
}
}
sender.setParameters(params);
});
}
function removeNonG722(sdp) {
return sdp.replace(/m=audio ([0-9]+) RTP\/SAVPF ([0-9 ]*)/g, 'm=audio $1 RTP\/SAVPF 9');
}
function setBAS(sdp, bandwidth, isScreen) {
if (!bandwidth) {
return sdp;
}
if (typeof isFirefox !== 'undefined' && isFirefox) {
return sdp;
}
if (isScreen) {
if (!bandwidth.screen) {
console.warn('It seems that you are not using bandwidth for screen. Screen sharing is expected to fail.');
} else if (bandwidth.screen < 300) {
console.warn('It seems that you are using wrong bandwidth value for screen. Screen sharing is expected to fail.');
}
}
// if screen; must use at least 300kbs
if (bandwidth.screen && isScreen) {
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n');
}
// remove existing bandwidth lines
if (bandwidth.audio || bandwidth.video) {
sdp = sdp.replace(/b=AS([^\r\n]+\r\n)/g, '');
}
if (bandwidth.audio) {
sdp = sdp.replace(/a=mid:audio\r\n/g, 'a=mid:audio\r\nb=AS:' + bandwidth.audio + '\r\n');
}
if (bandwidth.screen) {
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.screen + '\r\n');
} else if (bandwidth.video) {
sdp = sdp.replace(/a=mid:video\r\n/g, 'a=mid:video\r\nb=AS:' + bandwidth.video + '\r\n');
}
return sdp;
}
// Find the line in sdpLines that starts with |prefix|, and, if specified,
// contains |substr| (case-insensitive search).
function findLine(sdpLines, prefix, substr) {
return findLineInRange(sdpLines, 0, -1, prefix, substr);
}
// Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
// and, if specified, contains |substr| (case-insensitive search).
function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
var realEndLine = endLine !== -1 ? endLine : sdpLines.length;
for (var i = startLine; i < realEndLine; ++i) {
if (sdpLines[i].indexOf(prefix) === 0) {
if (!substr ||
sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
return i;
}
}
}
return null;
}
// Gets the codec payload type from an a=rtpmap:X line.
function getCodecPayloadType(sdpLine) {
var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');
var result = sdpLine.match(pattern);
return (result && result.length === 2) ? result[1] : null;
}
function setVideoBitrates(sdp, params) {
params = params || {};
var xgoogle_min_bitrate = params.min;
var xgoogle_max_bitrate = params.max;
var sdpLines = sdp.split('\r\n');
// VP8
var vp8Index = findLine(sdpLines, 'a=rtpmap', 'VP8/90000');
var vp8Payload;
if (vp8Index) {
vp8Payload = getCodecPayloadType(sdpLines[vp8Index]);
}
if (!vp8Payload) {
return sdp;
}
var rtxIndex = findLine(sdpLines, 'a=rtpmap', 'rtx/90000');
var rtxPayload;
if (rtxIndex) {
rtxPayload = getCodecPayloadType(sdpLines[rtxIndex]);
}
if (!rtxIndex) {
return sdp;
}
var rtxFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + rtxPayload.toString());
if (rtxFmtpLineIndex !== null) {
var appendrtxNext = '\r\n';
appendrtxNext += 'a=fmtp:' + vp8Payload + ' x-google-min-bitrate=' + (xgoogle_min_bitrate || '228') + '; x-google-max-bitrate=' + (xgoogle_max_bitrate || '228');
sdpLines[rtxFmtpLineIndex] = sdpLines[rtxFmtpLineIndex].concat(appendrtxNext);
sdp = sdpLines.join('\r\n');
}
return sdp;
}
function setOpusAttributes(sdp, params) {
params = params || {};
var sdpLines = sdp.split('\r\n');
// Opus
var opusIndex = findLine(sdpLines, 'a=rtpmap', 'opus/48000');
var opusPayload;
if (opusIndex) {
opusPayload = getCodecPayloadType(sdpLines[opusIndex]);
}
if (!opusPayload) {
return sdp;
}
var opusFmtpLineIndex = findLine(sdpLines, 'a=fmtp:' + opusPayload.toString());
if (opusFmtpLineIndex === null) {
return sdp;
}
var appendOpusNext = '';
appendOpusNext += '; stereo=' + (typeof params.stereo != 'undefined' ? params.stereo : '1');
appendOpusNext += '; sprop-stereo=' + (typeof params['sprop-stereo'] != 'undefined' ? params['sprop-stereo'] : '1');
if (typeof params.maxaveragebitrate != 'undefined') {
appendOpusNext += '; maxaveragebitrate=' + (params.maxaveragebitrate || 128 * 1024 * 8);
}
if (typeof params.maxplaybackrate != 'undefined') {
appendOpusNext += '; maxplaybackrate=' + (params.maxplaybackrate || 128 * 1024 * 8);
}
if (typeof params.cbr != 'undefined') {
appendOpusNext += '; cbr=' + (typeof params.cbr != 'undefined' ? params.cbr : '1');
}
if (typeof params.useinbandfec != 'undefined') {
appendOpusNext += '; useinbandfec=' + params.useinbandfec;
}
if (typeof params.usedtx != 'undefined') {
appendOpusNext += '; usedtx=' + params.usedtx;
}
if (typeof params.maxptime != 'undefined') {
appendOpusNext += '\r\na=maxptime:' + params.maxptime;
}
sdpLines[opusFmtpLineIndex] = sdpLines[opusFmtpLineIndex].concat(appendOpusNext);
sdp = sdpLines.join('\r\n');
return sdp;
}
// forceStereoAudio => via webrtcexample.com
// requires getUserMedia => echoCancellation:false
function forceStereoAudio(sdp) {
var sdpLines = sdp.split('\r\n');
var fmtpLineIndex = null;
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('opus/48000') !== -1) {
var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
break;
}
}
for (var i = 0; i < sdpLines.length; i++) {
if (sdpLines[i].search('a=fmtp') !== -1) {
var payload = extractSdp(sdpLines[i], /a=fmtp:(\d+)/);
if (payload === opusPayload) {
fmtpLineIndex = i;
break;
}
}
}
if (fmtpLineIndex === null) return sdp;
sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat('; stereo=1; sprop-stereo=1');
sdp = sdpLines.join('\r\n');
return sdp;
}
return {
removeVPX: removeVPX,
disableNACK: disableNACK,
prioritize: prioritize,
removeNonG722: removeNonG722,
setApplicationSpecificBandwidth: function(sdp, bandwidth, isScreen) {
return setBAS(sdp, bandwidth, isScreen);
},
setVideoBitrates: function(sdp, params) {
return setVideoBitrates(sdp, params);
},
setOpusAttributes: function(sdp, params) {
return setOpusAttributes(sdp, params);
},
preferVP9: function(sdp) {
return preferCodec(sdp, 'vp9');
},
preferCodec: preferCodec,
forceStereoAudio: forceStereoAudio
};
})();
// backward compatibility
window.BandwidthHandler = CodecsHandler;
// OnIceCandidateHandler.js
var OnIceCandidateHandler = (function() {
function processCandidates(connection, icePair) {
var candidate = icePair.candidate;
var iceRestrictions = connection.candidates;
var stun = iceRestrictions.stun;
var turn = iceRestrictions.turn;
if (!isNull(iceRestrictions.reflexive)) {
stun = iceRestrictions.reflexive;
}
if (!isNull(iceRestrictions.relay)) {
turn = iceRestrictions.relay;
}
if (!iceRestrictions.host && !!candidate.match(/typ host/g)) {
return;
}
if (!turn && !!candidate.match(/typ relay/g)) {
return;
}
if (!stun && !!candidate.match(/typ srflx/g)) {
return;
}
var protocol = connection.iceProtocols;
if (!protocol.udp && !!candidate.match(/ udp /g)) {
return;
}
if (!protocol.tcp && !!candidate.match(/ tcp /g)) {
return;
}
if (connection.enableLogs) {
console.debug('Your candidate pairs:', candidate);
}
return {
candidate: candidate,
sdpMid: icePair.sdpMid,
sdpMLineIndex: icePair.sdpMLineIndex
};
}
return {
processCandidates: processCandidates
};
})();
// IceServersHandler.js
var IceServersHandler = (function() {
function getIceServers(connection) {
// resiprocate: 3344+4433
// pions: 7575
var iceServers = [{
'urls': [
'stun:stun.l.google.com:19302',
'stun:stun1.l.google.com:19302',
'stun:stun2.l.google.com:19302',
'stun:stun.l.google.com:19302?transport=udp',
]
}];
return iceServers;
}
return {
getIceServers: getIceServers
};
})();
// getUserMediaHandler.js
function setStreamType(constraints, stream) {
if (constraints.mandatory && constraints.mandatory.chromeMediaSource) {
stream.isScreen = true;
} else if (constraints.mozMediaSource || constraints.mediaSource) {
stream.isScreen = true;
} else if (constraints.video) {
stream.isVideo = true;
} else if (constraints.audio) {
stream.isAudio = true;
}
}
// allow users to manage this object (to support re-capturing of screen/etc.)
window.currentUserMediaRequest = {
streams: [],
mutex: false,
queueRequests: [],
remove: function(idInstance) {
this.mutex = false;
var stream = this.streams[idInstance];
if (!stream) {
return;
}
stream = stream.stream;
var options = stream.currentUserMediaRequestOptions;
if (this.queueRequests.indexOf(options)) {
delete this.queueRequests[this.queueRequests.indexOf(options)];
this.queueRequests = removeNullEntries(this.queueRequests);
}
this.streams[idInstance].stream = null;
delete this.streams[idInstance];
}
};
function getUserMediaHandler(options) {
if (currentUserMediaRequest.mutex === true) {
currentUserMediaRequest.queueRequests.push(options);
return;
}
currentUserMediaRequest.mutex = true;
// easy way to match
var idInstance = JSON.stringify(options.localMediaConstraints);
function streaming(stream, returnBack) {
setStreamType(options.localMediaConstraints, stream);
var streamEndedEvent = 'ended';
if ('oninactive' in stream) {
streamEndedEvent = 'inactive';
}
stream.addEventListener(streamEndedEvent, function() {
delete currentUserMediaRequest.streams[idInstance];
currentUserMediaRequest.mutex = false;
if (currentUserMediaRequest.queueRequests.indexOf(options)) {
delete currentUserMediaRequest.queueRequests[currentUserMediaRequest.queueRequests.indexOf(options)];
currentUserMediaRequest.queueRequests = removeNullEntries(currentUserMediaRequest.queueRequests);
}
}, false);
currentUserMediaRequest.streams[idInstance] = {
stream: stream
};
currentUserMediaRequest.mutex = false;
if (currentUserMediaRequest.queueRequests.length) {
getUserMediaHandler(currentUserMediaRequest.queueRequests.shift());
}
// callback
options.onGettingLocalMedia(stream, returnBack);
}
if (currentUserMediaRequest.streams[idInstance]) {
streaming(currentUserMediaRequest.streams[idInstance].stream, true);
} else {
var isBlackBerry = !!(/BB10|BlackBerry/i.test(navigator.userAgent || ''));
if (isBlackBerry || typeof navigator.mediaDevices === 'undefined' || typeof navigator.mediaDevices.getUserMedia !== 'function') {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
navigator.getUserMedia(options.localMediaConstraints, function(stream) {
stream.streamid = stream.streamid || stream.id || getRandomString();
stream.idInstance = idInstance;
streaming(stream);
}, function(error) {
options.onLocalMediaError(error, options.localMediaConstraints);
});
return;
}
if (typeof navigator.mediaDevices === 'undefined') {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var getUserMediaSuccess = function() {};
var getUserMediaFailure = function() {};
var getUserMediaStream, getUserMediaError;
navigator.mediaDevices = {
getUserMedia: function(hints) {
navigator.getUserMedia(hints, function(getUserMediaSuccess) {
getUserMediaSuccess(stream);
getUserMediaStream = stream;
}, function(error) {
getUserMediaFailure(error);
getUserMediaError = error;
});
return {
then: function(successCB) {
if (getUserMediaStream) {
successCB(getUserMediaStream);
return;
}
getUserMediaSuccess = successCB;
return {
then: function(failureCB) {
if (getUserMediaError) {
failureCB(getUserMediaError);
return;
}
getUserMediaFailure = failureCB;
}
}
}
}
}
};
}
if (options.localMediaConstraints.isScreen === true) {
if (navigator.mediaDevices.getDisplayMedia) {
navigator.mediaDevices.getDisplayMedia(options.localMediaConstraints).then(function(stream) {
stream.streamid = stream.streamid || stream.id || getRandomString();
stream.idInstance = idInstance;
streaming(stream);
}).catch(function(error) {
options.onLocalMediaError(error, options.localMediaConstraints);
});
} else if (navigator.getDisplayMedia) {
navigator.getDisplayMedia(options.localMediaConstraints).then(function(stream) {
stream.streamid = stream.streamid || stream.id || getRandomString();
stream.idInstance = idInstance;
streaming(stream);
}).catch(function(error) {
options.onLocalMediaError(error, options.localMediaConstraints);
});
} else {
throw new Error('getDisplayMedia API is not availabe in this browser.');
}
return;
}
navigator.mediaDevices.getUserMedia(options.localMediaConstraints).then(function(stream) {
stream.streamid = stream.streamid || stream.id || getRandomString();
stream.idInstance = idInstance;
streaming(stream);
}).catch(function(error) {
options.onLocalMediaError(error, options.localMediaConstraints);
});
}
}
// StreamsHandler.js
var StreamsHandler = (function() {
function handleType(type) {
if (!type) {
return;
}
if (typeof type === 'string' || typeof type === 'undefined') {
return type;
}
if (type.audio && type.video) {
return null;
}
if (type.audio) {
return 'audio';
}
if (type.video) {
return 'video';
}
return;
}
function setHandlers(stream, syncAction, connection) {
if (!stream || !stream.addEventListener) return;
if (typeof syncAction == 'undefined' || syncAction == true) {
var streamEndedEvent = 'ended';
if ('oninactive' in stream) {
streamEndedEvent = 'inactive';
}
stream.addEventListener(streamEndedEvent, function() {
StreamsHandler.onSyncNeeded(this.streamid, streamEndedEvent);
}, false);
}
stream.mute = function(type, isSyncAction) {
type = handleType(type);
if (typeof isSyncAction !== 'undefined') {
syncAction = isSyncAction;
}
if (typeof type == 'undefined' || type == 'audio') {
getTracks(stream, 'audio').forEach(function(track) {
track.enabled = false;
connection.streamEvents[stream.streamid].isAudioMuted = true;
});
}
if (typeof type == 'undefined' || type == 'video') {
getTracks(stream, 'video').forEach(function(track) {
track.enabled = false;
});
}
if (typeof syncAction == 'undefined' || syncAction == true) {
StreamsHandler.onSyncNeeded(stream.streamid, 'mute', type);
}
connection.streamEvents[stream.streamid].muteType = type || 'both';
fireEvent(stream, 'mute', type);
};
stream.unmute = function(type, isSyncAction) {
type = handleType(type);
if (typeof isSyncAction !== 'undefined') {
syncAction = isSyncAction;
}
graduallyIncreaseVolume();
if (typeof type == 'undefined' || type == 'audio') {
getTracks(stream, 'audio').forEach(function(track) {
track.enabled = true;
connection.streamEvents[stream.streamid].isAudioMuted = false;
});
}
if (typeof type == 'undefined' || type == 'video') {
getTracks(stream, 'video').forEach(function(track) {
track.enabled = true;
});
// make sure that video unmute doesn't affects audio
if (typeof type !== 'undefined' && type == 'video' && connection.streamEvents[stream.streamid].isAudioMuted) {
(function looper(times) {
if (!times) {
times = 0;
}
times++;
// check until five-seconds
if (times < 100 && connection.streamEvents[stream.streamid].isAudioMuted) {
stream.mute('audio');
setTimeout(function() {
looper(times);
}, 50);
}
})();
}
}
if (typeof syncAction == 'undefined' || syncAction == true) {
StreamsHandler.onSyncNeeded(stream.streamid, 'unmute', type);
}
connection.streamEvents[stream.streamid].unmuteType = type || 'both';
fireEvent(stream, 'unmute', type);
};
function graduallyIncreaseVolume() {
if (!connection.streamEvents[stream.streamid].mediaElement) {
return;
}
var mediaElement = connection.streamEvents[stream.streamid].mediaElement;
mediaElement.volume = 0;
afterEach(200, 5, function() {
try {
mediaElement.volume += .20;
} catch (e) {
mediaElement.volume = 1;
}
});
}
}
function afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes) {
startedTimes = (startedTimes || 0) + 1;
if (startedTimes >= numberOfTimes) return;
setTimeout(function() {
callback();
afterEach(setTimeoutInteval, numberOfTimes, callback, startedTimes);
}, setTimeoutInteval);
}
return {
setHandlers: setHandlers,
onSyncNeeded: function(streamid, action, type) {}
};
})();
// TextReceiver.js & TextSender.js
function TextReceiver(connection) {
var content = {};
function receive(data, userid, extra) {
// uuid is used to uniquely identify sending instance
var uuid = data.uuid;
if (!content[uuid]) {
content[uuid] = [];
}
content[uuid].push(data.message);
if (data.last) {
var message = content[uuid].join('');
if (data.isobject) {
message = JSON.parse(message);
}
// latency detection
var receivingTime = new Date().getTime();
var latency = receivingTime - data.sendingTime;
var e = {
data: message,
userid: userid,
extra: extra,
latency: latency
};
if (connection.autoTranslateText) {
e.original = e.data;
connection.Translator.TranslateText(e.data, function(translatedText) {
e.data = translatedText;
connection.onmessage(e);
});
} else {
connection.onmessage(e);
}
delete content[uuid];
}
}
return {
receive: receive
};
}
// TextSender.js
var TextSender = {
send: function(config) {
var connection = config.connection;
var channel = config.channel,
remoteUserId = config.remoteUserId,
initialText = config.text,
packetSize = connection.chunkSize || 1000,
textToTransfer = '',
isobject = false;
if (!isString(initialText)) {
isobject = true;
initialText = JSON.stringify(initialText);
}
// uuid is used to uniquely identify sending instance
var uuid = getRandomString();
var sendingTime = new Date().getTime();
sendText(initialText);
function sendText(textMessage, text) {
var data = {
type: 'text',
uuid: uuid,
sendingTime: sendingTime
};
if (textMessage) {
text = textMessage;
data.packets = parseInt(text.length / packetSize);
}
if (text.length > packetSize) {
data.message = text.slice(0, packetSize);
} else {
data.message = text;
data.last = true;
data.isobject = isobject;
}
channel.send(data, remoteUserId);
textToTransfer = text.slice(data.message.length);
if (textToTransfer.length) {
setTimeout(function() {
sendText(null, textToTransfer);
}, connection.chunkInterval || 100);
}
}
}
};
// FileProgressBarHandler.js
var FileProgressBarHandler = (function() {
function handle(connection) {
var progressHelper = {};
connection.onFileStart = function(file) {
var div = document.createElement('div');
div.title = file.name;
div.innerHTML = ' ';
if (file.remoteUserId) {
div.innerHTML += ' (Sharing with:' + file.remoteUserId + ')';
}
if (!connection.filesContainer) {
connection.filesContainer = document.body || document.documentElement;
}
connection.filesContainer.insertBefore(div, connection.filesContainer.firstChild);
if (!file.remoteUserId) {
progressHelper[file.uuid] = {
div: div,
progress: div.querySelector('progress'),
label: div.querySelector('label')
};
progressHelper[file.uuid].progress.max = file.maxChunks;
return;
}
if (!progressHelper[file.uuid]) {
progressHelper[file.uuid] = {};
}
progressHelper[file.uuid][file.remoteUserId] = {
div: div,
progress: div.querySelector('progress'),
label: div.querySelector('label')
};
progressHelper[file.uuid][file.remoteUserId].progress.max = file.maxChunks;
};
connection.onFileProgress = function(chunk) {
var helper = progressHelper[chunk.uuid];
if (!helper) {
return;
}
if (chunk.remoteUserId) {
helper = progressHelper[chunk.uuid][chunk.remoteUserId];
if (!helper) {
return;
}
}
helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max;
updateLabel(helper.progress, helper.label);
};
connection.onFileEnd = function(file) {
var helper = progressHelper[file.uuid];
if (!helper) {
console.error('No such progress-helper element exist.', file);
return;
}
if (file.remoteUserId) {
helper = progressHelper[file.uuid][file.remoteUserId];
if (!helper) {
return;
}
}
var div = helper.div;
if (file.type.indexOf('image') != -1) {
div.innerHTML = 'Download ' + file.name + '
';
} else {
div.innerHTML = 'Download ' + file.name + '
';
}
};
function updateLabel(progress, label) {
if (progress.position === -1) {
return;
}
var position = +progress.position.toFixed(2).split('.')[1] || 100;
label.innerHTML = position + '%';
}
}
return {
handle: handle
};
})();
// TranslationHandler.js
var TranslationHandler = (function() {
function handle(connection) {
connection.autoTranslateText = false;
connection.language = 'en';
connection.googKey = 'AIzaSyCgB5hmFY74WYB-EoWkhr9cAGr6TiTHrEE';
connection.Translator = {
TranslateText: function(text, callback) {
// if(location.protocol === 'https:') return callback(text);
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
var sourceText = encodeURIComponent(text); // escape
var randomNumber = 'method' + connection.token();
window[randomNumber] = function(response) {
if (response.data && response.data.translations[0] && callback) {
callback(response.data.translations[0].translatedText);
return;
}
if (response.error && response.error.message === 'Daily Limit Exceeded') {
console.error('Text translation failed. Error message: "Daily Limit Exceeded."');
return;
}
if (response.error) {
console.error(response.error.message);
return;
}
console.error(response);
};
var source = 'https://www.googleapis.com/language/translate/v2?key=' + connection.googKey + '&target=' + (connection.language || 'en-US') + '&callback=window.' + randomNumber + '&q=' + sourceText;
newScript.src = source;
document.getElementsByTagName('head')[0].appendChild(newScript);
},
getListOfLanguages: function(callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var response = JSON.parse(xhr.responseText);
if (response && response.data && response.data.languages) {
callback(response.data.languages);
return;
}
if (response.error && response.error.message === 'Daily Limit Exceeded') {
console.error('Text translation failed. Error message: "Daily Limit Exceeded."');
return;
}
if (response.error) {
console.error(response.error.message);
return;
}
console.error(response);
}
}
var url = 'https://www.googleapis.com/language/translate/v2/languages?key=' + connection.googKey + '&target=en';
xhr.open('GET', url, true);
xhr.send(null);
}
};
}
return {
handle: handle
};
})();
// _____________________
// RTCMultiConnection.js
(function(connection) {
forceOptions = forceOptions || {
useDefaultDevices: true
};
connection.channel = connection.sessionid = roomid;
var mPeer = new MultiPeers(connection);
var preventDuplicateOnStreamEvents = {};
mPeer.onGettingLocalMedia = function(stream, callback) {
callback = callback || function() {};
if (preventDuplicateOnStreamEvents[stream.streamid]) {
callback();
return;
}
preventDuplicateOnStreamEvents[stream.streamid] = true;
try {
stream.type = 'local';
} catch (e) {}
connection.setStreamEndHandler(stream);
getRMCMediaElement(stream, function(mediaElement) {
mediaElement.id = stream.streamid;
mediaElement.muted = true;
mediaElement.volume = 0;
if (connection.attachStreams.indexOf(stream) === -1) {
connection.attachStreams.push(stream);
}
if (typeof StreamsHandler !== 'undefined') {
StreamsHandler.setHandlers(stream, true, connection);
}
connection.streamEvents[stream.streamid] = {
stream: stream,
type: 'local',
mediaElement: mediaElement,
userid: connection.userid,
extra: connection.extra,
streamid: stream.streamid,
isAudioMuted: true
};
try {
setHarkEvents(connection, connection.streamEvents[stream.streamid]);
setMuteHandlers(connection, connection.streamEvents[stream.streamid]);
connection.onstream(connection.streamEvents[stream.streamid]);
} catch (e) {
//
}
callback();
}, connection);
};
mPeer.onGettingRemoteMedia = function(stream, remoteUserId) {
try {
stream.type = 'remote';
} catch (e) {}
connection.setStreamEndHandler(stream, 'remote-stream');
getRMCMediaElement(stream, function(mediaElement) {
mediaElement.id = stream.streamid;
if (typeof StreamsHandler !== 'undefined') {
StreamsHandler.setHandlers(stream, false, connection);
}
connection.streamEvents[stream.streamid] = {
stream: stream,
type: 'remote',
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {},
mediaElement: mediaElement,
streamid: stream.streamid
};
setMuteHandlers(connection, connection.streamEvents[stream.streamid]);
connection.onstream(connection.streamEvents[stream.streamid]);
}, connection);
};
mPeer.onRemovingRemoteMedia = function(stream, remoteUserId) {
var streamEvent = connection.streamEvents[stream.streamid];
if (!streamEvent) {
streamEvent = {
stream: stream,
type: 'remote',
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {},
streamid: stream.streamid,
mediaElement: connection.streamEvents[stream.streamid] ? connection.streamEvents[stream.streamid].mediaElement : null
};
}
if (connection.peersBackup[streamEvent.userid]) {
streamEvent.extra = connection.peersBackup[streamEvent.userid].extra;
}
connection.onstreamended(streamEvent);
delete connection.streamEvents[stream.streamid];
};
mPeer.onNegotiationNeeded = function(message, remoteUserId, callback) {
callback = callback || function() {};
remoteUserId = remoteUserId || message.remoteUserId;
message = message || '';
// usually a message looks like this
var messageToDeliver = {
remoteUserId: remoteUserId,
message: message,
sender: connection.userid
};
if (message.remoteUserId && message.message && message.sender) {
// if a code is manually passing required data
messageToDeliver = message;
}
connectSocket(function() {
connection.socket.emit(connection.socketMessageEvent, messageToDeliver, callback);
});
};
function onUserLeft(remoteUserId) {
connection.deletePeer(remoteUserId);
}
mPeer.onUserLeft = onUserLeft;
mPeer.disconnectWith = function(remoteUserId, callback) {
if (connection.socket) {
connection.socket.emit('disconnect-with', remoteUserId, callback || function() {});
}
connection.deletePeer(remoteUserId);
};
connection.socketOptions = {
// 'force new connection': true, // For SocketIO version < 1.0
// 'forceNew': true, // For SocketIO version >= 1.0
'transport': 'polling' // fixing transport:unknown issues
};
function connectSocket(connectCallback) {
connection.socketAutoReConnect = true;
if (connection.socket) { // todo: check here readySate/etc. to make sure socket is still opened
if (connectCallback) {
connectCallback(connection.socket);
}
return;
}
if (typeof SocketConnection === 'undefined') {
if (typeof FirebaseConnection !== 'undefined') {
window.SocketConnection = FirebaseConnection;
} else if (typeof PubNubConnection !== 'undefined') {
window.SocketConnection = PubNubConnection;
} else {
throw 'SocketConnection.js seems missed.';
}
}
new SocketConnection(connection, function(s) {
if (connectCallback) {
connectCallback(connection.socket);
}
});
}
// 1st paramter is roomid
// 2rd paramter is a callback function
connection.openOrJoin = function(roomid, callback) {
callback = callback || function() {};
connection.checkPresence(roomid, function(isRoomExist, roomid) {
if (isRoomExist) {
connection.sessionid = roomid;
var localPeerSdpConstraints = false;
var remotePeerSdpConstraints = false;
var isOneWay = !!connection.session.oneway;
var isDataOnly = isData(connection.session);
remotePeerSdpConstraints = {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
}
localPeerSdpConstraints = {
OfferToReceiveAudio: isOneWay ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: isOneWay ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
}
var connectionDescription = {
remoteUserId: connection.sessionid,
message: {
newParticipationRequest: true,
isOneWay: isOneWay,
isDataOnly: isDataOnly,
localPeerSdpConstraints: localPeerSdpConstraints,
remotePeerSdpConstraints: remotePeerSdpConstraints
},
sender: connection.userid
};
beforeJoin(connectionDescription.message, function() {
joinRoom(connectionDescription, callback);
});
return;
}
connection.waitingForLocalMedia = true;
connection.isInitiator = true;
connection.sessionid = roomid || connection.sessionid;
if (isData(connection.session)) {
openRoom(callback);
return;
}
connection.captureUserMedia(function() {
openRoom(callback);
});
});
};
// don't allow someone to join this person until he has the media
connection.waitingForLocalMedia = false;
connection.open = function(roomid, callback) {
callback = callback || function() {};
connection.waitingForLocalMedia = true;
connection.isInitiator = true;
connection.sessionid = roomid || connection.sessionid;
connectSocket(function() {
if (isData(connection.session)) {
openRoom(callback);
return;
}
connection.captureUserMedia(function() {
openRoom(callback);
});
});
};
// this object keeps extra-data records for all connected users
// this object is never cleared so you can always access extra-data even if a user left
connection.peersBackup = {};
connection.deletePeer = function(remoteUserId) {
if (!remoteUserId || !connection.peers[remoteUserId]) {
return;
}
var eventObject = {
userid: remoteUserId,
extra: connection.peers[remoteUserId] ? connection.peers[remoteUserId].extra : {}
};
if (connection.peersBackup[eventObject.userid]) {
eventObject.extra = connection.peersBackup[eventObject.userid].extra;
}
connection.onleave(eventObject);
if (!!connection.peers[remoteUserId]) {
connection.peers[remoteUserId].streams.forEach(function(stream) {
stream.stop();
});
var peer = connection.peers[remoteUserId].peer;
if (peer && peer.iceConnectionState !== 'closed') {
try {
// peer.close();
} catch (e) {}
}
if (connection.peers[remoteUserId]) {
connection.peers[remoteUserId].peer = null;
delete connection.peers[remoteUserId];
}
}
}
connection.rejoin = function(connectionDescription) {
if (connection.isInitiator || !connectionDescription || !Object.keys(connectionDescription).length) {
return;
}
var extra = {};
if (connection.peers[connectionDescription.remoteUserId]) {
extra = connection.peers[connectionDescription.remoteUserId].extra;
connection.deletePeer(connectionDescription.remoteUserId);
}
if (connectionDescription && connectionDescription.remoteUserId) {
connection.join(connectionDescription.remoteUserId);
connection.onReConnecting({
userid: connectionDescription.remoteUserId,
extra: extra
});
}
};
connection.join = function(remoteUserId, options) {
connection.sessionid = (remoteUserId ? remoteUserId.sessionid || remoteUserId.remoteUserId || remoteUserId : false) || connection.sessionid;
connection.sessionid += '';
var localPeerSdpConstraints = false;
var remotePeerSdpConstraints = false;
var isOneWay = false;
var isDataOnly = false;
if ((remoteUserId && remoteUserId.session) || !remoteUserId || typeof remoteUserId === 'string') {
var session = remoteUserId ? remoteUserId.session || connection.session : connection.session;
isOneWay = !!session.oneway;
isDataOnly = isData(session);
remotePeerSdpConstraints = {
OfferToReceiveAudio: connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: connection.sdpConstraints.mandatory.OfferToReceiveVideo
};
localPeerSdpConstraints = {
OfferToReceiveAudio: isOneWay ? !!connection.session.audio : connection.sdpConstraints.mandatory.OfferToReceiveAudio,
OfferToReceiveVideo: isOneWay ? !!connection.session.video || !!connection.session.screen : connection.sdpConstraints.mandatory.OfferToReceiveVideo
};
}
options = options || {};
var cb = function() {};
if (typeof options === 'function') {
cb = options;
options = {};
}
if (typeof options.localPeerSdpConstraints !== 'undefined') {
localPeerSdpConstraints = options.localPeerSdpConstraints;
}
if (typeof options.remotePeerSdpConstraints !== 'undefined') {
remotePeerSdpConstraints = options.remotePeerSdpConstraints;
}
if (typeof options.isOneWay !== 'undefined') {
isOneWay = options.isOneWay;
}
if (typeof options.isDataOnly !== 'undefined') {
isDataOnly = options.isDataOnly;
}
var connectionDescription = {
remoteUserId: connection.sessionid,
message: {
newParticipationRequest: true,
isOneWay: isOneWay,
isDataOnly: isDataOnly,
localPeerSdpConstraints: localPeerSdpConstraints,
remotePeerSdpConstraints: remotePeerSdpConstraints
},
sender: connection.userid
};
beforeJoin(connectionDescription.message, function() {
connectSocket(function() {
joinRoom(connectionDescription, cb);
});
});
return connectionDescription;
};
function joinRoom(connectionDescription, cb) {
connection.socket.emit('join-room', {
sessionid: connection.sessionid,
session: connection.session,
mediaConstraints: connection.mediaConstraints,
sdpConstraints: connection.sdpConstraints,
streams: getStreamInfoForAdmin(),
extra: connection.extra,
password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : ''
}, function(isRoomJoined, error) {
if (isRoomJoined === true) {
if (connection.enableLogs) {
console.log('isRoomJoined: ', isRoomJoined, ' roomid: ', connection.sessionid);
}
if (!!connection.peers[connection.sessionid]) {
// on socket disconnect & reconnect
return;
}
mPeer.onNegotiationNeeded(connectionDescription);
}
if (isRoomJoined === false) {
if (connection.enableLogs) {
console.warn('isRoomJoined: ', error, ' roomid: ', connection.sessionid);
}
// [disabled] retry after 3 seconds
false && setTimeout(function() {
joinRoom(connectionDescription, cb);
}, 3000);
}
cb(isRoomJoined, connection.sessionid, error);
});
}
connection.publicRoomIdentifier = '';
function openRoom(callback) {
if (connection.enableLogs) {
console.log('Sending open-room signal to socket.io');
}
connection.waitingForLocalMedia = false;
connection.socket.emit('open-room', {
sessionid: connection.sessionid,
session: connection.session,
mediaConstraints: connection.mediaConstraints,
sdpConstraints: connection.sdpConstraints,
streams: getStreamInfoForAdmin(),
extra: connection.extra,
identifier: connection.publicRoomIdentifier,
password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : ''
}, function(isRoomOpened, error) {
if (isRoomOpened === true) {
if (connection.enableLogs) {
console.log('isRoomOpened: ', isRoomOpened, ' roomid: ', connection.sessionid);
}
callback(isRoomOpened, connection.sessionid);
}
if (isRoomOpened === false) {
if (connection.enableLogs) {
console.warn('isRoomOpened: ', error, ' roomid: ', connection.sessionid);
}
callback(isRoomOpened, connection.sessionid, error);
}
});
}
function getStreamInfoForAdmin() {
try {
return connection.streamEvents.selectAll('local').map(function(event) {
return {
streamid: event.streamid,
tracks: event.stream.getTracks().length
};
});
} catch (e) {
return [];
}
}
function beforeJoin(userPreferences, callback) {
if (connection.dontCaptureUserMedia || userPreferences.isDataOnly) {
callback();
return;
}
var localMediaConstraints = {};
if (userPreferences.localPeerSdpConstraints.OfferToReceiveAudio) {
localMediaConstraints.audio = connection.mediaConstraints.audio;
}
if (userPreferences.localPeerSdpConstraints.OfferToReceiveVideo) {
localMediaConstraints.video = connection.mediaConstraints.video;
}
var session = userPreferences.session || connection.session;
if (session.oneway && session.audio !== 'two-way' && session.video !== 'two-way' && session.screen !== 'two-way') {
callback();
return;
}
if (session.oneway && session.audio && session.audio === 'two-way') {
session = {
audio: true
};
}
if (session.audio || session.video || session.screen) {
if (session.screen) {
if (DetectRTC.browser.name === 'Edge') {
navigator.getDisplayMedia({
video: true,
audio: isAudioPlusTab(connection)
}).then(function(screen) {
screen.isScreen = true;
mPeer.onGettingLocalMedia(screen);
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
connection.invokeGetUserMedia(null, callback);
} else {
callback(screen);
}
}, function(error) {
console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.');
});
} else {
connection.invokeGetUserMedia({
audio: isAudioPlusTab(connection),
video: true,
isScreen: true
}, (session.audio || session.video) && !isAudioPlusTab(connection) ? connection.invokeGetUserMedia(null, callback) : callback);
}
} else if (session.audio || session.video) {
connection.invokeGetUserMedia(null, callback, session);
}
}
}
connection.getUserMedia = connection.captureUserMedia = function(callback, sessionForced) {
callback = callback || function() {};
var session = sessionForced || connection.session;
if (connection.dontCaptureUserMedia || isData(session)) {
callback();
return;
}
if (session.audio || session.video || session.screen) {
if (session.screen) {
if (DetectRTC.browser.name === 'Edge') {
navigator.getDisplayMedia({
video: true,
audio: isAudioPlusTab(connection)
}).then(function(screen) {
screen.isScreen = true;
mPeer.onGettingLocalMedia(screen);
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
var nonScreenSession = {};
for (var s in session) {
if (s !== 'screen') {
nonScreenSession[s] = session[s];
}
}
connection.invokeGetUserMedia(sessionForced, callback, nonScreenSession);
return;
}
callback(screen);
}, function(error) {
console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.');
});
} else {
connection.invokeGetUserMedia({
audio: isAudioPlusTab(connection),
video: true,
isScreen: true
}, function(stream) {
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
var nonScreenSession = {};
for (var s in session) {
if (s !== 'screen') {
nonScreenSession[s] = session[s];
}
}
connection.invokeGetUserMedia(sessionForced, callback, nonScreenSession);
return;
}
callback(stream);
});
}
} else if (session.audio || session.video) {
connection.invokeGetUserMedia(sessionForced, callback, session);
}
}
};
connection.onbeforeunload = function(arg1, dontCloseSocket) {
if (!connection.closeBeforeUnload) {
return;
}
connection.peers.getAllParticipants().forEach(function(participant) {
mPeer.onNegotiationNeeded({
userLeft: true
}, participant);
if (connection.peers[participant] && connection.peers[participant].peer) {
connection.peers[participant].peer.close();
}
delete connection.peers[participant];
});
if (!dontCloseSocket) {
// connection.closeSocket();
}
connection.isInitiator = false;
};
if (!window.ignoreBeforeUnload) {
// user can implement its own version of window.onbeforeunload
connection.closeBeforeUnload = true;
window.addEventListener('beforeunload', connection.onbeforeunload, false);
} else {
connection.closeBeforeUnload = false;
}
connection.userid = getRandomString();
connection.changeUserId = function(newUserId, callback) {
callback = callback || function() {};
// connection.userid = newUserId;
connection.socket.emit('changed-uuid', connection.userid, callback);
};
connection.extra = {};
connection.attachStreams = [];
connection.session = {
audio: true,
video: true
};
connection.enableFileSharing = false;
// all values in kbps
connection.bandwidth = {
screen: false,
audio: false,
video: false
};
connection.codecs = {
audio: 'opus',
video: 'VP9'
};
connection.processSdp = function(sdp) {
// ignore SDP modification if unified-pan is supported
if (isUnifiedPlanSupportedDefault()) {
return sdp;
}
if (DetectRTC.browser.name === 'Safari') {
return sdp;
}
if (connection.codecs.video.toUpperCase() === 'VP8') {
sdp = CodecsHandler.preferCodec(sdp, 'vp8');
}
if (connection.codecs.video.toUpperCase() === 'VP9') {
sdp = CodecsHandler.preferCodec(sdp, 'vp9');
}
if (connection.codecs.video.toUpperCase() === 'H264') {
sdp = CodecsHandler.preferCodec(sdp, 'h264');
}
if (connection.codecs.audio === 'G722') {
sdp = CodecsHandler.removeNonG722(sdp);
}
if (DetectRTC.browser.name === 'Firefox') {
return sdp;
}
if (connection.bandwidth.video || connection.bandwidth.screen) {
sdp = CodecsHandler.setApplicationSpecificBandwidth(sdp, connection.bandwidth, !!connection.session.screen);
}
if (connection.bandwidth.video) {
sdp = CodecsHandler.setVideoBitrates(sdp, {
min: connection.bandwidth.video * 8 * 1024,
max: connection.bandwidth.video * 8 * 1024
});
}
if (connection.bandwidth.audio) {
sdp = CodecsHandler.setOpusAttributes(sdp, {
maxaveragebitrate: connection.bandwidth.audio * 8 * 1024,
maxplaybackrate: connection.bandwidth.audio * 8 * 1024,
stereo: 1,
// maxptime: 3
});
}
return sdp;
};
if (typeof CodecsHandler !== 'undefined') {
connection.BandwidthHandler = connection.CodecsHandler = CodecsHandler;
}
connection.mediaConstraints = {
audio: {
mandatory: {},
optional: connection.bandwidth.audio ? [{
bandwidth: connection.bandwidth.audio * 8 * 1024 || 128 * 8 * 1024
}] : []
},
video: {
mandatory: {},
optional: connection.bandwidth.video ? [{
bandwidth: connection.bandwidth.video * 8 * 1024 || 128 * 8 * 1024
}, {
facingMode: 'user'
}] : [{
facingMode: 'user'
}]
}
};
if (DetectRTC.browser.name === 'Firefox') {
connection.mediaConstraints = {
audio: true,
video: true
};
}
if (!forceOptions.useDefaultDevices && !DetectRTC.isMobileDevice) {
DetectRTC.load(function() {
var lastAudioDevice, lastVideoDevice;
// it will force RTCMultiConnection to capture last-devices
// i.e. if external microphone is attached to system, we should prefer it over built-in devices.
DetectRTC.MediaDevices.forEach(function(device) {
if (device.kind === 'audioinput' && connection.mediaConstraints.audio !== false) {
lastAudioDevice = device;
}
if (device.kind === 'videoinput' && connection.mediaConstraints.video !== false) {
lastVideoDevice = device;
}
});
if (lastAudioDevice) {
if (DetectRTC.browser.name === 'Firefox') {
if (connection.mediaConstraints.audio !== true) {
connection.mediaConstraints.audio.deviceId = lastAudioDevice.id;
} else {
connection.mediaConstraints.audio = {
deviceId: lastAudioDevice.id
}
}
return;
}
if (connection.mediaConstraints.audio == true) {
connection.mediaConstraints.audio = {
mandatory: {},
optional: []
}
}
if (!connection.mediaConstraints.audio.optional) {
connection.mediaConstraints.audio.optional = [];
}
var optional = [{
sourceId: lastAudioDevice.id
}];
connection.mediaConstraints.audio.optional = optional.concat(connection.mediaConstraints.audio.optional);
}
if (lastVideoDevice) {
if (DetectRTC.browser.name === 'Firefox') {
if (connection.mediaConstraints.video !== true) {
connection.mediaConstraints.video.deviceId = lastVideoDevice.id;
} else {
connection.mediaConstraints.video = {
deviceId: lastVideoDevice.id
}
}
return;
}
if (connection.mediaConstraints.video == true) {
connection.mediaConstraints.video = {
mandatory: {},
optional: []
}
}
if (!connection.mediaConstraints.video.optional) {
connection.mediaConstraints.video.optional = [];
}
var optional = [{
sourceId: lastVideoDevice.id
}];
connection.mediaConstraints.video.optional = optional.concat(connection.mediaConstraints.video.optional);
}
});
}
connection.sdpConstraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
},
optional: [{
VoiceActivityDetection: false
}]
};
connection.sdpSemantics = null; // "unified-plan" or "plan-b", ref: webrtc.org/web-apis/chrome/unified-plan/
connection.iceCandidatePoolSize = null; // 0
connection.bundlePolicy = null; // max-bundle
connection.rtcpMuxPolicy = null; // "require" or "negotiate"
connection.iceTransportPolicy = null; // "relay" or "all"
connection.optionalArgument = {
optional: [{
DtlsSrtpKeyAgreement: true
}, {
googImprovedWifiBwe: true
}, {
googScreencastMinBitrate: 300
}, {
googIPv6: true
}, {
googDscp: true
}, {
googCpuUnderuseThreshold: 55
}, {
googCpuOveruseThreshold: 85
}, {
googSuspendBelowMinBitrate: true
}, {
googCpuOveruseDetection: true
}],
mandatory: {}
};
connection.iceServers = IceServersHandler.getIceServers(connection);
connection.candidates = {
host: true,
stun: true,
turn: true
};
connection.iceProtocols = {
tcp: true,
udp: true
};
// EVENTs
connection.onopen = function(event) {
if (!!connection.enableLogs) {
console.info('Data connection has been opened between you & ', event.userid);
}
};
connection.onclose = function(event) {
if (!!connection.enableLogs) {
console.warn('Data connection has been closed between you & ', event.userid);
}
};
connection.onerror = function(error) {
if (!!connection.enableLogs) {
console.error(error.userid, 'data-error', error);
}
};
connection.onmessage = function(event) {
if (!!connection.enableLogs) {
console.debug('data-message', event.userid, event.data);
}
};
connection.send = function(data, remoteUserId) {
connection.peers.send(data, remoteUserId);
};
connection.close = connection.disconnect = connection.leave = function() {
connection.onbeforeunload(false, true);
};
connection.closeEntireSession = function(callback) {
callback = callback || function() {};
connection.socket.emit('close-entire-session', function looper() {
if (connection.getAllParticipants().length) {
setTimeout(looper, 100);
return;
}
connection.onEntireSessionClosed({
sessionid: connection.sessionid,
userid: connection.userid,
extra: connection.extra
});
connection.changeUserId(null, function() {
connection.close();
callback();
});
});
};
connection.onEntireSessionClosed = function(event) {
if (!connection.enableLogs) return;
console.info('Entire session is closed: ', event.sessionid, event.extra);
};
connection.onstream = function(e) {
var parentNode = connection.videosContainer;
parentNode.insertBefore(e.mediaElement, parentNode.firstChild);
var played = e.mediaElement.play();
if (typeof played !== 'undefined') {
played.catch(function() {
/*** iOS 11 doesn't allow automatic play and rejects ***/
}).then(function() {
setTimeout(function() {
e.mediaElement.play();
}, 2000);
});
return;
}
setTimeout(function() {
e.mediaElement.play();
}, 2000);
};
connection.onstreamended = function(e) {
if (!e.mediaElement) {
e.mediaElement = document.getElementById(e.streamid);
}
if (!e.mediaElement || !e.mediaElement.parentNode) {
return;
}
e.mediaElement.parentNode.removeChild(e.mediaElement);
};
connection.direction = 'many-to-many';
connection.removeStream = function(streamid, remoteUserId) {
var stream;
connection.attachStreams.forEach(function(localStream) {
if (localStream.id === streamid) {
stream = localStream;
}
});
if (!stream) {
console.warn('No such stream exist.', streamid);
return;
}
connection.peers.getAllParticipants().forEach(function(participant) {
if (remoteUserId && participant !== remoteUserId) {
return;
}
var user = connection.peers[participant];
try {
user.peer.removeStream(stream);
} catch (e) {}
});
connection.renegotiate();
};
connection.addStream = function(session, remoteUserId) {
if (!!session.getTracks) {
if (connection.attachStreams.indexOf(session) === -1) {
if (!session.streamid) {
session.streamid = session.id;
}
connection.attachStreams.push(session);
}
connection.renegotiate(remoteUserId);
return;
}
if (isData(session)) {
connection.renegotiate(remoteUserId);
return;
}
if (session.audio || session.video || session.screen) {
if (session.screen) {
if (DetectRTC.browser.name === 'Edge') {
navigator.getDisplayMedia({
video: true,
audio: isAudioPlusTab(connection)
}).then(function(screen) {
screen.isScreen = true;
mPeer.onGettingLocalMedia(screen);
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
connection.invokeGetUserMedia(null, function(stream) {
gumCallback(stream);
});
} else {
gumCallback(screen);
}
}, function(error) {
console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.');
});
} else {
connection.invokeGetUserMedia({
audio: isAudioPlusTab(connection),
video: true,
isScreen: true
}, function(stream) {
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
connection.invokeGetUserMedia(null, function(stream) {
gumCallback(stream);
});
} else {
gumCallback(stream);
}
});
}
} else if (session.audio || session.video) {
connection.invokeGetUserMedia(null, gumCallback);
}
}
function gumCallback(stream) {
if (session.streamCallback) {
session.streamCallback(stream);
}
connection.renegotiate(remoteUserId);
}
};
connection.invokeGetUserMedia = function(localMediaConstraints, callback, session) {
if (!session) {
session = connection.session;
}
if (!localMediaConstraints) {
localMediaConstraints = connection.mediaConstraints;
}
getUserMediaHandler({
onGettingLocalMedia: function(stream) {
var videoConstraints = localMediaConstraints.video;
if (videoConstraints) {
if (videoConstraints.mediaSource || videoConstraints.mozMediaSource) {
stream.isScreen = true;
} else if (videoConstraints.mandatory && videoConstraints.mandatory.chromeMediaSource) {
stream.isScreen = true;
}
}
if (!stream.isScreen) {
stream.isVideo = !!getTracks(stream, 'video').length;
stream.isAudio = !stream.isVideo && getTracks(stream, 'audio').length;
}
mPeer.onGettingLocalMedia(stream, function() {
if (typeof callback === 'function') {
callback(stream);
}
});
},
onLocalMediaError: function(error, constraints) {
mPeer.onLocalMediaError(error, constraints);
},
localMediaConstraints: localMediaConstraints || {
audio: session.audio ? localMediaConstraints.audio : false,
video: session.video ? localMediaConstraints.video : false
}
});
};
function applyConstraints(stream, mediaConstraints) {
if (!stream) {
if (!!connection.enableLogs) {
console.error('No stream to applyConstraints.');
}
return;
}
if (mediaConstraints.audio) {
getTracks(stream, 'audio').forEach(function(track) {
track.applyConstraints(mediaConstraints.audio);
});
}
if (mediaConstraints.video) {
getTracks(stream, 'video').forEach(function(track) {
track.applyConstraints(mediaConstraints.video);
});
}
}
connection.applyConstraints = function(mediaConstraints, streamid) {
if (!MediaStreamTrack || !MediaStreamTrack.prototype.applyConstraints) {
alert('track.applyConstraints is NOT supported in your browser.');
return;
}
if (streamid) {
var stream;
if (connection.streamEvents[streamid]) {
stream = connection.streamEvents[streamid].stream;
}
applyConstraints(stream, mediaConstraints);
return;
}
connection.attachStreams.forEach(function(stream) {
applyConstraints(stream, mediaConstraints);
});
};
function replaceTrack(track, remoteUserId, isVideoTrack) {
if (remoteUserId) {
mPeer.replaceTrack(track, remoteUserId, isVideoTrack);
return;
}
connection.peers.getAllParticipants().forEach(function(participant) {
mPeer.replaceTrack(track, participant, isVideoTrack);
});
}
connection.replaceTrack = function(session, remoteUserId, isVideoTrack) {
session = session || {};
if (!RTCPeerConnection.prototype.getSenders) {
connection.addStream(session);
return;
}
if (session instanceof MediaStreamTrack) {
replaceTrack(session, remoteUserId, isVideoTrack);
return;
}
if (session instanceof MediaStream) {
if (getTracks(session, 'video').length) {
replaceTrack(getTracks(session, 'video')[0], remoteUserId, true);
}
if (getTracks(session, 'audio').length) {
replaceTrack(getTracks(session, 'audio')[0], remoteUserId, false);
}
return;
}
if (isData(session)) {
throw 'connection.replaceTrack requires audio and/or video and/or screen.';
return;
}
if (session.audio || session.video || session.screen) {
if (session.screen) {
if (DetectRTC.browser.name === 'Edge') {
navigator.getDisplayMedia({
video: true,
audio: isAudioPlusTab(connection)
}).then(function(screen) {
screen.isScreen = true;
mPeer.onGettingLocalMedia(screen);
if ((session.audio || session.video) && !isAudioPlusTab(connection)) {
connection.invokeGetUserMedia(null, gumCallback);
} else {
gumCallback(screen);
}
}, function(error) {
console.error('Unable to capture screen on Edge. HTTPs and version 17+ is required.');
});
} else {
connection.invokeGetUserMedia({
audio: isAudioPlusTab(connection),
video: true,
isScreen: true
}, (session.audio || session.video) && !isAudioPlusTab(connection) ? connection.invokeGetUserMedia(null, gumCallback) : gumCallback);
}
} else if (session.audio || session.video) {
connection.invokeGetUserMedia(null, gumCallback);
}
}
function gumCallback(stream) {
connection.replaceTrack(stream, remoteUserId, isVideoTrack || session.video || session.screen);
}
};
connection.resetTrack = function(remoteUsersIds, isVideoTrack) {
if (!remoteUsersIds) {
remoteUsersIds = connection.getAllParticipants();
}
if (typeof remoteUsersIds == 'string') {
remoteUsersIds = [remoteUsersIds];
}
remoteUsersIds.forEach(function(participant) {
var peer = connection.peers[participant].peer;
if ((typeof isVideoTrack === 'undefined' || isVideoTrack === true) && peer.lastVideoTrack) {
connection.replaceTrack(peer.lastVideoTrack, participant, true);
}
if ((typeof isVideoTrack === 'undefined' || isVideoTrack === false) && peer.lastAudioTrack) {
connection.replaceTrack(peer.lastAudioTrack, participant, false);
}
});
};
connection.renegotiate = function(remoteUserId) {
if (remoteUserId) {
mPeer.renegotiatePeer(remoteUserId);
return;
}
connection.peers.getAllParticipants().forEach(function(participant) {
mPeer.renegotiatePeer(participant);
});
};
connection.setStreamEndHandler = function(stream, isRemote) {
if (!stream || !stream.addEventListener) return;
isRemote = !!isRemote;
if (stream.alreadySetEndHandler) {
return;
}
stream.alreadySetEndHandler = true;
var streamEndedEvent = 'ended';
if ('oninactive' in stream) {
streamEndedEvent = 'inactive';
}
stream.addEventListener(streamEndedEvent, function() {
if (stream.idInstance) {
currentUserMediaRequest.remove(stream.idInstance);
}
if (!isRemote) {
// reset attachStreams
var streams = [];
connection.attachStreams.forEach(function(s) {
if (s.id != stream.id) {
streams.push(s);
}
});
connection.attachStreams = streams;
}
// connection.renegotiate();
var streamEvent = connection.streamEvents[stream.streamid];
if (!streamEvent) {
streamEvent = {
stream: stream,
streamid: stream.streamid,
type: isRemote ? 'remote' : 'local',
userid: connection.userid,
extra: connection.extra,
mediaElement: connection.streamEvents[stream.streamid] ? connection.streamEvents[stream.streamid].mediaElement : null
};
}
if (isRemote && connection.peers[streamEvent.userid]) {
// reset remote "streams"
var peer = connection.peers[streamEvent.userid].peer;
var streams = [];
peer.getRemoteStreams().forEach(function(s) {
if (s.id != stream.id) {
streams.push(s);
}
});
connection.peers[streamEvent.userid].streams = streams;
}
if (streamEvent.userid === connection.userid && streamEvent.type === 'remote') {
return;
}
if (connection.peersBackup[streamEvent.userid]) {
streamEvent.extra = connection.peersBackup[streamEvent.userid].extra;
}
connection.onstreamended(streamEvent);
delete connection.streamEvents[stream.streamid];
}, false);
};
connection.onMediaError = function(error, constraints) {
if (!!connection.enableLogs) {
console.error(error, constraints);
}
};
connection.autoCloseEntireSession = false;
connection.filesContainer = connection.videosContainer = document.body || document.documentElement;
connection.isInitiator = false;
connection.shareFile = mPeer.shareFile;
if (typeof FileProgressBarHandler !== 'undefined') {
FileProgressBarHandler.handle(connection);
}
if (typeof TranslationHandler !== 'undefined') {
TranslationHandler.handle(connection);
}
connection.token = getRandomString;
connection.onNewParticipant = function(participantId, userPreferences) {
connection.acceptParticipationRequest(participantId, userPreferences);
};
connection.acceptParticipationRequest = function(participantId, userPreferences) {
if (userPreferences.successCallback) {
userPreferences.successCallback();
delete userPreferences.successCallback;
}
mPeer.createNewPeer(participantId, userPreferences);
};
if (typeof StreamsHandler !== 'undefined') {
connection.StreamsHandler = StreamsHandler;
}
connection.onleave = function(userid) {};
connection.invokeSelectFileDialog = function(callback) {
var selector = new FileSelector();
selector.accept = '*.*';
selector.selectSingleFile(callback);
};
connection.onmute = function(e) {
if (!e || !e.mediaElement) {
return;
}
if (e.muteType === 'both' || e.muteType === 'video') {
e.mediaElement.src = null;
var paused = e.mediaElement.pause();
if (typeof paused !== 'undefined') {
paused.then(function() {
e.mediaElement.poster = e.snapshot || 'https://cdn.webrtc-experiment.com/images/muted.png';
});
} else {
e.mediaElement.poster = e.snapshot || 'https://cdn.webrtc-experiment.com/images/muted.png';
}
} else if (e.muteType === 'audio') {
e.mediaElement.muted = true;
}
};
connection.onunmute = function(e) {
if (!e || !e.mediaElement || !e.stream) {
return;
}
if (e.unmuteType === 'both' || e.unmuteType === 'video') {
e.mediaElement.poster = null;
e.mediaElement.srcObject = e.stream;
e.mediaElement.play();
} else if (e.unmuteType === 'audio') {
e.mediaElement.muted = false;
}
};
connection.onExtraDataUpdated = function(event) {
event.status = 'online';
connection.onUserStatusChanged(event, true);
};
connection.getAllParticipants = function(sender) {
return connection.peers.getAllParticipants(sender);
};
if (typeof StreamsHandler !== 'undefined') {
StreamsHandler.onSyncNeeded = function(streamid, action, type) {
connection.peers.getAllParticipants().forEach(function(participant) {
mPeer.onNegotiationNeeded({
streamid: streamid,
action: action,
streamSyncNeeded: true,
type: type || 'both'
}, participant);
});
};
}
connection.connectSocket = function(callback) {
connectSocket(callback);
};
connection.closeSocket = function() {
try {
io.sockets = {};
} catch (e) {};
if (!connection.socket) return;
if (typeof connection.socket.disconnect === 'function') {
connection.socket.disconnect();
}
if (typeof connection.socket.resetProps === 'function') {
connection.socket.resetProps();
}
connection.socket = null;
};
connection.getSocket = function(callback) {
if (!callback && connection.enableLogs) {
console.warn('getSocket.callback paramter is required.');
}
callback = callback || function() {};
if (!connection.socket) {
connectSocket(function() {
callback(connection.socket);
});
} else {
callback(connection.socket);
}
return connection.socket; // callback is preferred over return-statement
};
connection.getRemoteStreams = mPeer.getRemoteStreams;
var skipStreams = ['selectFirst', 'selectAll', 'forEach'];
connection.streamEvents = {
selectFirst: function(options) {
return connection.streamEvents.selectAll(options)[0];
},
selectAll: function(options) {
if (!options) {
// default will always be all streams
options = {
local: true,
remote: true,
isScreen: true,
isAudio: true,
isVideo: true
};
}
if (options == 'local') {
options = {
local: true
};
}
if (options == 'remote') {
options = {
remote: true
};
}
if (options == 'screen') {
options = {
isScreen: true
};
}
if (options == 'audio') {
options = {
isAudio: true
};
}
if (options == 'video') {
options = {
isVideo: true
};
}
var streams = [];
Object.keys(connection.streamEvents).forEach(function(key) {
var event = connection.streamEvents[key];
if (skipStreams.indexOf(key) !== -1) return;
var ignore = true;
if (options.local && event.type === 'local') {
ignore = false;
}
if (options.remote && event.type === 'remote') {
ignore = false;
}
if (options.isScreen && event.stream.isScreen) {
ignore = false;
}
if (options.isVideo && event.stream.isVideo) {
ignore = false;
}
if (options.isAudio && event.stream.isAudio) {
ignore = false;
}
if (options.userid && event.userid === options.userid) {
ignore = false;
}
if (ignore === false) {
streams.push(event);
}
});
return streams;
}
};
connection.socketURL = '/'; // generated via config.json
connection.socketMessageEvent = 'RTCMultiConnection-Message'; // generated via config.json
connection.socketCustomEvent = 'RTCMultiConnection-Custom-Message'; // generated via config.json
connection.DetectRTC = DetectRTC;
connection.setCustomSocketEvent = function(customEvent) {
if (customEvent) {
connection.socketCustomEvent = customEvent;
}
if (!connection.socket) {
return;
}
connection.socket.emit('set-custom-socket-event-listener', connection.socketCustomEvent);
};
connection.getNumberOfBroadcastViewers = function(broadcastId, callback) {
if (!connection.socket || !broadcastId || !callback) return;
connection.socket.emit('get-number-of-users-in-specific-broadcast', broadcastId, callback);
};
connection.onNumberOfBroadcastViewersUpdated = function(event) {
if (!connection.enableLogs || !connection.isInitiator) return;
console.info('Number of broadcast (', event.broadcastId, ') viewers', event.numberOfBroadcastViewers);
};
connection.onUserStatusChanged = function(event, dontWriteLogs) {
if (!!connection.enableLogs && !dontWriteLogs) {
console.info(event.userid, event.status);
}
};
connection.getUserMediaHandler = getUserMediaHandler;
connection.multiPeersHandler = mPeer;
connection.enableLogs = true;
connection.setCustomSocketHandler = function(customSocketHandler) {
if (typeof SocketConnection !== 'undefined') {
SocketConnection = customSocketHandler;
}
};
// default value should be 15k because [old]Firefox's receiving limit is 16k!
// however 64k works chrome-to-chrome
connection.chunkSize = 40 * 1000;
connection.maxParticipantsAllowed = 1000;
// eject or leave single user
connection.disconnectWith = mPeer.disconnectWith;
// check if room exist on server
// we will pass roomid to the server and wait for callback (i.e. server's response)
connection.checkPresence = function(roomid, callback) {
roomid = roomid || connection.sessionid;
if (SocketConnection.name === 'SSEConnection') {
SSEConnection.checkPresence(roomid, function(isRoomExist, _roomid, extra) {
if (!connection.socket) {
if (!isRoomExist) {
// connection.userid = _roomid;
}
connection.connectSocket(function() {
callback(isRoomExist, _roomid, extra);
});
return;
}
callback(isRoomExist, _roomid);
});
return;
}
if (!connection.socket) {
connection.connectSocket(function() {
connection.checkPresence(roomid, callback);
});
return;
}
connection.socket.emit('check-presence', roomid + '', function(isRoomExist, _roomid, extra) {
if (connection.enableLogs) {
console.log('checkPresence.isRoomExist: ', isRoomExist, ' roomid: ', _roomid);
}
callback(isRoomExist, _roomid, extra);
});
};
connection.onReadyForOffer = function(remoteUserId, userPreferences) {
connection.multiPeersHandler.createNewPeer(remoteUserId, userPreferences);
};
connection.setUserPreferences = function(userPreferences) {
if (connection.dontAttachStream) {
userPreferences.dontAttachLocalStream = true;
}
if (connection.dontGetRemoteStream) {
userPreferences.dontGetRemoteStream = true;
}
return userPreferences;
};
connection.updateExtraData = function() {
connection.socket.emit('extra-data-updated', connection.extra);
};
connection.enableScalableBroadcast = false;
connection.maxRelayLimitPerUser = 3; // each broadcast should serve only 3 users
connection.dontCaptureUserMedia = false;
connection.dontAttachStream = false;
connection.dontGetRemoteStream = false;
connection.onReConnecting = function(event) {
if (connection.enableLogs) {
console.info('ReConnecting with', event.userid, '...');
}
};
connection.beforeAddingStream = function(stream) {
return stream;
};
connection.beforeRemovingStream = function(stream) {
return stream;
};
if (typeof isChromeExtensionAvailable !== 'undefined') {
connection.checkIfChromeExtensionAvailable = isChromeExtensionAvailable;
}
if (typeof isFirefoxExtensionAvailable !== 'undefined') {
connection.checkIfChromeExtensionAvailable = isFirefoxExtensionAvailable;
}
if (typeof getChromeExtensionStatus !== 'undefined') {
connection.getChromeExtensionStatus = getChromeExtensionStatus;
}
connection.modifyScreenConstraints = function(screen_constraints) {
return screen_constraints;
};
//var rem2 = localStorage.getItem("usernow");
// rem2 = '"' + rem2 + '"';
var con = 0;
//setInterval(function(){alert(ssid2+" "+rem2);}, 15000);
//ssid2 = JSON.parse(ssid2);
//var ssid2 = connection.sessionid;
connection.onPeerStateChanged = function(state) {
if (connection.enableLogs) {
if (state.iceConnectionState.search(/closed|failed/gi) !== -1) {
console.error('Peer connection is closed between you & ', state.userid, state.extra, 'state:', state.iceConnectionState);
con++;
//alert(con);
/*setTimeout(function(){
if(con>1){
connection.closeSocket();
//connection.attachStreams.forEach(function(stream) {
// stream.stop();
// });
var ssid6 = localStorage.getItem("tablesession");
ssid6 = JSON.parse(ssid6);
connection.join(ssid6);
}con = 0;}, 10000);*/
/*var rem2 = localStorage.getItem("usernow");
//ssid2 = localStorage.getItem("tablesession");
ssid2 = JSON.parse(ssid2);
var myarray = rem2 + ',' + ssid2 ;
myarray = JSON.stringify(myarray);
connection.socket.emit('rej',myarray);*/
/*connection.socket.emit('join-room', {
sessionid: connection.sessionid,
session: connection.session,
mediaConstraints: connection.mediaConstraints,
sdpConstraints: connection.sdpConstraints,
streams: getStreamInfoForAdmin(),
extra: connection.extra,
password: typeof connection.password !== 'undefined' && typeof connection.password !== 'object' ? connection.password : ''
});*/
}
}
};
connection.isOnline = true;
listenEventHandler('online', function() {
connection.isOnline = true;
});
listenEventHandler('offline', function() {
connection.isOnline = false;
});
connection.isLowBandwidth = false;
if (navigator && navigator.connection && navigator.connection.type) {
connection.isLowBandwidth = navigator.connection.type.toString().toLowerCase().search(/wifi|cell/g) !== -1;
if (connection.isLowBandwidth) {
connection.bandwidth = {
audio: false,
video: false,
screen: false
};
if (connection.mediaConstraints.audio && connection.mediaConstraints.audio.optional && connection.mediaConstraints.audio.optional.length) {
var newArray = [];
connection.mediaConstraints.audio.optional.forEach(function(opt) {
if (typeof opt.bandwidth === 'undefined') {
newArray.push(opt);
}
});
connection.mediaConstraints.audio.optional = newArray;
}
if (connection.mediaConstraints.video && connection.mediaConstraints.video.optional && connection.mediaConstraints.video.optional.length) {
var newArray = [];
connection.mediaConstraints.video.optional.forEach(function(opt) {
if (typeof opt.bandwidth === 'undefined') {
newArray.push(opt);
}
});
connection.mediaConstraints.video.optional = newArray;
}
}
}
connection.getExtraData = function(remoteUserId, callback) {
if (!remoteUserId) throw 'remoteUserId is required.';
if (typeof callback === 'function') {
connection.socket.emit('get-remote-user-extra-data', remoteUserId, function(extra, remoteUserId, error) {
callback(extra, remoteUserId, error);
});
return;
}
if (!connection.peers[remoteUserId]) {
if (connection.peersBackup[remoteUserId]) {
return connection.peersBackup[remoteUserId].extra;
}
return {};
}
return connection.peers[remoteUserId].extra;
};
if (!!forceOptions.autoOpenOrJoin) {
connection.openOrJoin(connection.sessionid);
}
connection.onUserIdAlreadyTaken = function(useridAlreadyTaken, yourNewUserId) {
// via #683
connection.close();
connection.closeSocket();
connection.isInitiator = false;
connection.userid = connection.token();
connection.join(connection.sessionid);
if (connection.enableLogs) {
console.warn('Userid already taken.', useridAlreadyTaken, 'Your new userid:', connection.userid);
}
};
connection.trickleIce = true;
connection.version = '3.6.9';
connection.onSettingLocalDescription = function(event) {
if (connection.enableLogs) {
console.info('Set local description for remote user', event.userid);
}
};
connection.resetScreen = function() {
sourceId = null;
if (DetectRTC && DetectRTC.screen) {
delete DetectRTC.screen.sourceId;
}
currentUserMediaRequest = {
streams: [],
mutex: false,
queueRequests: []
};
};
// if disabled, "event.mediaElement" for "onstream" will be NULL
connection.autoCreateMediaElement = true;
// set password
connection.password = null;
// set password
connection.setPassword = function(password, callback) {
callback = callback || function() {};
if (connection.socket) {
connection.socket.emit('set-password', password, callback);
} else {
connection.password = password;
callback(true, connection.sessionid, null);
}
};
connection.onSocketDisconnect = function(event) {
if (connection.enableLogs) {
console.warn('socket.io connection is closed');
}
};
connection.onSocketError = function(event) {
if (connection.enableLogs) {
console.warn('socket.io connection is failed');
}
};
// error messages
connection.errors = {
ROOM_NOT_AVAILABLE: 'Room not available',
INVALID_PASSWORD: 'Invalid password',
USERID_NOT_AVAILABLE: 'User ID does not exist',
ROOM_PERMISSION_DENIED: 'Room permission denied',
ROOM_FULL: 'Room full',
DID_NOT_JOIN_ANY_ROOM: 'Did not join any room yet',
INVALID_SOCKET: 'Invalid socket',
PUBLIC_IDENTIFIER_MISSING: 'publicRoomIdentifier is required',
INVALID_ADMIN_CREDENTIAL: 'Invalid username or password attempted'
};
})(this);
};
if (typeof module !== 'undefined' /* && !!module.exports*/ ) {
module.exports = exports = RTCMultiConnection;
}
if (typeof define === 'function' && define.amd) {
define('RTCMultiConnection', [], function() {
return RTCMultiConnection;
});
}
(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.adapter = 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 0 && arguments[0] !== undefined ? arguments[0] : {},
window = _ref.window;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
shimChrome: true,
shimFirefox: 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,
// Expose sdp as a convenience. For production apps include directly.
sdp: sdp
};
// 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;
}
if (browserDetails.version === null) {
logging('Chrome shim can not determine version, not shimming.');
return adapter;
}
logging('adapter.js shimming chrome.');
// Export to the adapter global object visible in the browser.
adapter.browserShim = chromeShim;
// Must be called before shimPeerConnection.
commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
chromeShim.shimGetUserMedia(window, browserDetails);
chromeShim.shimMediaStream(window, browserDetails);
chromeShim.shimPeerConnection(window, browserDetails);
chromeShim.shimOnTrack(window, browserDetails);
chromeShim.shimAddTrackRemoveTrack(window, browserDetails);
chromeShim.shimGetSendersWithDtmf(window, browserDetails);
chromeShim.shimGetStats(window, browserDetails);
chromeShim.shimSenderReceiverGetStats(window, browserDetails);
chromeShim.fixNegotiationNeeded(window, browserDetails);
commonShim.shimRTCIceCandidate(window, browserDetails);
commonShim.shimConnectionState(window, browserDetails);
commonShim.shimMaxMessageSize(window, browserDetails);
commonShim.shimSendThrowTypeError(window, browserDetails);
commonShim.removeExtmapAllowMixed(window, browserDetails);
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;
// Must be called before shimPeerConnection.
commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
firefoxShim.shimGetUserMedia(window, browserDetails);
firefoxShim.shimPeerConnection(window, browserDetails);
firefoxShim.shimOnTrack(window, browserDetails);
firefoxShim.shimRemoveStream(window, browserDetails);
firefoxShim.shimSenderGetStats(window, browserDetails);
firefoxShim.shimReceiverGetStats(window, browserDetails);
firefoxShim.shimRTCDataChannel(window, browserDetails);
firefoxShim.shimAddTransceiver(window, browserDetails);
firefoxShim.shimGetParameters(window, browserDetails);
firefoxShim.shimCreateOffer(window, browserDetails);
firefoxShim.shimCreateAnswer(window, browserDetails);
commonShim.shimRTCIceCandidate(window, browserDetails);
commonShim.shimConnectionState(window, browserDetails);
commonShim.shimMaxMessageSize(window, browserDetails);
commonShim.shimSendThrowTypeError(window, browserDetails);
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;
// Must be called before shimCallbackAPI.
commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);
commonShim.shimParameterlessSetLocalDescription(window, browserDetails);
safariShim.shimRTCIceServerUrls(window, browserDetails);
safariShim.shimCreateOfferLegacy(window, browserDetails);
safariShim.shimCallbacksAPI(window, browserDetails);
safariShim.shimLocalStreamsAPI(window, browserDetails);
safariShim.shimRemoteStreamsAPI(window, browserDetails);
safariShim.shimTrackEventTransceiver(window, browserDetails);
safariShim.shimGetUserMedia(window, browserDetails);
safariShim.shimAudioContext(window, browserDetails);
commonShim.shimRTCIceCandidate(window, browserDetails);
commonShim.shimMaxMessageSize(window, browserDetails);
commonShim.shimSendThrowTypeError(window, browserDetails);
commonShim.removeExtmapAllowMixed(window, browserDetails);
break;
default:
logging('Unsupported browser!');
break;
}
return adapter;
}
// Browser shims.
},{"./chrome/chrome_shim":3,"./common_shim":6,"./firefox/firefox_shim":7,"./safari/safari_shim":10,"./utils":11,"sdp":12}],3:[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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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, browserDetails) {
if (!window.RTCPeerConnection) {
return;
}
// 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, browserDetails) {
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];
});
}
}
// Attempt to fix ONN in plan-b mode.
function fixNegotiationNeeded(window, browserDetails) {
utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) {
var pc = e.target;
if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') {
if (pc.signalingState !== 'stable') {
return;
}
}
return e;
});
}
},{"../utils.js":11,"./getdisplaymedia":4,"./getusermedia":5}],4:[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);
});
};
}
},{}],5:[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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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, browserDetails) {
var navigator = window && window.navigator;
if (!navigator.mediaDevices) {
return;
}
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":11}],6:[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';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
exports.shimRTCIceCandidate = shimRTCIceCandidate;
exports.shimMaxMessageSize = shimMaxMessageSize;
exports.shimSendThrowTypeError = shimSendThrowTypeError;
exports.shimConnectionState = shimConnectionState;
exports.removeExtmapAllowMixed = removeExtmapAllowMixed;
exports.shimAddIceCandidateNullOrEmpty = shimAddIceCandidateNullOrEmpty;
exports.shimParameterlessSetLocalDescription = shimParameterlessSetLocalDescription;
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, browserDetails) {
if (!window.RTCPeerConnection) {
return;
}
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 removeExtmapAllowMixed(window, browserDetails) {
/* remove a=extmap-allow-mixed for webrtc.org < M71 */
if (!window.RTCPeerConnection) {
return;
}
if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
return;
}
if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
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) {
var sdp = desc.sdp.split('\n').filter(function (line) {
return line.trim() !== 'a=extmap-allow-mixed';
}).join('\n');
// Safari enforces read-only-ness of RTCSessionDescription fields.
if (window.RTCSessionDescription && desc instanceof window.RTCSessionDescription) {
arguments[0] = new window.RTCSessionDescription({
type: desc.type,
sdp: sdp
});
} else {
desc.sdp = sdp;
}
}
return nativeSRD.apply(this, arguments);
};
}
function shimAddIceCandidateNullOrEmpty(window, browserDetails) {
// Support for addIceCandidate(null or undefined)
// as well as addIceCandidate({candidate: "", ...})
// https://bugs.chromium.org/p/chromium/issues/detail?id=978582
// Note: must be called before other polyfills which change the signature.
if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
return;
}
var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {
return;
}
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 for ignoring exists for Chrome M77+.
// Safari ignores as well, exact version unknown but works in the same
// version that also ignores addIceCandidate(null).
if ((browserDetails.browser === 'chrome' && browserDetails.version < 78 || browserDetails.browser === 'firefox' && browserDetails.version < 68 || browserDetails.browser === 'safari') && arguments[0] && arguments[0].candidate === '') {
return Promise.resolve();
}
return nativeAddIceCandidate.apply(this, arguments);
};
}
// Note: Make sure to call this ahead of APIs that modify
// setLocalDescription.length
function shimParameterlessSetLocalDescription(window, browserDetails) {
if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
return;
}
var nativeSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {
return;
}
window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
var _this = this;
var desc = arguments[0] || {};
if ((typeof desc === 'undefined' ? 'undefined' : _typeof(desc)) !== 'object' || desc.type && desc.sdp) {
return nativeSetLocalDescription.apply(this, arguments);
}
// The remaining steps should technically happen when SLD comes off the
// RTCPeerConnection's operations chain (not ahead of going on it), but
// this is too difficult to shim. Instead, this shim only covers the
// common case where the operations chain is empty. This is imperfect, but
// should cover many cases. Rationale: Even if we can't reduce the glare
// window to zero on imperfect implementations, there's value in tapping
// into the perfect negotiation pattern that several browsers support.
desc = { type: desc.type, sdp: desc.sdp };
if (!desc.type) {
switch (this.signalingState) {
case 'stable':
case 'have-local-offer':
case 'have-remote-pranswer':
desc.type = 'offer';
break;
default:
desc.type = 'answer';
break;
}
}
if (desc.sdp || desc.type !== 'offer' && desc.type !== 'answer') {
return nativeSetLocalDescription.apply(this, [desc]);
}
var func = desc.type === 'offer' ? this.createOffer : this.createAnswer;
return func.apply(this).then(function (d) {
return nativeSetLocalDescription.apply(_this, [d]);
});
};
}
},{"./utils":11,"sdp":12}],7:[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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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;
exports.shimAddTransceiver = shimAddTransceiver;
exports.shimGetParameters = shimGetParameters;
exports.shimCreateOffer = shimCreateOffer;
exports.shimCreateAnswer = shimCreateAnswer;
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, browserDetails) {
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];
});
}
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;
}
}
function shimAddTransceiver(window) {
// https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
// Firefox ignores the init sendEncodings options passed to addTransceiver
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
return;
}
var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
if (origAddTransceiver) {
window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() {
this.setParametersPromises = [];
var initParameters = arguments[1];
var shouldPerformCheck = initParameters && 'sendEncodings' in initParameters;
if (shouldPerformCheck) {
// If sendEncodings params are provided, validate grammar
initParameters.sendEncodings.forEach(function (encodingParam) {
if ('rid' in encodingParam) {
var ridRegex = /^[a-z0-9]{0,16}$/i;
if (!ridRegex.test(encodingParam.rid)) {
throw new TypeError('Invalid RID value provided.');
}
}
if ('scaleResolutionDownBy' in encodingParam) {
if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
throw new RangeError('scale_resolution_down_by must be >= 1.0');
}
}
if ('maxFramerate' in encodingParam) {
if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
throw new RangeError('max_framerate must be >= 0.0');
}
}
});
}
var transceiver = origAddTransceiver.apply(this, arguments);
if (shouldPerformCheck) {
// Check if the init options were applied. If not we do this in an
// asynchronous way and save the promise reference in a global object.
// This is an ugly hack, but at the same time is way more robust than
// checking the sender parameters before and after the createOffer
// Also note that after the createoffer we are not 100% sure that
// the params were asynchronously applied so we might miss the
// opportunity to recreate offer.
var sender = transceiver.sender;
var params = sender.getParameters();
if (!('encodings' in params) ||
// Avoid being fooled by patched getParameters() below.
params.encodings.length === 1 && Object.keys(params.encodings[0]).length === 0) {
params.encodings = initParameters.sendEncodings;
sender.sendEncodings = initParameters.sendEncodings;
this.setParametersPromises.push(sender.setParameters(params).then(function () {
delete sender.sendEncodings;
}).catch(function () {
delete sender.sendEncodings;
}));
}
}
return transceiver;
};
}
}
function shimGetParameters(window) {
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCRtpSender)) {
return;
}
var origGetParameters = window.RTCRtpSender.prototype.getParameters;
if (origGetParameters) {
window.RTCRtpSender.prototype.getParameters = function getParameters() {
var params = origGetParameters.apply(this, arguments);
if (!('encodings' in params)) {
params.encodings = [].concat(this.sendEncodings || [{}]);
}
return params;
};
}
}
function shimCreateOffer(window) {
// https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
// Firefox ignores the init sendEncodings options passed to addTransceiver
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
return;
}
var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
window.RTCPeerConnection.prototype.createOffer = function createOffer() {
var _this4 = this,
_arguments2 = arguments;
if (this.setParametersPromises && this.setParametersPromises.length) {
return Promise.all(this.setParametersPromises).then(function () {
return origCreateOffer.apply(_this4, _arguments2);
}).finally(function () {
_this4.setParametersPromises = [];
});
}
return origCreateOffer.apply(this, arguments);
};
}
function shimCreateAnswer(window) {
// https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
// Firefox ignores the init sendEncodings options passed to addTransceiver
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
return;
}
var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
var _this5 = this,
_arguments3 = arguments;
if (this.setParametersPromises && this.setParametersPromises.length) {
return Promise.all(this.setParametersPromises).then(function () {
return origCreateAnswer.apply(_this5, _arguments3);
}).finally(function () {
_this5.setParametersPromises = [];
});
}
return origCreateAnswer.apply(this, arguments);
};
}
},{"../utils":11,"./getdisplaymedia":8,"./getusermedia":9}],8:[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);
};
}
},{}],9:[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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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, browserDetails) {
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":11}],10:[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';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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;
exports.shimAudioContext = shimAudioContext;
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) {
var _this2 = this;
for (var _len = arguments.length, streams = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
streams[_key - 1] = arguments[_key];
}
if (streams) {
streams.forEach(function (stream) {
if (!_this2._localStreams) {
_this2._localStreams = [stream];
} else if (!_this2._localStreams.includes(stream)) {
_this2._localStreams.push(stream);
}
});
}
return _addTrack.apply(this, arguments);
};
}
if (!('removeStream' in window.RTCPeerConnection.prototype)) {
window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
var _this3 = 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)) {
_this3.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 _this4 = 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 (!_this4._remoteStreams) {
_this4._remoteStreams = [];
}
if (_this4._remoteStreams.includes(stream)) {
return;
}
_this4._remoteStreams.push(stream);
var event = new Event('addstream');
event.stream = stream;
_this4.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) {
if (!window.RTCPeerConnection) {
return;
}
// 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 OrigPeerConnection) {
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.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 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', { direction: 'recvonly' });
}
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', { direction: 'recvonly' });
}
}
return origCreateOffer.apply(this, arguments);
};
}
function shimAudioContext(window) {
if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || window.AudioContext) {
return;
}
window.AudioContext = window.webkitAudioContext;
}
},{"../utils":11}],11:[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 _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof 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) {
if (cb.handleEvent) {
cb.handleEvent(modifiedEvent);
} else {
cb(modifiedEvent);
}
}
};
this._eventMap = this._eventMap || {};
if (!this._eventMap[eventNameToWrap]) {
this._eventMap[eventNameToWrap] = new Map();
}
this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]);
};
var nativeRemoveEventListener = proto.removeEventListener;
proto.removeEventListener = function (nativeEventName, cb) {
if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) {
return nativeRemoveEventListener.apply(this, arguments);
}
if (!this._eventMap[eventNameToWrap].has(cb)) {
return nativeRemoveEventListener.apply(this, arguments);
}
var unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
this._eventMap[eventNameToWrap].delete(cb);
if (this._eventMap[eventNameToWrap].size === 0) {
delete this._eventMap[eventNameToWrap];
}
if (Object.keys(this._eventMap).length === 0) {
delete this._eventMap;
}
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) {
// 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;
}
var navigator = window.navigator;
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 (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;
}
},{}],12:[function(require,module,exports){
/* eslint-env node */
'use strict';
// SDP helpers.
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : 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"
// Input can be prefixed with a=.
SDPUtils.parseCandidate = function (line) {
var parts = void 0;
// 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: { 1: 'rtp', 2: 'rtcp' }[parts[1]] || parts[1],
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 compatibility.
candidate.usernameFragment = parts[i + 1];
break;
default:
// extension handling, in particular ufrag. Don't overwrite.
if (candidate[parts[i]] === undefined) {
candidate[parts[i]] = parts[i + 1];
}
break;
}
}
return candidate;
};
// Translates a candidate object into SDP candidate attribute.
// This does not include the a= prefix!
SDPUtils.writeCandidate = function (candidate) {
var sdp = [];
sdp.push(candidate.foundation);
var component = candidate.component;
if (component === 'rtp') {
sdp.push(1);
} else if (component === 'rtcp') {
sdp.push(2);
} else {
sdp.push(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.
// Sample input:
// a=ice-options:foo bar
SDPUtils.parseIceOptions = function (line) {
return line.substr(14).split(' ');
};
// Parses a 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;
};
// Generates 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 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 an 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 a fmtp 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 = void 0;
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 a fmtp 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] !== undefined) {
params.push(param + '=' + codec.parameters[param]);
} else {
params.push(param);
}
});
line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
}
return line;
};
// Parses a 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 a 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;
};
// Parse a ssrc-group line (see RFC 5576). Sample input:
// a=ssrc-group:semantics 12 34
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);
}
};
// Parses a fingerprint line for DTLS-SRTP.
SDPUtils.parseFingerprint = function (line) {
var parts = line.substr(14).split(' ');
return {
algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
value: parts[1].toUpperCase() // the definition is upper-case in RFC 4572.
};
};
// 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 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 a=crypto lines into
// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
SDPUtils.parseCryptoLine = function (line) {
var parts = line.substr(9).split(' ');
return {
tag: parseInt(parts[0], 10),
cryptoSuite: parts[1],
keyParams: parts[2],
sessionParams: parts.slice(3)
};
};
SDPUtils.writeCryptoLine = function (parameters) {
return 'a=crypto:' + parameters.tag + ' ' + parameters.cryptoSuite + ' ' + (_typeof(parameters.keyParams) === 'object' ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) : parameters.keyParams) + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + '\r\n';
};
// Parses the crypto key parameters into
// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
SDPUtils.parseCryptoKeyParams = function (keyParams) {
if (keyParams.indexOf('inline:') !== 0) {
return null;
}
var parts = keyParams.substr(7).split('|');
return {
keyMethod: 'inline',
keySalt: parts[0],
lifeTime: parts[1],
mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
mkiLength: parts[2] ? parts[2].split(':')[1] : undefined
};
};
SDPUtils.writeCryptoKeyParams = function (keyParams) {
return keyParams.keyMethod + ':' + keyParams.keySalt + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + (keyParams.mkiValue && keyParams.mkiLength ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength : '');
};
// Extracts all SDES parameters.
SDPUtils.getCryptoParameters = function (mediaSection, sessionpart) {
var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:');
return lines.map(SDPUtils.parseCryptoLine);
};
// 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 ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0];
var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0];
if (!(ufrag && pwd)) {
return null;
}
return {
usernameFragment: ufrag.substr(12),
password: pwd.substr(10)
};
};
// Serializes ICE parameters to SDP.
SDPUtils.writeIceParameters = function (params) {
var sdp = 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n';
if (params.iceLite) {
sdp += 'a=ice-lite\r\n';
}
return sdp;
};
// Parses the SDP media section and returns RTCRtpParameters.
SDPUtils.parseRtpParameters = function (mediaSection) {
var description = {
codecs: [],
headerExtensions: [],
fecMechanisms: [],
rtcp: []
};
var lines = SDPUtils.splitLines(mediaSection);
var mline = lines[0].split(' ');
for (var i = 3; i < mline.length; i++) {
// find all codecs from mline[3..]
var pt = mline[i];
var rtpmapline = SDPUtils.matchPrefix(mediaSection, 'a=rtpmap:' + pt + ' ')[0];
if (rtpmapline) {
var codec = SDPUtils.parseRtpMap(rtpmapline);
var fmtps = SDPUtils.matchPrefix(mediaSection, 'a=fmtp:' + pt + ' ');
// Only the first a=fmtp: is considered.
codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
codec.rtcpFeedback = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:' + pt + ' ').map(SDPUtils.parseRtcpFb);
description.codecs.push(codec);
// 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';
}
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 = void 0;
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 that with RTX there might be multiple
// SSRCs.
var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:').map(function (line) {
return SDPUtils.parseSsrcMedia(line);
}).filter(function (obj) {
return obj.attribute === 'cname';
})[0];
if (remoteSsrc) {
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;
};
SDPUtils.writeRtcpParameters = function (rtcpParameters) {
var sdp = '';
if (rtcpParameters.reducedSize) {
sdp += 'a=rtcp-rsize\r\n';
}
if (rtcpParameters.mux) {
sdp += 'a=rtcp-mux\r\n';
}
if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {
sdp += 'a=ssrc:' + rtcpParameters.ssrc + ' cname:' + rtcpParameters.cname + '\r\n';
}
return sdp;
};
// parses either a=msid: or a=ssrc:... msid lines and returns
// the id of the MediaStream and MediaStreamTrack.
SDPUtils.parseMsid = function (mediaSection) {
var parts = void 0;
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] };
}
};
// SCTP
// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
// to draft-ietf-mmusic-sctp-sdp-05
SDPUtils.parseSctpDescription = function (mediaSection) {
var mline = SDPUtils.parseMLine(mediaSection);
var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
var maxMessageSize = void 0;
if (maxSizeLine.length > 0) {
maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);
}
if (isNaN(maxMessageSize)) {
maxMessageSize = 65536;
}
var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
if (sctpPort.length > 0) {
return {
port: parseInt(sctpPort[0].substr(12), 10),
protocol: mline.fmt,
maxMessageSize: maxMessageSize
};
}
var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
if (sctpMapLines.length > 0) {
var parts = sctpMapLines[0].substr(10).split(' ');
return {
port: parseInt(parts[0], 10),
protocol: parts[1],
maxMessageSize: maxMessageSize
};
}
};
// SCTP
// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
// support by now receiving in this format, unless we originally parsed
// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line
// protocol of DTLS/SCTP -- without UDP/ or TCP/)
SDPUtils.writeSctpDescription = function (media, sctp) {
var output = [];
if (media.protocol !== 'DTLS/SCTP') {
output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctp-port:' + sctp.port + '\r\n'];
} else {
output = ['m=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', 'c=IN IP4 0.0.0.0\r\n', 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n'];
}
if (sctp.maxMessageSize !== undefined) {
output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
}
return output.join('');
};
// 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 boiler 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 = void 0;
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';
};
// 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;
}
},{}]},{},[1])(1)
});
// __________________
// getHTMLMediaElement.js
function getHTMLMediaElement(mediaElement, config) {
config = config || {};
if (!mediaElement.nodeName || (mediaElement.nodeName.toLowerCase() != 'audio' && mediaElement.nodeName.toLowerCase() != 'video')) {
if (!mediaElement.getVideoTracks().length) {
return getAudioElement(mediaElement, config);
}
var mediaStream = mediaElement;
mediaElement = document.createElement(mediaStream.getVideoTracks().length ? 'video' : 'audio');
try {
mediaElement.setAttributeNode(document.createAttribute('autoplay'));
mediaElement.setAttributeNode(document.createAttribute('playsinline'));
} catch (e) {
mediaElement.setAttribute('autoplay', true);
mediaElement.setAttribute('playsinline', true);
}
if ('srcObject' in mediaElement) {
mediaElement.srcObject = mediaStream;
} else {
mediaElement[!!navigator.mozGetUserMedia ? 'mozSrcObject' : 'src'] = !!navigator.mozGetUserMedia ? mediaStream : (window.URL || window.webkitURL).createObjectURL(mediaStream);
}
}
if (mediaElement.nodeName && mediaElement.nodeName.toLowerCase() == 'audio') {
return getAudioElement(mediaElement, config);
}
var buttons = config.buttons || ['mute-audio', 'mute-video', 'full-screen', 'volume-slider', 'stop'];
buttons.has = function(element) {
return buttons.indexOf(element) !== -1;
};
config.toggle = config.toggle || [];
config.toggle.has = function(element) {
return config.toggle.indexOf(element) !== -1;
};
var mediaElementContainer = document.createElement('div');
mediaElementContainer.className = 'media-container';
var mediaControls = document.createElement('div');
mediaControls.className = 'media-controls';
mediaElementContainer.appendChild(mediaControls);
if (buttons.has('mute-audio')) {
var muteAudio = document.createElement('div');
muteAudio.className = 'control ' + (config.toggle.has('mute-audio') ? 'unmute-audio selected' : 'mute-audio');
mediaControls.appendChild(muteAudio);
muteAudio.onclick = function() {
if (muteAudio.className.indexOf('unmute-audio') != -1) {
muteAudio.className = muteAudio.className.replace('unmute-audio selected', 'mute-audio');
mediaElement.muted = false;
mediaElement.volume = 1;
if (config.onUnMuted) config.onUnMuted('audio');
} else {
muteAudio.className = muteAudio.className.replace('mute-audio', 'unmute-audio selected');
mediaElement.muted = true;
mediaElement.volume = 0;
if (config.onMuted) config.onMuted('audio');
}
};
}
if (buttons.has('mute-video')) {
var muteVideo = document.createElement('div');
muteVideo.className = 'control ' + (config.toggle.has('mute-video') ? 'unmute-video selected' : 'mute-video');
mediaControls.appendChild(muteVideo);
muteVideo.onclick = function() {
if (muteVideo.className.indexOf('unmute-video') != -1) {
muteVideo.className = muteVideo.className.replace('unmute-video selected', 'mute-video');
mediaElement.muted = false;
mediaElement.volume = 1;
mediaElement.play();
if (config.onUnMuted) config.onUnMuted('video');
} else {
muteVideo.className = muteVideo.className.replace('mute-video', 'unmute-video selected');
mediaElement.muted = true;
mediaElement.volume = 0;
mediaElement.pause();
if (config.onMuted) config.onMuted('video');
}
};
}
if (buttons.has('take-snapshot')) {
var takeSnapshot = document.createElement('div');
takeSnapshot.className = 'control take-snapshot';
mediaControls.appendChild(takeSnapshot);
takeSnapshot.onclick = function() {
if (config.onTakeSnapshot) config.onTakeSnapshot();
};
}
if (buttons.has('stop')) {
var stop = document.createElement('div');
stop.className = 'control stop';
mediaControls.appendChild(stop);
stop.onclick = function() {
mediaElementContainer.style.opacity = 0;
setTimeout(function() {
if (mediaElementContainer.parentNode) {
mediaElementContainer.parentNode.removeChild(mediaElementContainer);
}
}, 800);
if (config.onStopped) config.onStopped();
};
}
var volumeControl = document.createElement('div');
volumeControl.className = 'volume-control';
if (buttons.has('record-audio')) {
var recordAudio = document.createElement('div');
recordAudio.className = 'control ' + (config.toggle.has('record-audio') ? 'stop-recording-audio selected' : 'record-audio');
volumeControl.appendChild(recordAudio);
recordAudio.onclick = function() {
if (recordAudio.className.indexOf('stop-recording-audio') != -1) {
recordAudio.className = recordAudio.className.replace('stop-recording-audio selected', 'record-audio');
if (config.onRecordingStopped) config.onRecordingStopped('audio');
} else {
recordAudio.className = recordAudio.className.replace('record-audio', 'stop-recording-audio selected');
if (config.onRecordingStarted) config.onRecordingStarted('audio');
}
};
}
if (buttons.has('record-video')) {
var recordVideo = document.createElement('div');
recordVideo.className = 'control ' + (config.toggle.has('record-video') ? 'stop-recording-video selected' : 'record-video');
volumeControl.appendChild(recordVideo);
recordVideo.onclick = function() {
if (recordVideo.className.indexOf('stop-recording-video') != -1) {
recordVideo.className = recordVideo.className.replace('stop-recording-video selected', 'record-video');
if (config.onRecordingStopped) config.onRecordingStopped('video');
} else {
recordVideo.className = recordVideo.className.replace('record-video', 'stop-recording-video selected');
if (config.onRecordingStarted) config.onRecordingStarted('video');
}
};
}
if (buttons.has('volume-slider')) {
var volumeSlider = document.createElement('div');
volumeSlider.className = 'control volume-slider';
volumeControl.appendChild(volumeSlider);
var slider = document.createElement('input');
slider.type = 'range';
slider.min = 0;
slider.max = 100;
slider.value = 100;
slider.onchange = function() {
mediaElement.volume = '.' + slider.value.toString().substr(0, 1);
};
volumeSlider.appendChild(slider);
}
if (buttons.has('full-screen')) {
var zoom = document.createElement('div');
zoom.className = 'control ' + (config.toggle.has('zoom-in') ? 'zoom-out selected' : 'zoom-in');
if (!slider && !recordAudio && !recordVideo && zoom) {
mediaControls.insertBefore(zoom, mediaControls.firstChild);
} else volumeControl.appendChild(zoom);
zoom.onclick = function() {
if (zoom.className.indexOf('zoom-out') != -1) {
zoom.className = zoom.className.replace('zoom-out selected', 'zoom-in');
exitFullScreen();
} else {
zoom.className = zoom.className.replace('zoom-in', 'zoom-out selected');
launchFullscreen(mediaElementContainer);
}
};
function launchFullscreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
}
}
function exitFullScreen() {
if (document.fullscreen) {
document.cancelFullScreen();
}
if (document.mozFullScreen) {
document.mozCancelFullScreen();
}
if (document.webkitIsFullScreen) {
document.webkitCancelFullScreen();
}
}
function screenStateChange(e) {
if (e.srcElement != mediaElementContainer) return;
var isFullScreeMode = document.webkitIsFullScreen || document.mozFullScreen || document.fullscreen;
mediaElementContainer.style.width = (isFullScreeMode ? (window.innerWidth - 20) : config.width) + 'px';
mediaElementContainer.style.display = isFullScreeMode ? 'block' : 'inline-block';
if (config.height) {
mediaBox.style.height = (isFullScreeMode ? (window.innerHeight - 20) : config.height) + 'px';
}
if (!isFullScreeMode && config.onZoomout) config.onZoomout();
if (isFullScreeMode && config.onZoomin) config.onZoomin();
if (!isFullScreeMode && zoom.className.indexOf('zoom-out') != -1) {
zoom.className = zoom.className.replace('zoom-out selected', 'zoom-in');
if (config.onZoomout) config.onZoomout();
}
setTimeout(adjustControls, 1000);
}
document.addEventListener('fullscreenchange', screenStateChange, false);
document.addEventListener('mozfullscreenchange', screenStateChange, false);
document.addEventListener('webkitfullscreenchange', screenStateChange, false);
}
if (buttons.has('volume-slider') || buttons.has('full-screen') || buttons.has('record-audio') || buttons.has('record-video')) {
mediaElementContainer.appendChild(volumeControl);
}
var mediaBox = document.createElement('div');
mediaBox.className = 'media-box';
mediaElementContainer.appendChild(mediaBox);
if (config.title) {
var h2 = document.createElement('h2');
h2.innerHTML = config.title;
h2.setAttribute('style', 'position: absolute;color:white;font-size:17px;text-shadow: 1px 1px black;padding:0;margin:0;text-align: left; margin-top: 10px; margin-left: 10px; display: block; border: 0;line-height:1.5;z-index:1;');
mediaBox.appendChild(h2);
}
mediaBox.appendChild(mediaElement);
if (!config.width) config.width = (innerWidth / 2) - 50;
mediaElementContainer.style.width = config.width + 'px';
if (config.height) {
mediaBox.style.height = config.height + 'px';
}
mediaBox.querySelector('video').style.maxHeight = innerHeight + 'px';
var times = 0;
function adjustControls() {
mediaControls.style.marginLeft = (mediaElementContainer.clientWidth - mediaControls.clientWidth - 2) + 'px';
if (slider) {
slider.style.width = (mediaElementContainer.clientWidth / 3) + 'px';
volumeControl.style.marginLeft = (mediaElementContainer.clientWidth / 3 - 30) + 'px';
if (zoom) zoom.style['border-top-right-radius'] = '5px';
} else {
volumeControl.style.marginLeft = (mediaElementContainer.clientWidth - volumeControl.clientWidth - 2) + 'px';
}
volumeControl.style.marginTop = (mediaElementContainer.clientHeight - volumeControl.clientHeight - 2) + 'px';
if (times < 10) {
times++;
setTimeout(adjustControls, 1000);
} else times = 0;
}
if (config.showOnMouseEnter || typeof config.showOnMouseEnter === 'undefined') {
mediaElementContainer.onmouseenter = mediaElementContainer.onmousedown = function() {
adjustControls();
mediaControls.style.opacity = 1;
volumeControl.style.opacity = 1;
};
mediaElementContainer.onmouseleave = function() {
mediaControls.style.opacity = 0;
volumeControl.style.opacity = 0;
};
} else {
setTimeout(function() {
adjustControls();
setTimeout(function() {
mediaControls.style.opacity = 1;
volumeControl.style.opacity = 1;
}, 300);
}, 700);
}
adjustControls();
mediaElementContainer.toggle = function(clasName) {
if (typeof clasName != 'string') {
for (var i = 0; i < clasName.length; i++) {
mediaElementContainer.toggle(clasName[i]);
}
return;
}
if (clasName == 'mute-audio' && muteAudio) muteAudio.onclick();
if (clasName == 'mute-video' && muteVideo) muteVideo.onclick();
if (clasName == 'record-audio' && recordAudio) recordAudio.onclick();
if (clasName == 'record-video' && recordVideo) recordVideo.onclick();
if (clasName == 'stop' && stop) stop.onclick();
return this;
};
mediaElementContainer.media = mediaElement;
return mediaElementContainer;
}
// __________________
// getAudioElement.js
function getAudioElement(mediaElement, config) {
config = config || {};
if (!mediaElement.nodeName || (mediaElement.nodeName.toLowerCase() != 'audio' && mediaElement.nodeName.toLowerCase() != 'video')) {
var mediaStream = mediaElement;
mediaElement = document.createElement('audio');
try {
mediaElement.setAttributeNode(document.createAttribute('autoplay'));
mediaElement.setAttributeNode(document.createAttribute('controls'));
} catch (e) {
mediaElement.setAttribute('autoplay', true);
mediaElement.setAttribute('controls', true);
}
if ('srcObject' in mediaElement) {
mediaElement.mediaElement = mediaStream;
} else {
mediaElement[!!navigator.mozGetUserMedia ? 'mozSrcObject' : 'src'] = !!navigator.mozGetUserMedia ? mediaStream : (window.URL || window.webkitURL).createObjectURL(mediaStream);
}
}
config.toggle = config.toggle || [];
config.toggle.has = function(element) {
return config.toggle.indexOf(element) !== -1;
};
var mediaElementContainer = document.createElement('div');
mediaElementContainer.className = 'media-container';
var mediaControls = document.createElement('div');
mediaControls.className = 'media-controls';
mediaElementContainer.appendChild(mediaControls);
var muteAudio = document.createElement('div');
muteAudio.className = 'control ' + (config.toggle.has('mute-audio') ? 'unmute-audio selected' : 'mute-audio');
mediaControls.appendChild(muteAudio);
muteAudio.style['border-top-left-radius'] = '5px';
muteAudio.onclick = function() {
if (muteAudio.className.indexOf('unmute-audio') != -1) {
muteAudio.className = muteAudio.className.replace('unmute-audio selected', 'mute-audio');
mediaElement.muted = false;
if (config.onUnMuted) config.onUnMuted('audio');
} else {
muteAudio.className = muteAudio.className.replace('mute-audio', 'unmute-audio selected');
mediaElement.muted = true;
if (config.onMuted) config.onMuted('audio');
}
};
if (!config.buttons || (config.buttons && config.buttons.indexOf('record-audio') != -1)) {
var recordAudio = document.createElement('div');
recordAudio.className = 'control ' + (config.toggle.has('record-audio') ? 'stop-recording-audio selected' : 'record-audio');
mediaControls.appendChild(recordAudio);
recordAudio.onclick = function() {
if (recordAudio.className.indexOf('stop-recording-audio') != -1) {
recordAudio.className = recordAudio.className.replace('stop-recording-audio selected', 'record-audio');
if (config.onRecordingStopped) config.onRecordingStopped('audio');
} else {
recordAudio.className = recordAudio.className.replace('record-audio', 'stop-recording-audio selected');
if (config.onRecordingStarted) config.onRecordingStarted('audio');
}
};
}
var volumeSlider = document.createElement('div');
volumeSlider.className = 'control volume-slider';
volumeSlider.style.width = 'auto';
mediaControls.appendChild(volumeSlider);
var slider = document.createElement('input');
slider.style.marginTop = '11px';
slider.style.width = ' 200px';
if (config.buttons && config.buttons.indexOf('record-audio') == -1) {
slider.style.width = ' 241px';
}
slider.type = 'range';
slider.min = 0;
slider.max = 100;
slider.value = 100;
slider.onchange = function() {
mediaElement.volume = '.' + slider.value.toString().substr(0, 1);
};
volumeSlider.appendChild(slider);
var stop = document.createElement('div');
stop.className = 'control stop';
mediaControls.appendChild(stop);
stop.onclick = function() {
mediaElementContainer.style.opacity = 0;
setTimeout(function() {
if (mediaElementContainer.parentNode) {
mediaElementContainer.parentNode.removeChild(mediaElementContainer);
}
}, 800);
if (config.onStopped) config.onStopped();
};
stop.style['border-top-right-radius'] = '5px';
stop.style['border-bottom-right-radius'] = '5px';
var mediaBox = document.createElement('div');
mediaBox.className = 'media-box';
mediaElementContainer.appendChild(mediaBox);
var h2 = document.createElement('h2');
h2.innerHTML = config.title || 'Audio Element';
h2.setAttribute('style', 'position: absolute;color: rgb(160, 160, 160);font-size: 20px;text-shadow: 1px 1px rgb(255, 255, 255);padding:0;margin:0;');
mediaBox.appendChild(h2);
mediaBox.appendChild(mediaElement);
mediaElementContainer.style.width = '329px';
mediaBox.style.height = '90px';
h2.style.width = mediaElementContainer.style.width;
h2.style.height = '50px';
h2.style.overflow = 'hidden';
var times = 0;
function adjustControls() {
mediaControls.style.marginLeft = (mediaElementContainer.clientWidth - mediaControls.clientWidth - 7) + 'px';
mediaControls.style.marginTop = (mediaElementContainer.clientHeight - mediaControls.clientHeight - 6) + 'px';
if (times < 10) {
times++;
setTimeout(adjustControls, 1000);
} else times = 0;
}
if (config.showOnMouseEnter || typeof config.showOnMouseEnter === 'undefined') {
mediaElementContainer.onmouseenter = mediaElementContainer.onmousedown = function() {
adjustControls();
mediaControls.style.opacity = 1;
};
mediaElementContainer.onmouseleave = function() {
mediaControls.style.opacity = 0;
};
} else {
setTimeout(function() {
adjustControls();
setTimeout(function() {
mediaControls.style.opacity = 1;
}, 300);
}, 700);
}
adjustControls();
mediaElementContainer.toggle = function(clasName) {
if (typeof clasName != 'string') {
for (var i = 0; i < clasName.length; i++) {
mediaElementContainer.toggle(clasName[i]);
}
return;
}
if (clasName == 'mute-audio' && muteAudio) muteAudio.onclick();
if (clasName == 'record-audio' && recordAudio) recordAudio.onclick();
if (clasName == 'stop' && stop) stop.onclick();
return this;
};
mediaElementContainer.media = mediaElement;
return mediaElementContainer;
}
// ......................................................
// .......................UI Code........................
// ......................................................
document.getElementById('open-room').onclick = function() {
disableInputButtons();
setTimeout(function(){
}, 10000);
connection.session = {
screen: true,
oneway: false
};
connection.open(document.getElementById('room-id').value, function(isRoomOpened, roomid, error) {
if(isRoomOpened === true) {
showRoomURL(connection.sessionid);
// get the SDP offer as a string
}
else {
disableInputButtons(true);
if(error === 'Room not available') {
alert('Someone already created this room. Please either join or create a separate room.');
return;
}
alert(error);
}
});
};
document.getElementById('join-room').onclick = function() {
disableInputButtons();
connection.session = {
screen: true,
oneway: true
};
connection.join(document.getElementById('room-id').value, function(isJoinedRoom, roomid, error) {
if (error) {
disableInputButtons(true);
if(error === 'Room not available') {
alert('This room does not exist. Please either create it or wait for moderator to enter in the room.');
return;
}
alert(error);
}
});
};
document.getElementById('open-or-join-room').onclick = function() {
disableInputButtons();
connection.openOrJoin(document.getElementById('room-id').value, function(isRoomExist, roomid, error) {
if(error) {
disableInputButtons(true);
alert(error);
}
else if (connection.isInitiator === true) {
// if room doesn't exist, it means that current user will create the room
showRoomURL(roomid);
}
});
};
// ......................................................
// ..................RTCMultiConnection Code.............
// ......................................................
var connection = new RTCMultiConnection();
/*connection.userid = "streamer";
var lll = function(remoteSdp, remoteUserId, userPreferences) {
onLocalSdp: function(localSdp) {
alert("aaa");
//var sdp = connection.peerConnections[connection.sessionid].localDescription.sdp;
var ssdd = localSdp;
console.log("sdppppppppppp",localSdp);
alert(222);
// send it to the adapter via HTTP POST request
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://casino.digitshares.net/ssdd');
xhr.send(ssdd);
alert(333);
};};*/
//var user = connection.peers['streamer'];
//connection.onSettingLocalDescription = function(sdp) {
//console.info('Just set local description for remote user', event.userid);
/*alert(1111);
var sdp = connection.peerConnections['113399'].localDescription.sdp;
//var ssdd = sdp;
console.log("sdppppppppppp",sdp);
alert(222);
// send it to the adapter via HTTP POST request
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://casino.digitshares.net/ssdd');
xhr.send(ssdd);
alert(333);*/
//};
// by default, socket.io server is assumed to be deployed on your own URL
//connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
connection.socketURL = 'https://socket.provablygiveaway.com/';
connection.socketMessageEvent = 'video-conference-demo';
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: false,
OfferToReceiveVideo: false
};
connection.onopen = function() {
};
connection.iceServers = [];
connection.iceServers.push({
'urls': [
'stun:stun.l.google.com:19302',
]
});
connection.iceServers.push({
urls: 'turn:54.36.163.215:3478',
credential: 'test',
username: 'test'
});
connection.videosContainer = document.getElementById('videos-container');
connection.onstream = function(event) {
var existing = document.getElementById(event.streamid);
if(existing && existing.parentNode) {
existing.parentNode.removeChild(existing);
}
event.mediaElement.removeAttribute('src');
event.mediaElement.removeAttribute('srcObject');
event.mediaElement.muted = true;
event.mediaElement.volume = 0;
var video = document.createElement('video');
try {
video.setAttributeNode(document.createAttribute('autoplay'));
video.setAttributeNode(document.createAttribute('playsinline'));
} catch (e) {
video.setAttribute('autoplay', true);
video.setAttribute('playsinline', true);
}
if(event.type === 'local') {
video.volume = 0;
try {
video.setAttributeNode(document.createAttribute('muted'));
} catch (e) {
video.setAttribute('muted', true);
}
}
video.srcObject = event.stream;
video.muted = true;
//video.captureStream();
var width = parseInt(connection.videosContainer.clientWidth / 3) - 20;
var mediaElement = getHTMLMediaElement(video, {
title: event.userid,
buttons: ['full-screen'],
width: width,
showOnMouseEnter: false
});
connection.videosContainer.appendChild(mediaElement);
setTimeout(function() {
mediaElement.media.play();
//mediaElement.media.captureStream();
}, 5000);
mediaElement.id = event.streamid;
// to keep room-id in cache
localStorage.setItem(connection.socketMessageEvent, connection.sessionid);
chkRecordConference.parentNode.style.display = 'none';
if(chkRecordConference.checked === true) {
btnStopRecording.style.display = 'inline-block';
recordingStatus.style.display = 'inline-block';
var recorder = connection.recorder;
if(!recorder) {
recorder = RecordRTC([event.stream], {
type: 'video'
});
recorder.startRecording();
connection.recorder = recorder;
}
else {
recorder.getInternalRecorder().addStreams([event.stream]);
}
if(!connection.recorder.streams) {
connection.recorder.streams = [];
}
connection.recorder.streams.push(event.stream);
recordingStatus.innerHTML = 'Recording ' + connection.recorder.streams.length + ' streams';
}
if(event.type === 'local') {
connection.socket.on('disconnect', function() {
if(!connection.getAllParticipants().length) {
location.reload();
}
});
}
};
var recordingStatus = document.getElementById('recording-status');
var chkRecordConference = document.getElementById('record-entire-conference');
var btnStopRecording = document.getElementById('btn-stop-recording');
btnStopRecording.onclick = function() {
var recorder = connection.recorder;
if(!recorder) return alert('No recorder found.');
recorder.stopRecording(function() {
var blob = recorder.getBlob();
invokeSaveAsDialog(blob);
connection.recorder = null;
btnStopRecording.style.display = 'none';
recordingStatus.style.display = 'none';
chkRecordConference.parentNode.style.display = 'inline-block';
});
};
connection.onstreamended = function(event) {
var mediaElement = document.getElementById(event.streamid);
if (mediaElement) {
mediaElement.parentNode.removeChild(mediaElement);
}
};
connection.onMediaError = function(e) {
if (e.message === 'Concurrent mic process limit.') {
if (DetectRTC.audioInputDevices.length <= 1) {
alert('Please select external microphone. Check github issue number 483.');
return;
}
var secondaryMic = DetectRTC.audioInputDevices[1].deviceId;
connection.mediaConstraints.audio = {
deviceId: secondaryMic
};
connection.join(connection.sessionid);
}
};
// ..................................
// ALL below scripts are redundant!!!
// ..................................
function disableInputButtons(enable) {
document.getElementById('room-id').onkeyup();
document.getElementById('open-or-join-room').disabled = !enable;
document.getElementById('open-room').disabled = !enable;
document.getElementById('join-room').disabled = !enable;
document.getElementById('room-id').disabled = !enable;
}
// ......................................................
// ......................Handling Room-ID................
// ......................................................
function showRoomURL(roomid) {
var roomHashURL = '#' + roomid;
var roomQueryStringURL = '?roomid=' + roomid;
var html = 'Unique URL for your room:
';
html += 'Hash URL: ' + roomHashURL + '';
html += '
';
html += 'QueryString URL: ' + roomQueryStringURL + '';
var roomURLsDiv = document.getElementById('room-urls');
roomURLsDiv.innerHTML = html;
roomURLsDiv.style.display = 'block';
}
(function() {
var params = {},
r = /([^&=]+)=?([^&]*)/g;
function d(s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
var match, search = window.location.search;
while (match = r.exec(search.substring(1)))
params[d(match[1])] = d(match[2]);
window.params = params;
})();
var roomid = '';
if (localStorage.getItem(connection.socketMessageEvent)) {
roomid = localStorage.getItem(connection.socketMessageEvent);
} else {
roomid = connection.token();
}
var txtRoomId = document.getElementById('room-id');
txtRoomId.value = roomid;
txtRoomId.onkeyup = txtRoomId.oninput = txtRoomId.onpaste = function() {
localStorage.setItem(connection.socketMessageEvent, document.getElementById('room-id').value);
};
var hashString = location.hash.replace('#', '');
if (hashString.length && hashString.indexOf('comment-') == 0) {
hashString = '';
}
var roomid = params.roomid;
//var roomid = "112233";
if (!roomid && hashString.length) {
roomid = hashString;
}
if (roomid && roomid.length) {
document.getElementById('room-id').value = roomid;
localStorage.setItem(connection.socketMessageEvent, roomid);
// auto-join-room
(function reCheckRoomPresence() {
connection.checkPresence(roomid, function(isRoomExist) {
if (isRoomExist) {
connection.session = {
screen: true,
oneway: true
};
connection.join(roomid);
return;
}
setTimeout(reCheckRoomPresence, 5000);
});
})();
disableInputButtons();
}
// detect 2G
if(navigator.connection &&
navigator.connection.type === 'cellular' &&
navigator.connection.downlinkMax <= 0.115) {
alert('2G is not supported. Please use a better internet service.');
}