699 lines
28 KiB
JavaScript
699 lines
28 KiB
JavaScript
/*
|
|
* 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.fixNegotiationNeeded = fixNegotiationNeeded;
|
|
exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack;
|
|
exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;
|
|
Object.defineProperty(exports, "shimGetDisplayMedia", {
|
|
enumerable: true,
|
|
get: function get() {
|
|
return _getdisplaymedia.shimGetDisplayMedia;
|
|
}
|
|
});
|
|
exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
|
|
exports.shimGetStats = shimGetStats;
|
|
Object.defineProperty(exports, "shimGetUserMedia", {
|
|
enumerable: true,
|
|
get: function get() {
|
|
return _getusermedia.shimGetUserMedia;
|
|
}
|
|
});
|
|
exports.shimMediaStream = shimMediaStream;
|
|
exports.shimOnTrack = shimOnTrack;
|
|
exports.shimPeerConnection = shimPeerConnection;
|
|
exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;
|
|
var utils = _interopRequireWildcard(require("../utils.js"));
|
|
var _getusermedia = require("./getusermedia");
|
|
var _getdisplaymedia = require("./getdisplaymedia");
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
|
|
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
|
|
function shimMediaStream(window) {
|
|
window.MediaStream = window.MediaStream || window.webkitMediaStream;
|
|
}
|
|
function shimOnTrack(window) {
|
|
if (_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;
|
|
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;
|
|
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) === '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) === '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) === '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;
|
|
var receiver;
|
|
var err;
|
|
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;
|
|
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;
|
|
});
|
|
}
|