chrome_shim.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. /*
  2. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3. *
  4. * Use of this source code is governed by a BSD-style license
  5. * that can be found in the LICENSE file in the root of the source
  6. * tree.
  7. */
  8. /* eslint-env node */
  9. 'use strict';
  10. Object.defineProperty(exports, "__esModule", {
  11. value: true
  12. });
  13. exports.fixNegotiationNeeded = fixNegotiationNeeded;
  14. exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack;
  15. exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;
  16. Object.defineProperty(exports, "shimGetDisplayMedia", {
  17. enumerable: true,
  18. get: function get() {
  19. return _getdisplaymedia.shimGetDisplayMedia;
  20. }
  21. });
  22. exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
  23. exports.shimGetStats = shimGetStats;
  24. Object.defineProperty(exports, "shimGetUserMedia", {
  25. enumerable: true,
  26. get: function get() {
  27. return _getusermedia.shimGetUserMedia;
  28. }
  29. });
  30. exports.shimMediaStream = shimMediaStream;
  31. exports.shimOnTrack = shimOnTrack;
  32. exports.shimPeerConnection = shimPeerConnection;
  33. exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;
  34. var utils = _interopRequireWildcard(require("../utils.js"));
  35. var _getusermedia = require("./getusermedia");
  36. var _getdisplaymedia = require("./getdisplaymedia");
  37. 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); }
  38. 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; }
  39. 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; }
  40. function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
  41. 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); }
  42. 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); }
  43. function shimMediaStream(window) {
  44. window.MediaStream = window.MediaStream || window.webkitMediaStream;
  45. }
  46. function shimOnTrack(window) {
  47. if (_typeof(window) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
  48. Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
  49. get: function get() {
  50. return this._ontrack;
  51. },
  52. set: function set(f) {
  53. if (this._ontrack) {
  54. this.removeEventListener('track', this._ontrack);
  55. }
  56. this.addEventListener('track', this._ontrack = f);
  57. },
  58. enumerable: true,
  59. configurable: true
  60. });
  61. var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
  62. window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
  63. var _this = this;
  64. if (!this._ontrackpoly) {
  65. this._ontrackpoly = function (e) {
  66. // onaddstream does not fire when a track is added to an existing
  67. // stream. But stream.onaddtrack is implemented so we use that.
  68. e.stream.addEventListener('addtrack', function (te) {
  69. var receiver;
  70. if (window.RTCPeerConnection.prototype.getReceivers) {
  71. receiver = _this.getReceivers().find(function (r) {
  72. return r.track && r.track.id === te.track.id;
  73. });
  74. } else {
  75. receiver = {
  76. track: te.track
  77. };
  78. }
  79. var event = new Event('track');
  80. event.track = te.track;
  81. event.receiver = receiver;
  82. event.transceiver = {
  83. receiver: receiver
  84. };
  85. event.streams = [e.stream];
  86. _this.dispatchEvent(event);
  87. });
  88. e.stream.getTracks().forEach(function (track) {
  89. var receiver;
  90. if (window.RTCPeerConnection.prototype.getReceivers) {
  91. receiver = _this.getReceivers().find(function (r) {
  92. return r.track && r.track.id === track.id;
  93. });
  94. } else {
  95. receiver = {
  96. track: track
  97. };
  98. }
  99. var event = new Event('track');
  100. event.track = track;
  101. event.receiver = receiver;
  102. event.transceiver = {
  103. receiver: receiver
  104. };
  105. event.streams = [e.stream];
  106. _this.dispatchEvent(event);
  107. });
  108. };
  109. this.addEventListener('addstream', this._ontrackpoly);
  110. }
  111. return origSetRemoteDescription.apply(this, arguments);
  112. };
  113. } else {
  114. // even if RTCRtpTransceiver is in window, it is only used and
  115. // emitted in unified-plan. Unfortunately this means we need
  116. // to unconditionally wrap the event.
  117. utils.wrapPeerConnectionEvent(window, 'track', function (e) {
  118. if (!e.transceiver) {
  119. Object.defineProperty(e, 'transceiver', {
  120. value: {
  121. receiver: e.receiver
  122. }
  123. });
  124. }
  125. return e;
  126. });
  127. }
  128. }
  129. function shimGetSendersWithDtmf(window) {
  130. // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
  131. if (_typeof(window) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) {
  132. var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) {
  133. return {
  134. track: track,
  135. get dtmf() {
  136. if (this._dtmf === undefined) {
  137. if (track.kind === 'audio') {
  138. this._dtmf = pc.createDTMFSender(track);
  139. } else {
  140. this._dtmf = null;
  141. }
  142. }
  143. return this._dtmf;
  144. },
  145. _pc: pc
  146. };
  147. };
  148. // augment addTrack when getSenders is not available.
  149. if (!window.RTCPeerConnection.prototype.getSenders) {
  150. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  151. this._senders = this._senders || [];
  152. return this._senders.slice(); // return a copy of the internal state.
  153. };
  154. var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  155. window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
  156. var sender = origAddTrack.apply(this, arguments);
  157. if (!sender) {
  158. sender = shimSenderWithDtmf(this, track);
  159. this._senders.push(sender);
  160. }
  161. return sender;
  162. };
  163. var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  164. window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
  165. origRemoveTrack.apply(this, arguments);
  166. var idx = this._senders.indexOf(sender);
  167. if (idx !== -1) {
  168. this._senders.splice(idx, 1);
  169. }
  170. };
  171. }
  172. var origAddStream = window.RTCPeerConnection.prototype.addStream;
  173. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  174. var _this2 = this;
  175. this._senders = this._senders || [];
  176. origAddStream.apply(this, [stream]);
  177. stream.getTracks().forEach(function (track) {
  178. _this2._senders.push(shimSenderWithDtmf(_this2, track));
  179. });
  180. };
  181. var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  182. window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
  183. var _this3 = this;
  184. this._senders = this._senders || [];
  185. origRemoveStream.apply(this, [stream]);
  186. stream.getTracks().forEach(function (track) {
  187. var sender = _this3._senders.find(function (s) {
  188. return s.track === track;
  189. });
  190. if (sender) {
  191. // remove sender
  192. _this3._senders.splice(_this3._senders.indexOf(sender), 1);
  193. }
  194. });
  195. };
  196. } else if (_typeof(window) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
  197. var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  198. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  199. var _this4 = this;
  200. var senders = origGetSenders.apply(this, []);
  201. senders.forEach(function (sender) {
  202. return sender._pc = _this4;
  203. });
  204. return senders;
  205. };
  206. Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  207. get: function get() {
  208. if (this._dtmf === undefined) {
  209. if (this.track.kind === 'audio') {
  210. this._dtmf = this._pc.createDTMFSender(this.track);
  211. } else {
  212. this._dtmf = null;
  213. }
  214. }
  215. return this._dtmf;
  216. }
  217. });
  218. }
  219. }
  220. function shimGetStats(window) {
  221. if (!window.RTCPeerConnection) {
  222. return;
  223. }
  224. var origGetStats = window.RTCPeerConnection.prototype.getStats;
  225. window.RTCPeerConnection.prototype.getStats = function getStats() {
  226. var _this5 = this;
  227. var _arguments = Array.prototype.slice.call(arguments),
  228. selector = _arguments[0],
  229. onSucc = _arguments[1],
  230. onErr = _arguments[2];
  231. // If selector is a function then we are in the old style stats so just
  232. // pass back the original getStats format to avoid breaking old users.
  233. if (arguments.length > 0 && typeof selector === 'function') {
  234. return origGetStats.apply(this, arguments);
  235. }
  236. // When spec-style getStats is supported, return those when called with
  237. // either no arguments or the selector argument is null.
  238. if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) {
  239. return origGetStats.apply(this, []);
  240. }
  241. var fixChromeStats_ = function fixChromeStats_(response) {
  242. var standardReport = {};
  243. var reports = response.result();
  244. reports.forEach(function (report) {
  245. var standardStats = {
  246. id: report.id,
  247. timestamp: report.timestamp,
  248. type: {
  249. localcandidate: 'local-candidate',
  250. remotecandidate: 'remote-candidate'
  251. }[report.type] || report.type
  252. };
  253. report.names().forEach(function (name) {
  254. standardStats[name] = report.stat(name);
  255. });
  256. standardReport[standardStats.id] = standardStats;
  257. });
  258. return standardReport;
  259. };
  260. // shim getStats with maplike support
  261. var makeMapStats = function makeMapStats(stats) {
  262. return new Map(Object.keys(stats).map(function (key) {
  263. return [key, stats[key]];
  264. }));
  265. };
  266. if (arguments.length >= 2) {
  267. var successCallbackWrapper_ = function successCallbackWrapper_(response) {
  268. onSucc(makeMapStats(fixChromeStats_(response)));
  269. };
  270. return origGetStats.apply(this, [successCallbackWrapper_, selector]);
  271. }
  272. // promise-support
  273. return new Promise(function (resolve, reject) {
  274. origGetStats.apply(_this5, [function (response) {
  275. resolve(makeMapStats(fixChromeStats_(response)));
  276. }, reject]);
  277. }).then(onSucc, onErr);
  278. };
  279. }
  280. function shimSenderReceiverGetStats(window) {
  281. if (!(_typeof(window) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {
  282. return;
  283. }
  284. // shim sender stats.
  285. if (!('getStats' in window.RTCRtpSender.prototype)) {
  286. var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  287. if (origGetSenders) {
  288. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  289. var _this6 = this;
  290. var senders = origGetSenders.apply(this, []);
  291. senders.forEach(function (sender) {
  292. return sender._pc = _this6;
  293. });
  294. return senders;
  295. };
  296. }
  297. var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  298. if (origAddTrack) {
  299. window.RTCPeerConnection.prototype.addTrack = function addTrack() {
  300. var sender = origAddTrack.apply(this, arguments);
  301. sender._pc = this;
  302. return sender;
  303. };
  304. }
  305. window.RTCRtpSender.prototype.getStats = function getStats() {
  306. var sender = this;
  307. return this._pc.getStats().then(function (result) {
  308. return (
  309. /* Note: this will include stats of all senders that
  310. * send a track with the same id as sender.track as
  311. * it is not possible to identify the RTCRtpSender.
  312. */
  313. utils.filterStats(result, sender.track, true)
  314. );
  315. });
  316. };
  317. }
  318. // shim receiver stats.
  319. if (!('getStats' in window.RTCRtpReceiver.prototype)) {
  320. var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
  321. if (origGetReceivers) {
  322. window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
  323. var _this7 = this;
  324. var receivers = origGetReceivers.apply(this, []);
  325. receivers.forEach(function (receiver) {
  326. return receiver._pc = _this7;
  327. });
  328. return receivers;
  329. };
  330. }
  331. utils.wrapPeerConnectionEvent(window, 'track', function (e) {
  332. e.receiver._pc = e.srcElement;
  333. return e;
  334. });
  335. window.RTCRtpReceiver.prototype.getStats = function getStats() {
  336. var receiver = this;
  337. return this._pc.getStats().then(function (result) {
  338. return utils.filterStats(result, receiver.track, false);
  339. });
  340. };
  341. }
  342. if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) {
  343. return;
  344. }
  345. // shim RTCPeerConnection.getStats(track).
  346. var origGetStats = window.RTCPeerConnection.prototype.getStats;
  347. window.RTCPeerConnection.prototype.getStats = function getStats() {
  348. if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) {
  349. var track = arguments[0];
  350. var sender;
  351. var receiver;
  352. var err;
  353. this.getSenders().forEach(function (s) {
  354. if (s.track === track) {
  355. if (sender) {
  356. err = true;
  357. } else {
  358. sender = s;
  359. }
  360. }
  361. });
  362. this.getReceivers().forEach(function (r) {
  363. if (r.track === track) {
  364. if (receiver) {
  365. err = true;
  366. } else {
  367. receiver = r;
  368. }
  369. }
  370. return r.track === track;
  371. });
  372. if (err || sender && receiver) {
  373. return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError'));
  374. } else if (sender) {
  375. return sender.getStats();
  376. } else if (receiver) {
  377. return receiver.getStats();
  378. }
  379. return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError'));
  380. }
  381. return origGetStats.apply(this, arguments);
  382. };
  383. }
  384. function shimAddTrackRemoveTrackWithNative(window) {
  385. // shim addTrack/removeTrack with native variants in order to make
  386. // the interactions with legacy getLocalStreams behave as in other browsers.
  387. // Keeps a mapping stream.id => [stream, rtpsenders...]
  388. window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
  389. var _this8 = this;
  390. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  391. return Object.keys(this._shimmedLocalStreams).map(function (streamId) {
  392. return _this8._shimmedLocalStreams[streamId][0];
  393. });
  394. };
  395. var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  396. window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
  397. if (!stream) {
  398. return origAddTrack.apply(this, arguments);
  399. }
  400. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  401. var sender = origAddTrack.apply(this, arguments);
  402. if (!this._shimmedLocalStreams[stream.id]) {
  403. this._shimmedLocalStreams[stream.id] = [stream, sender];
  404. } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
  405. this._shimmedLocalStreams[stream.id].push(sender);
  406. }
  407. return sender;
  408. };
  409. var origAddStream = window.RTCPeerConnection.prototype.addStream;
  410. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  411. var _this9 = this;
  412. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  413. stream.getTracks().forEach(function (track) {
  414. var alreadyExists = _this9.getSenders().find(function (s) {
  415. return s.track === track;
  416. });
  417. if (alreadyExists) {
  418. throw new DOMException('Track already exists.', 'InvalidAccessError');
  419. }
  420. });
  421. var existingSenders = this.getSenders();
  422. origAddStream.apply(this, arguments);
  423. var newSenders = this.getSenders().filter(function (newSender) {
  424. return existingSenders.indexOf(newSender) === -1;
  425. });
  426. this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
  427. };
  428. var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  429. window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
  430. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  431. delete this._shimmedLocalStreams[stream.id];
  432. return origRemoveStream.apply(this, arguments);
  433. };
  434. var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  435. window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
  436. var _this10 = this;
  437. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  438. if (sender) {
  439. Object.keys(this._shimmedLocalStreams).forEach(function (streamId) {
  440. var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender);
  441. if (idx !== -1) {
  442. _this10._shimmedLocalStreams[streamId].splice(idx, 1);
  443. }
  444. if (_this10._shimmedLocalStreams[streamId].length === 1) {
  445. delete _this10._shimmedLocalStreams[streamId];
  446. }
  447. });
  448. }
  449. return origRemoveTrack.apply(this, arguments);
  450. };
  451. }
  452. function shimAddTrackRemoveTrack(window, browserDetails) {
  453. if (!window.RTCPeerConnection) {
  454. return;
  455. }
  456. // shim addTrack and removeTrack.
  457. if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) {
  458. return shimAddTrackRemoveTrackWithNative(window);
  459. }
  460. // also shim pc.getLocalStreams when addTrack is shimmed
  461. // to return the original streams.
  462. var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams;
  463. window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
  464. var _this11 = this;
  465. var nativeStreams = origGetLocalStreams.apply(this);
  466. this._reverseStreams = this._reverseStreams || {};
  467. return nativeStreams.map(function (stream) {
  468. return _this11._reverseStreams[stream.id];
  469. });
  470. };
  471. var origAddStream = window.RTCPeerConnection.prototype.addStream;
  472. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  473. var _this12 = this;
  474. this._streams = this._streams || {};
  475. this._reverseStreams = this._reverseStreams || {};
  476. stream.getTracks().forEach(function (track) {
  477. var alreadyExists = _this12.getSenders().find(function (s) {
  478. return s.track === track;
  479. });
  480. if (alreadyExists) {
  481. throw new DOMException('Track already exists.', 'InvalidAccessError');
  482. }
  483. });
  484. // Add identity mapping for consistency with addTrack.
  485. // Unless this is being used with a stream from addTrack.
  486. if (!this._reverseStreams[stream.id]) {
  487. var newStream = new window.MediaStream(stream.getTracks());
  488. this._streams[stream.id] = newStream;
  489. this._reverseStreams[newStream.id] = stream;
  490. stream = newStream;
  491. }
  492. origAddStream.apply(this, [stream]);
  493. };
  494. var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  495. window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
  496. this._streams = this._streams || {};
  497. this._reverseStreams = this._reverseStreams || {};
  498. origRemoveStream.apply(this, [this._streams[stream.id] || stream]);
  499. delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id];
  500. delete this._streams[stream.id];
  501. };
  502. window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
  503. var _this13 = this;
  504. if (this.signalingState === 'closed') {
  505. throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
  506. }
  507. var streams = [].slice.call(arguments, 1);
  508. if (streams.length !== 1 || !streams[0].getTracks().find(function (t) {
  509. return t === track;
  510. })) {
  511. // this is not fully correct but all we can manage without
  512. // [[associated MediaStreams]] internal slot.
  513. throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError');
  514. }
  515. var alreadyExists = this.getSenders().find(function (s) {
  516. return s.track === track;
  517. });
  518. if (alreadyExists) {
  519. throw new DOMException('Track already exists.', 'InvalidAccessError');
  520. }
  521. this._streams = this._streams || {};
  522. this._reverseStreams = this._reverseStreams || {};
  523. var oldStream = this._streams[stream.id];
  524. if (oldStream) {
  525. // this is using odd Chrome behaviour, use with caution:
  526. // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
  527. // Note: we rely on the high-level addTrack/dtmf shim to
  528. // create the sender with a dtmf sender.
  529. oldStream.addTrack(track);
  530. // Trigger ONN async.
  531. Promise.resolve().then(function () {
  532. _this13.dispatchEvent(new Event('negotiationneeded'));
  533. });
  534. } else {
  535. var newStream = new window.MediaStream([track]);
  536. this._streams[stream.id] = newStream;
  537. this._reverseStreams[newStream.id] = stream;
  538. this.addStream(newStream);
  539. }
  540. return this.getSenders().find(function (s) {
  541. return s.track === track;
  542. });
  543. };
  544. // replace the internal stream id with the external one and
  545. // vice versa.
  546. function replaceInternalStreamId(pc, description) {
  547. var sdp = description.sdp;
  548. Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
  549. var externalStream = pc._reverseStreams[internalId];
  550. var internalStream = pc._streams[externalStream.id];
  551. sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id);
  552. });
  553. return new RTCSessionDescription({
  554. type: description.type,
  555. sdp: sdp
  556. });
  557. }
  558. function replaceExternalStreamId(pc, description) {
  559. var sdp = description.sdp;
  560. Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
  561. var externalStream = pc._reverseStreams[internalId];
  562. var internalStream = pc._streams[externalStream.id];
  563. sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id);
  564. });
  565. return new RTCSessionDescription({
  566. type: description.type,
  567. sdp: sdp
  568. });
  569. }
  570. ['createOffer', 'createAnswer'].forEach(function (method) {
  571. var nativeMethod = window.RTCPeerConnection.prototype[method];
  572. var methodObj = _defineProperty({}, method, function () {
  573. var _this14 = this;
  574. var args = arguments;
  575. var isLegacyCall = arguments.length && typeof arguments[0] === 'function';
  576. if (isLegacyCall) {
  577. return nativeMethod.apply(this, [function (description) {
  578. var desc = replaceInternalStreamId(_this14, description);
  579. args[0].apply(null, [desc]);
  580. }, function (err) {
  581. if (args[1]) {
  582. args[1].apply(null, err);
  583. }
  584. }, arguments[2]]);
  585. }
  586. return nativeMethod.apply(this, arguments).then(function (description) {
  587. return replaceInternalStreamId(_this14, description);
  588. });
  589. });
  590. window.RTCPeerConnection.prototype[method] = methodObj[method];
  591. });
  592. var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
  593. window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
  594. if (!arguments.length || !arguments[0].type) {
  595. return origSetLocalDescription.apply(this, arguments);
  596. }
  597. arguments[0] = replaceExternalStreamId(this, arguments[0]);
  598. return origSetLocalDescription.apply(this, arguments);
  599. };
  600. // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
  601. var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription');
  602. Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', {
  603. get: function get() {
  604. var description = origLocalDescription.get.apply(this);
  605. if (description.type === '') {
  606. return description;
  607. }
  608. return replaceInternalStreamId(this, description);
  609. }
  610. });
  611. window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
  612. var _this15 = this;
  613. if (this.signalingState === 'closed') {
  614. throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
  615. }
  616. // We can not yet check for sender instanceof RTCRtpSender
  617. // since we shim RTPSender. So we check if sender._pc is set.
  618. if (!sender._pc) {
  619. throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError');
  620. }
  621. var isLocal = sender._pc === this;
  622. if (!isLocal) {
  623. throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError');
  624. }
  625. // Search for the native stream the senders track belongs to.
  626. this._streams = this._streams || {};
  627. var stream;
  628. Object.keys(this._streams).forEach(function (streamid) {
  629. var hasTrack = _this15._streams[streamid].getTracks().find(function (track) {
  630. return sender.track === track;
  631. });
  632. if (hasTrack) {
  633. stream = _this15._streams[streamid];
  634. }
  635. });
  636. if (stream) {
  637. if (stream.getTracks().length === 1) {
  638. // if this is the last track of the stream, remove the stream. This
  639. // takes care of any shimmed _senders.
  640. this.removeStream(this._reverseStreams[stream.id]);
  641. } else {
  642. // relying on the same odd chrome behaviour as above.
  643. stream.removeTrack(sender.track);
  644. }
  645. this.dispatchEvent(new Event('negotiationneeded'));
  646. }
  647. };
  648. }
  649. function shimPeerConnection(window, browserDetails) {
  650. if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
  651. // very basic support for old versions.
  652. window.RTCPeerConnection = window.webkitRTCPeerConnection;
  653. }
  654. if (!window.RTCPeerConnection) {
  655. return;
  656. }
  657. // shim implicit creation of RTCSessionDescription/RTCIceCandidate
  658. if (browserDetails.version < 53) {
  659. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
  660. var nativeMethod = window.RTCPeerConnection.prototype[method];
  661. var methodObj = _defineProperty({}, method, function () {
  662. arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
  663. return nativeMethod.apply(this, arguments);
  664. });
  665. window.RTCPeerConnection.prototype[method] = methodObj[method];
  666. });
  667. }
  668. }
  669. // Attempt to fix ONN in plan-b mode.
  670. function fixNegotiationNeeded(window, browserDetails) {
  671. utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) {
  672. var pc = e.target;
  673. if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') {
  674. if (pc.signalingState !== 'stable') {
  675. return;
  676. }
  677. }
  678. return e;
  679. });
  680. }