You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

8285 lines
266KB

  1. var ZLMRTCClient = (function (exports) {
  2. 'use strict';
  3. const Events$1 = {
  4. WEBRTC_NOT_SUPPORT: 'WEBRTC_NOT_SUPPORT',
  5. WEBRTC_ICE_CANDIDATE_ERROR: 'WEBRTC_ICE_CANDIDATE_ERROR',
  6. WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED: 'WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED',
  7. WEBRTC_ON_REMOTE_STREAMS: 'WEBRTC_ON_REMOTE_STREAMS',
  8. WEBRTC_ON_LOCAL_STREAM: 'WEBRTC_ON_LOCAL_STREAM',
  9. WEBRTC_ON_CONNECTION_STATE_CHANGE: 'WEBRTC_ON_CONNECTION_STATE_CHANGE',
  10. WEBRTC_ON_DATA_CHANNEL_OPEN: 'WEBRTC_ON_DATA_CHANNEL_OPEN',
  11. WEBRTC_ON_DATA_CHANNEL_CLOSE: 'WEBRTC_ON_DATA_CHANNEL_CLOSE',
  12. WEBRTC_ON_DATA_CHANNEL_ERR: 'WEBRTC_ON_DATA_CHANNEL_ERR',
  13. WEBRTC_ON_DATA_CHANNEL_MSG: 'WEBRTC_ON_DATA_CHANNEL_MSG',
  14. CAPTURE_STREAM_FAILED: 'CAPTURE_STREAM_FAILED'
  15. };
  16. const VERSION$1 = '1.0.1';
  17. const BUILD_DATE = 'Mon Jul 04 2022 19:50:55 GMT+0800 (China Standard Time)';
  18. // Copyright (C) <2018> Intel Corporation
  19. //
  20. // SPDX-License-Identifier: Apache-2.0
  21. // eslint-disable-next-line require-jsdoc
  22. function isFirefox() {
  23. return window.navigator.userAgent.match('Firefox') !== null;
  24. } // eslint-disable-next-line require-jsdoc
  25. function isChrome() {
  26. return window.navigator.userAgent.match('Chrome') !== null;
  27. } // eslint-disable-next-line require-jsdoc
  28. function isEdge() {
  29. return window.navigator.userAgent.match(/Edge\/(\d+).(\d+)$/) !== null;
  30. } // eslint-disable-next-line require-jsdoc
  31. // Copyright (C) <2018> Intel Corporation
  32. /**
  33. * @class AudioSourceInfo
  34. * @classDesc Source info about an audio track. Values: 'mic', 'screen-cast', 'file', 'mixed'.
  35. * @memberOf Owt.Base
  36. * @readonly
  37. * @enum {string}
  38. */
  39. const AudioSourceInfo = {
  40. MIC: 'mic',
  41. SCREENCAST: 'screen-cast',
  42. FILE: 'file',
  43. MIXED: 'mixed'
  44. };
  45. /**
  46. * @class VideoSourceInfo
  47. * @classDesc Source info about a video track. Values: 'camera', 'screen-cast', 'file', 'mixed'.
  48. * @memberOf Owt.Base
  49. * @readonly
  50. * @enum {string}
  51. */
  52. const VideoSourceInfo = {
  53. CAMERA: 'camera',
  54. SCREENCAST: 'screen-cast',
  55. FILE: 'file',
  56. MIXED: 'mixed'
  57. };
  58. /**
  59. * @class TrackKind
  60. * @classDesc Kind of a track. Values: 'audio' for audio track, 'video' for video track, 'av' for both audio and video tracks.
  61. * @memberOf Owt.Base
  62. * @readonly
  63. * @enum {string}
  64. */
  65. const TrackKind = {
  66. /**
  67. * Audio tracks.
  68. * @type string
  69. */
  70. AUDIO: 'audio',
  71. /**
  72. * Video tracks.
  73. * @type string
  74. */
  75. VIDEO: 'video',
  76. /**
  77. * Both audio and video tracks.
  78. * @type string
  79. */
  80. AUDIO_AND_VIDEO: 'av'
  81. };
  82. /**
  83. * @class Resolution
  84. * @memberOf Owt.Base
  85. * @classDesc The Resolution defines the size of a rectangle.
  86. * @constructor
  87. * @param {number} width
  88. * @param {number} height
  89. */
  90. class Resolution {
  91. // eslint-disable-next-line require-jsdoc
  92. constructor(width, height) {
  93. /**
  94. * @member {number} width
  95. * @instance
  96. * @memberof Owt.Base.Resolution
  97. */
  98. this.width = width;
  99. /**
  100. * @member {number} height
  101. * @instance
  102. * @memberof Owt.Base.Resolution
  103. */
  104. this.height = height;
  105. }
  106. }
  107. /*
  108. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  109. *
  110. * Use of this source code is governed by a BSD-style license
  111. * that can be found in the LICENSE file in the root of the source
  112. * tree.
  113. */
  114. let logDisabled_ = true;
  115. let deprecationWarnings_ = true;
  116. /**
  117. * Extract browser version out of the provided user agent string.
  118. *
  119. * @param {!string} uastring userAgent string.
  120. * @param {!string} expr Regular expression used as match criteria.
  121. * @param {!number} pos position in the version string to be returned.
  122. * @return {!number} browser version.
  123. */
  124. function extractVersion(uastring, expr, pos) {
  125. const match = uastring.match(expr);
  126. return match && match.length >= pos && parseInt(match[pos], 10);
  127. }
  128. // Wraps the peerconnection event eventNameToWrap in a function
  129. // which returns the modified event object (or false to prevent
  130. // the event).
  131. function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
  132. if (!window.RTCPeerConnection) {
  133. return;
  134. }
  135. const proto = window.RTCPeerConnection.prototype;
  136. const nativeAddEventListener = proto.addEventListener;
  137. proto.addEventListener = function(nativeEventName, cb) {
  138. if (nativeEventName !== eventNameToWrap) {
  139. return nativeAddEventListener.apply(this, arguments);
  140. }
  141. const wrappedCallback = (e) => {
  142. const modifiedEvent = wrapper(e);
  143. if (modifiedEvent) {
  144. if (cb.handleEvent) {
  145. cb.handleEvent(modifiedEvent);
  146. } else {
  147. cb(modifiedEvent);
  148. }
  149. }
  150. };
  151. this._eventMap = this._eventMap || {};
  152. if (!this._eventMap[eventNameToWrap]) {
  153. this._eventMap[eventNameToWrap] = new Map();
  154. }
  155. this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
  156. return nativeAddEventListener.apply(this, [nativeEventName,
  157. wrappedCallback]);
  158. };
  159. const nativeRemoveEventListener = proto.removeEventListener;
  160. proto.removeEventListener = function(nativeEventName, cb) {
  161. if (nativeEventName !== eventNameToWrap || !this._eventMap
  162. || !this._eventMap[eventNameToWrap]) {
  163. return nativeRemoveEventListener.apply(this, arguments);
  164. }
  165. if (!this._eventMap[eventNameToWrap].has(cb)) {
  166. return nativeRemoveEventListener.apply(this, arguments);
  167. }
  168. const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
  169. this._eventMap[eventNameToWrap].delete(cb);
  170. if (this._eventMap[eventNameToWrap].size === 0) {
  171. delete this._eventMap[eventNameToWrap];
  172. }
  173. if (Object.keys(this._eventMap).length === 0) {
  174. delete this._eventMap;
  175. }
  176. return nativeRemoveEventListener.apply(this, [nativeEventName,
  177. unwrappedCb]);
  178. };
  179. Object.defineProperty(proto, 'on' + eventNameToWrap, {
  180. get() {
  181. return this['_on' + eventNameToWrap];
  182. },
  183. set(cb) {
  184. if (this['_on' + eventNameToWrap]) {
  185. this.removeEventListener(eventNameToWrap,
  186. this['_on' + eventNameToWrap]);
  187. delete this['_on' + eventNameToWrap];
  188. }
  189. if (cb) {
  190. this.addEventListener(eventNameToWrap,
  191. this['_on' + eventNameToWrap] = cb);
  192. }
  193. },
  194. enumerable: true,
  195. configurable: true
  196. });
  197. }
  198. function disableLog(bool) {
  199. if (typeof bool !== 'boolean') {
  200. return new Error('Argument type: ' + typeof bool +
  201. '. Please use a boolean.');
  202. }
  203. logDisabled_ = bool;
  204. return (bool) ? 'adapter.js logging disabled' :
  205. 'adapter.js logging enabled';
  206. }
  207. /**
  208. * Disable or enable deprecation warnings
  209. * @param {!boolean} bool set to true to disable warnings.
  210. */
  211. function disableWarnings(bool) {
  212. if (typeof bool !== 'boolean') {
  213. return new Error('Argument type: ' + typeof bool +
  214. '. Please use a boolean.');
  215. }
  216. deprecationWarnings_ = !bool;
  217. return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
  218. }
  219. function log$1() {
  220. if (typeof window === 'object') {
  221. if (logDisabled_) {
  222. return;
  223. }
  224. if (typeof console !== 'undefined' && typeof console.log === 'function') {
  225. console.log.apply(console, arguments);
  226. }
  227. }
  228. }
  229. /**
  230. * Shows a deprecation warning suggesting the modern and spec-compatible API.
  231. */
  232. function deprecated(oldMethod, newMethod) {
  233. if (!deprecationWarnings_) {
  234. return;
  235. }
  236. console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
  237. ' instead.');
  238. }
  239. /**
  240. * Browser detector.
  241. *
  242. * @return {object} result containing browser and version
  243. * properties.
  244. */
  245. function detectBrowser(window) {
  246. // Returned result object.
  247. const result = {browser: null, version: null};
  248. // Fail early if it's not a browser
  249. if (typeof window === 'undefined' || !window.navigator) {
  250. result.browser = 'Not a browser.';
  251. return result;
  252. }
  253. const {navigator} = window;
  254. if (navigator.mozGetUserMedia) { // Firefox.
  255. result.browser = 'firefox';
  256. result.version = extractVersion(navigator.userAgent,
  257. /Firefox\/(\d+)\./, 1);
  258. } else if (navigator.webkitGetUserMedia ||
  259. (window.isSecureContext === false && window.webkitRTCPeerConnection &&
  260. !window.RTCIceGatherer)) {
  261. // Chrome, Chromium, Webview, Opera.
  262. // Version matches Chrome/WebRTC version.
  263. // Chrome 74 removed webkitGetUserMedia on http as well so we need the
  264. // more complicated fallback to webkitRTCPeerConnection.
  265. result.browser = 'chrome';
  266. result.version = extractVersion(navigator.userAgent,
  267. /Chrom(e|ium)\/(\d+)\./, 2);
  268. } else if (navigator.mediaDevices &&
  269. navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
  270. result.browser = 'edge';
  271. result.version = extractVersion(navigator.userAgent,
  272. /Edge\/(\d+).(\d+)$/, 2);
  273. } else if (window.RTCPeerConnection &&
  274. navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { // Safari.
  275. result.browser = 'safari';
  276. result.version = extractVersion(navigator.userAgent,
  277. /AppleWebKit\/(\d+)\./, 1);
  278. result.supportsUnifiedPlan = window.RTCRtpTransceiver &&
  279. 'currentDirection' in window.RTCRtpTransceiver.prototype;
  280. } else { // Default fallthrough: not supported.
  281. result.browser = 'Not a supported browser.';
  282. return result;
  283. }
  284. return result;
  285. }
  286. /**
  287. * Checks if something is an object.
  288. *
  289. * @param {*} val The something you want to check.
  290. * @return true if val is an object, false otherwise.
  291. */
  292. function isObject$1(val) {
  293. return Object.prototype.toString.call(val) === '[object Object]';
  294. }
  295. /**
  296. * Remove all empty objects and undefined values
  297. * from a nested object -- an enhanced and vanilla version
  298. * of Lodash's `compact`.
  299. */
  300. function compactObject(data) {
  301. if (!isObject$1(data)) {
  302. return data;
  303. }
  304. return Object.keys(data).reduce(function(accumulator, key) {
  305. const isObj = isObject$1(data[key]);
  306. const value = isObj ? compactObject(data[key]) : data[key];
  307. const isEmptyObject = isObj && !Object.keys(value).length;
  308. if (value === undefined || isEmptyObject) {
  309. return accumulator;
  310. }
  311. return Object.assign(accumulator, {[key]: value});
  312. }, {});
  313. }
  314. /* iterates the stats graph recursively. */
  315. function walkStats(stats, base, resultSet) {
  316. if (!base || resultSet.has(base.id)) {
  317. return;
  318. }
  319. resultSet.set(base.id, base);
  320. Object.keys(base).forEach(name => {
  321. if (name.endsWith('Id')) {
  322. walkStats(stats, stats.get(base[name]), resultSet);
  323. } else if (name.endsWith('Ids')) {
  324. base[name].forEach(id => {
  325. walkStats(stats, stats.get(id), resultSet);
  326. });
  327. }
  328. });
  329. }
  330. /* filter getStats for a sender/receiver track. */
  331. function filterStats(result, track, outbound) {
  332. const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
  333. const filteredResult = new Map();
  334. if (track === null) {
  335. return filteredResult;
  336. }
  337. const trackStats = [];
  338. result.forEach(value => {
  339. if (value.type === 'track' &&
  340. value.trackIdentifier === track.id) {
  341. trackStats.push(value);
  342. }
  343. });
  344. trackStats.forEach(trackStat => {
  345. result.forEach(stats => {
  346. if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
  347. walkStats(result, stats, filteredResult);
  348. }
  349. });
  350. });
  351. return filteredResult;
  352. }
  353. /*
  354. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  355. *
  356. * Use of this source code is governed by a BSD-style license
  357. * that can be found in the LICENSE file in the root of the source
  358. * tree.
  359. */
  360. const logging = log$1;
  361. function shimGetUserMedia$3(window, browserDetails) {
  362. const navigator = window && window.navigator;
  363. if (!navigator.mediaDevices) {
  364. return;
  365. }
  366. const constraintsToChrome_ = function(c) {
  367. if (typeof c !== 'object' || c.mandatory || c.optional) {
  368. return c;
  369. }
  370. const cc = {};
  371. Object.keys(c).forEach(key => {
  372. if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
  373. return;
  374. }
  375. const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
  376. if (r.exact !== undefined && typeof r.exact === 'number') {
  377. r.min = r.max = r.exact;
  378. }
  379. const oldname_ = function(prefix, name) {
  380. if (prefix) {
  381. return prefix + name.charAt(0).toUpperCase() + name.slice(1);
  382. }
  383. return (name === 'deviceId') ? 'sourceId' : name;
  384. };
  385. if (r.ideal !== undefined) {
  386. cc.optional = cc.optional || [];
  387. let oc = {};
  388. if (typeof r.ideal === 'number') {
  389. oc[oldname_('min', key)] = r.ideal;
  390. cc.optional.push(oc);
  391. oc = {};
  392. oc[oldname_('max', key)] = r.ideal;
  393. cc.optional.push(oc);
  394. } else {
  395. oc[oldname_('', key)] = r.ideal;
  396. cc.optional.push(oc);
  397. }
  398. }
  399. if (r.exact !== undefined && typeof r.exact !== 'number') {
  400. cc.mandatory = cc.mandatory || {};
  401. cc.mandatory[oldname_('', key)] = r.exact;
  402. } else {
  403. ['min', 'max'].forEach(mix => {
  404. if (r[mix] !== undefined) {
  405. cc.mandatory = cc.mandatory || {};
  406. cc.mandatory[oldname_(mix, key)] = r[mix];
  407. }
  408. });
  409. }
  410. });
  411. if (c.advanced) {
  412. cc.optional = (cc.optional || []).concat(c.advanced);
  413. }
  414. return cc;
  415. };
  416. const shimConstraints_ = function(constraints, func) {
  417. if (browserDetails.version >= 61) {
  418. return func(constraints);
  419. }
  420. constraints = JSON.parse(JSON.stringify(constraints));
  421. if (constraints && typeof constraints.audio === 'object') {
  422. const remap = function(obj, a, b) {
  423. if (a in obj && !(b in obj)) {
  424. obj[b] = obj[a];
  425. delete obj[a];
  426. }
  427. };
  428. constraints = JSON.parse(JSON.stringify(constraints));
  429. remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
  430. remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
  431. constraints.audio = constraintsToChrome_(constraints.audio);
  432. }
  433. if (constraints && typeof constraints.video === 'object') {
  434. // Shim facingMode for mobile & surface pro.
  435. let face = constraints.video.facingMode;
  436. face = face && ((typeof face === 'object') ? face : {ideal: face});
  437. const getSupportedFacingModeLies = browserDetails.version < 66;
  438. if ((face && (face.exact === 'user' || face.exact === 'environment' ||
  439. face.ideal === 'user' || face.ideal === 'environment')) &&
  440. !(navigator.mediaDevices.getSupportedConstraints &&
  441. navigator.mediaDevices.getSupportedConstraints().facingMode &&
  442. !getSupportedFacingModeLies)) {
  443. delete constraints.video.facingMode;
  444. let matches;
  445. if (face.exact === 'environment' || face.ideal === 'environment') {
  446. matches = ['back', 'rear'];
  447. } else if (face.exact === 'user' || face.ideal === 'user') {
  448. matches = ['front'];
  449. }
  450. if (matches) {
  451. // Look for matches in label, or use last cam for back (typical).
  452. return navigator.mediaDevices.enumerateDevices()
  453. .then(devices => {
  454. devices = devices.filter(d => d.kind === 'videoinput');
  455. let dev = devices.find(d => matches.some(match =>
  456. d.label.toLowerCase().includes(match)));
  457. if (!dev && devices.length && matches.includes('back')) {
  458. dev = devices[devices.length - 1]; // more likely the back cam
  459. }
  460. if (dev) {
  461. constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
  462. {ideal: dev.deviceId};
  463. }
  464. constraints.video = constraintsToChrome_(constraints.video);
  465. logging('chrome: ' + JSON.stringify(constraints));
  466. return func(constraints);
  467. });
  468. }
  469. }
  470. constraints.video = constraintsToChrome_(constraints.video);
  471. }
  472. logging('chrome: ' + JSON.stringify(constraints));
  473. return func(constraints);
  474. };
  475. const shimError_ = function(e) {
  476. if (browserDetails.version >= 64) {
  477. return e;
  478. }
  479. return {
  480. name: {
  481. PermissionDeniedError: 'NotAllowedError',
  482. PermissionDismissedError: 'NotAllowedError',
  483. InvalidStateError: 'NotAllowedError',
  484. DevicesNotFoundError: 'NotFoundError',
  485. ConstraintNotSatisfiedError: 'OverconstrainedError',
  486. TrackStartError: 'NotReadableError',
  487. MediaDeviceFailedDueToShutdown: 'NotAllowedError',
  488. MediaDeviceKillSwitchOn: 'NotAllowedError',
  489. TabCaptureError: 'AbortError',
  490. ScreenCaptureError: 'AbortError',
  491. DeviceCaptureError: 'AbortError'
  492. }[e.name] || e.name,
  493. message: e.message,
  494. constraint: e.constraint || e.constraintName,
  495. toString() {
  496. return this.name + (this.message && ': ') + this.message;
  497. }
  498. };
  499. };
  500. const getUserMedia_ = function(constraints, onSuccess, onError) {
  501. shimConstraints_(constraints, c => {
  502. navigator.webkitGetUserMedia(c, onSuccess, e => {
  503. if (onError) {
  504. onError(shimError_(e));
  505. }
  506. });
  507. });
  508. };
  509. navigator.getUserMedia = getUserMedia_.bind(navigator);
  510. // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
  511. // function which returns a Promise, it does not accept spec-style
  512. // constraints.
  513. if (navigator.mediaDevices.getUserMedia) {
  514. const origGetUserMedia = navigator.mediaDevices.getUserMedia.
  515. bind(navigator.mediaDevices);
  516. navigator.mediaDevices.getUserMedia = function(cs) {
  517. return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {
  518. if (c.audio && !stream.getAudioTracks().length ||
  519. c.video && !stream.getVideoTracks().length) {
  520. stream.getTracks().forEach(track => {
  521. track.stop();
  522. });
  523. throw new DOMException('', 'NotFoundError');
  524. }
  525. return stream;
  526. }, e => Promise.reject(shimError_(e))));
  527. };
  528. }
  529. }
  530. /*
  531. * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
  532. *
  533. * Use of this source code is governed by a BSD-style license
  534. * that can be found in the LICENSE file in the root of the source
  535. * tree.
  536. */
  537. function shimGetDisplayMedia$2(window, getSourceId) {
  538. if (window.navigator.mediaDevices &&
  539. 'getDisplayMedia' in window.navigator.mediaDevices) {
  540. return;
  541. }
  542. if (!(window.navigator.mediaDevices)) {
  543. return;
  544. }
  545. // getSourceId is a function that returns a promise resolving with
  546. // the sourceId of the screen/window/tab to be shared.
  547. if (typeof getSourceId !== 'function') {
  548. console.error('shimGetDisplayMedia: getSourceId argument is not ' +
  549. 'a function');
  550. return;
  551. }
  552. window.navigator.mediaDevices.getDisplayMedia =
  553. function getDisplayMedia(constraints) {
  554. return getSourceId(constraints)
  555. .then(sourceId => {
  556. const widthSpecified = constraints.video && constraints.video.width;
  557. const heightSpecified = constraints.video &&
  558. constraints.video.height;
  559. const frameRateSpecified = constraints.video &&
  560. constraints.video.frameRate;
  561. constraints.video = {
  562. mandatory: {
  563. chromeMediaSource: 'desktop',
  564. chromeMediaSourceId: sourceId,
  565. maxFrameRate: frameRateSpecified || 3
  566. }
  567. };
  568. if (widthSpecified) {
  569. constraints.video.mandatory.maxWidth = widthSpecified;
  570. }
  571. if (heightSpecified) {
  572. constraints.video.mandatory.maxHeight = heightSpecified;
  573. }
  574. return window.navigator.mediaDevices.getUserMedia(constraints);
  575. });
  576. };
  577. }
  578. /*
  579. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  580. *
  581. * Use of this source code is governed by a BSD-style license
  582. * that can be found in the LICENSE file in the root of the source
  583. * tree.
  584. */
  585. function shimMediaStream(window) {
  586. window.MediaStream = window.MediaStream || window.webkitMediaStream;
  587. }
  588. function shimOnTrack$1(window) {
  589. if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
  590. window.RTCPeerConnection.prototype)) {
  591. Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
  592. get() {
  593. return this._ontrack;
  594. },
  595. set(f) {
  596. if (this._ontrack) {
  597. this.removeEventListener('track', this._ontrack);
  598. }
  599. this.addEventListener('track', this._ontrack = f);
  600. },
  601. enumerable: true,
  602. configurable: true
  603. });
  604. const origSetRemoteDescription =
  605. window.RTCPeerConnection.prototype.setRemoteDescription;
  606. window.RTCPeerConnection.prototype.setRemoteDescription =
  607. function setRemoteDescription() {
  608. if (!this._ontrackpoly) {
  609. this._ontrackpoly = (e) => {
  610. // onaddstream does not fire when a track is added to an existing
  611. // stream. But stream.onaddtrack is implemented so we use that.
  612. e.stream.addEventListener('addtrack', te => {
  613. let receiver;
  614. if (window.RTCPeerConnection.prototype.getReceivers) {
  615. receiver = this.getReceivers()
  616. .find(r => r.track && r.track.id === te.track.id);
  617. } else {
  618. receiver = {track: te.track};
  619. }
  620. const event = new Event('track');
  621. event.track = te.track;
  622. event.receiver = receiver;
  623. event.transceiver = {receiver};
  624. event.streams = [e.stream];
  625. this.dispatchEvent(event);
  626. });
  627. e.stream.getTracks().forEach(track => {
  628. let receiver;
  629. if (window.RTCPeerConnection.prototype.getReceivers) {
  630. receiver = this.getReceivers()
  631. .find(r => r.track && r.track.id === track.id);
  632. } else {
  633. receiver = {track};
  634. }
  635. const event = new Event('track');
  636. event.track = track;
  637. event.receiver = receiver;
  638. event.transceiver = {receiver};
  639. event.streams = [e.stream];
  640. this.dispatchEvent(event);
  641. });
  642. };
  643. this.addEventListener('addstream', this._ontrackpoly);
  644. }
  645. return origSetRemoteDescription.apply(this, arguments);
  646. };
  647. } else {
  648. // even if RTCRtpTransceiver is in window, it is only used and
  649. // emitted in unified-plan. Unfortunately this means we need
  650. // to unconditionally wrap the event.
  651. wrapPeerConnectionEvent(window, 'track', e => {
  652. if (!e.transceiver) {
  653. Object.defineProperty(e, 'transceiver',
  654. {value: {receiver: e.receiver}});
  655. }
  656. return e;
  657. });
  658. }
  659. }
  660. function shimGetSendersWithDtmf(window) {
  661. // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
  662. if (typeof window === 'object' && window.RTCPeerConnection &&
  663. !('getSenders' in window.RTCPeerConnection.prototype) &&
  664. 'createDTMFSender' in window.RTCPeerConnection.prototype) {
  665. const shimSenderWithDtmf = function(pc, track) {
  666. return {
  667. track,
  668. get dtmf() {
  669. if (this._dtmf === undefined) {
  670. if (track.kind === 'audio') {
  671. this._dtmf = pc.createDTMFSender(track);
  672. } else {
  673. this._dtmf = null;
  674. }
  675. }
  676. return this._dtmf;
  677. },
  678. _pc: pc
  679. };
  680. };
  681. // augment addTrack when getSenders is not available.
  682. if (!window.RTCPeerConnection.prototype.getSenders) {
  683. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  684. this._senders = this._senders || [];
  685. return this._senders.slice(); // return a copy of the internal state.
  686. };
  687. const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  688. window.RTCPeerConnection.prototype.addTrack =
  689. function addTrack(track, stream) {
  690. let sender = origAddTrack.apply(this, arguments);
  691. if (!sender) {
  692. sender = shimSenderWithDtmf(this, track);
  693. this._senders.push(sender);
  694. }
  695. return sender;
  696. };
  697. const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  698. window.RTCPeerConnection.prototype.removeTrack =
  699. function removeTrack(sender) {
  700. origRemoveTrack.apply(this, arguments);
  701. const idx = this._senders.indexOf(sender);
  702. if (idx !== -1) {
  703. this._senders.splice(idx, 1);
  704. }
  705. };
  706. }
  707. const origAddStream = window.RTCPeerConnection.prototype.addStream;
  708. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  709. this._senders = this._senders || [];
  710. origAddStream.apply(this, [stream]);
  711. stream.getTracks().forEach(track => {
  712. this._senders.push(shimSenderWithDtmf(this, track));
  713. });
  714. };
  715. const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  716. window.RTCPeerConnection.prototype.removeStream =
  717. function removeStream(stream) {
  718. this._senders = this._senders || [];
  719. origRemoveStream.apply(this, [stream]);
  720. stream.getTracks().forEach(track => {
  721. const sender = this._senders.find(s => s.track === track);
  722. if (sender) { // remove sender
  723. this._senders.splice(this._senders.indexOf(sender), 1);
  724. }
  725. });
  726. };
  727. } else if (typeof window === 'object' && window.RTCPeerConnection &&
  728. 'getSenders' in window.RTCPeerConnection.prototype &&
  729. 'createDTMFSender' in window.RTCPeerConnection.prototype &&
  730. window.RTCRtpSender &&
  731. !('dtmf' in window.RTCRtpSender.prototype)) {
  732. const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  733. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  734. const senders = origGetSenders.apply(this, []);
  735. senders.forEach(sender => sender._pc = this);
  736. return senders;
  737. };
  738. Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  739. get() {
  740. if (this._dtmf === undefined) {
  741. if (this.track.kind === 'audio') {
  742. this._dtmf = this._pc.createDTMFSender(this.track);
  743. } else {
  744. this._dtmf = null;
  745. }
  746. }
  747. return this._dtmf;
  748. }
  749. });
  750. }
  751. }
  752. function shimGetStats(window) {
  753. if (!window.RTCPeerConnection) {
  754. return;
  755. }
  756. const origGetStats = window.RTCPeerConnection.prototype.getStats;
  757. window.RTCPeerConnection.prototype.getStats = function getStats() {
  758. const [selector, onSucc, onErr] = arguments;
  759. // If selector is a function then we are in the old style stats so just
  760. // pass back the original getStats format to avoid breaking old users.
  761. if (arguments.length > 0 && typeof selector === 'function') {
  762. return origGetStats.apply(this, arguments);
  763. }
  764. // When spec-style getStats is supported, return those when called with
  765. // either no arguments or the selector argument is null.
  766. if (origGetStats.length === 0 && (arguments.length === 0 ||
  767. typeof selector !== 'function')) {
  768. return origGetStats.apply(this, []);
  769. }
  770. const fixChromeStats_ = function(response) {
  771. const standardReport = {};
  772. const reports = response.result();
  773. reports.forEach(report => {
  774. const standardStats = {
  775. id: report.id,
  776. timestamp: report.timestamp,
  777. type: {
  778. localcandidate: 'local-candidate',
  779. remotecandidate: 'remote-candidate'
  780. }[report.type] || report.type
  781. };
  782. report.names().forEach(name => {
  783. standardStats[name] = report.stat(name);
  784. });
  785. standardReport[standardStats.id] = standardStats;
  786. });
  787. return standardReport;
  788. };
  789. // shim getStats with maplike support
  790. const makeMapStats = function(stats) {
  791. return new Map(Object.keys(stats).map(key => [key, stats[key]]));
  792. };
  793. if (arguments.length >= 2) {
  794. const successCallbackWrapper_ = function(response) {
  795. onSucc(makeMapStats(fixChromeStats_(response)));
  796. };
  797. return origGetStats.apply(this, [successCallbackWrapper_,
  798. selector]);
  799. }
  800. // promise-support
  801. return new Promise((resolve, reject) => {
  802. origGetStats.apply(this, [
  803. function(response) {
  804. resolve(makeMapStats(fixChromeStats_(response)));
  805. }, reject]);
  806. }).then(onSucc, onErr);
  807. };
  808. }
  809. function shimSenderReceiverGetStats(window) {
  810. if (!(typeof window === 'object' && window.RTCPeerConnection &&
  811. window.RTCRtpSender && window.RTCRtpReceiver)) {
  812. return;
  813. }
  814. // shim sender stats.
  815. if (!('getStats' in window.RTCRtpSender.prototype)) {
  816. const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  817. if (origGetSenders) {
  818. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  819. const senders = origGetSenders.apply(this, []);
  820. senders.forEach(sender => sender._pc = this);
  821. return senders;
  822. };
  823. }
  824. const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  825. if (origAddTrack) {
  826. window.RTCPeerConnection.prototype.addTrack = function addTrack() {
  827. const sender = origAddTrack.apply(this, arguments);
  828. sender._pc = this;
  829. return sender;
  830. };
  831. }
  832. window.RTCRtpSender.prototype.getStats = function getStats() {
  833. const sender = this;
  834. return this._pc.getStats().then(result =>
  835. /* Note: this will include stats of all senders that
  836. * send a track with the same id as sender.track as
  837. * it is not possible to identify the RTCRtpSender.
  838. */
  839. filterStats(result, sender.track, true));
  840. };
  841. }
  842. // shim receiver stats.
  843. if (!('getStats' in window.RTCRtpReceiver.prototype)) {
  844. const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
  845. if (origGetReceivers) {
  846. window.RTCPeerConnection.prototype.getReceivers =
  847. function getReceivers() {
  848. const receivers = origGetReceivers.apply(this, []);
  849. receivers.forEach(receiver => receiver._pc = this);
  850. return receivers;
  851. };
  852. }
  853. wrapPeerConnectionEvent(window, 'track', e => {
  854. e.receiver._pc = e.srcElement;
  855. return e;
  856. });
  857. window.RTCRtpReceiver.prototype.getStats = function getStats() {
  858. const receiver = this;
  859. return this._pc.getStats().then(result =>
  860. filterStats(result, receiver.track, false));
  861. };
  862. }
  863. if (!('getStats' in window.RTCRtpSender.prototype &&
  864. 'getStats' in window.RTCRtpReceiver.prototype)) {
  865. return;
  866. }
  867. // shim RTCPeerConnection.getStats(track).
  868. const origGetStats = window.RTCPeerConnection.prototype.getStats;
  869. window.RTCPeerConnection.prototype.getStats = function getStats() {
  870. if (arguments.length > 0 &&
  871. arguments[0] instanceof window.MediaStreamTrack) {
  872. const track = arguments[0];
  873. let sender;
  874. let receiver;
  875. let err;
  876. this.getSenders().forEach(s => {
  877. if (s.track === track) {
  878. if (sender) {
  879. err = true;
  880. } else {
  881. sender = s;
  882. }
  883. }
  884. });
  885. this.getReceivers().forEach(r => {
  886. if (r.track === track) {
  887. if (receiver) {
  888. err = true;
  889. } else {
  890. receiver = r;
  891. }
  892. }
  893. return r.track === track;
  894. });
  895. if (err || (sender && receiver)) {
  896. return Promise.reject(new DOMException(
  897. 'There are more than one sender or receiver for the track.',
  898. 'InvalidAccessError'));
  899. } else if (sender) {
  900. return sender.getStats();
  901. } else if (receiver) {
  902. return receiver.getStats();
  903. }
  904. return Promise.reject(new DOMException(
  905. 'There is no sender or receiver for the track.',
  906. 'InvalidAccessError'));
  907. }
  908. return origGetStats.apply(this, arguments);
  909. };
  910. }
  911. function shimAddTrackRemoveTrackWithNative(window) {
  912. // shim addTrack/removeTrack with native variants in order to make
  913. // the interactions with legacy getLocalStreams behave as in other browsers.
  914. // Keeps a mapping stream.id => [stream, rtpsenders...]
  915. window.RTCPeerConnection.prototype.getLocalStreams =
  916. function getLocalStreams() {
  917. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  918. return Object.keys(this._shimmedLocalStreams)
  919. .map(streamId => this._shimmedLocalStreams[streamId][0]);
  920. };
  921. const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  922. window.RTCPeerConnection.prototype.addTrack =
  923. function addTrack(track, stream) {
  924. if (!stream) {
  925. return origAddTrack.apply(this, arguments);
  926. }
  927. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  928. const sender = origAddTrack.apply(this, arguments);
  929. if (!this._shimmedLocalStreams[stream.id]) {
  930. this._shimmedLocalStreams[stream.id] = [stream, sender];
  931. } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
  932. this._shimmedLocalStreams[stream.id].push(sender);
  933. }
  934. return sender;
  935. };
  936. const origAddStream = window.RTCPeerConnection.prototype.addStream;
  937. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  938. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  939. stream.getTracks().forEach(track => {
  940. const alreadyExists = this.getSenders().find(s => s.track === track);
  941. if (alreadyExists) {
  942. throw new DOMException('Track already exists.',
  943. 'InvalidAccessError');
  944. }
  945. });
  946. const existingSenders = this.getSenders();
  947. origAddStream.apply(this, arguments);
  948. const newSenders = this.getSenders()
  949. .filter(newSender => existingSenders.indexOf(newSender) === -1);
  950. this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
  951. };
  952. const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  953. window.RTCPeerConnection.prototype.removeStream =
  954. function removeStream(stream) {
  955. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  956. delete this._shimmedLocalStreams[stream.id];
  957. return origRemoveStream.apply(this, arguments);
  958. };
  959. const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  960. window.RTCPeerConnection.prototype.removeTrack =
  961. function removeTrack(sender) {
  962. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  963. if (sender) {
  964. Object.keys(this._shimmedLocalStreams).forEach(streamId => {
  965. const idx = this._shimmedLocalStreams[streamId].indexOf(sender);
  966. if (idx !== -1) {
  967. this._shimmedLocalStreams[streamId].splice(idx, 1);
  968. }
  969. if (this._shimmedLocalStreams[streamId].length === 1) {
  970. delete this._shimmedLocalStreams[streamId];
  971. }
  972. });
  973. }
  974. return origRemoveTrack.apply(this, arguments);
  975. };
  976. }
  977. function shimAddTrackRemoveTrack(window, browserDetails) {
  978. if (!window.RTCPeerConnection) {
  979. return;
  980. }
  981. // shim addTrack and removeTrack.
  982. if (window.RTCPeerConnection.prototype.addTrack &&
  983. browserDetails.version >= 65) {
  984. return shimAddTrackRemoveTrackWithNative(window);
  985. }
  986. // also shim pc.getLocalStreams when addTrack is shimmed
  987. // to return the original streams.
  988. const origGetLocalStreams = window.RTCPeerConnection.prototype
  989. .getLocalStreams;
  990. window.RTCPeerConnection.prototype.getLocalStreams =
  991. function getLocalStreams() {
  992. const nativeStreams = origGetLocalStreams.apply(this);
  993. this._reverseStreams = this._reverseStreams || {};
  994. return nativeStreams.map(stream => this._reverseStreams[stream.id]);
  995. };
  996. const origAddStream = window.RTCPeerConnection.prototype.addStream;
  997. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  998. this._streams = this._streams || {};
  999. this._reverseStreams = this._reverseStreams || {};
  1000. stream.getTracks().forEach(track => {
  1001. const alreadyExists = this.getSenders().find(s => s.track === track);
  1002. if (alreadyExists) {
  1003. throw new DOMException('Track already exists.',
  1004. 'InvalidAccessError');
  1005. }
  1006. });
  1007. // Add identity mapping for consistency with addTrack.
  1008. // Unless this is being used with a stream from addTrack.
  1009. if (!this._reverseStreams[stream.id]) {
  1010. const newStream = new window.MediaStream(stream.getTracks());
  1011. this._streams[stream.id] = newStream;
  1012. this._reverseStreams[newStream.id] = stream;
  1013. stream = newStream;
  1014. }
  1015. origAddStream.apply(this, [stream]);
  1016. };
  1017. const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  1018. window.RTCPeerConnection.prototype.removeStream =
  1019. function removeStream(stream) {
  1020. this._streams = this._streams || {};
  1021. this._reverseStreams = this._reverseStreams || {};
  1022. origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);
  1023. delete this._reverseStreams[(this._streams[stream.id] ?
  1024. this._streams[stream.id].id : stream.id)];
  1025. delete this._streams[stream.id];
  1026. };
  1027. window.RTCPeerConnection.prototype.addTrack =
  1028. function addTrack(track, stream) {
  1029. if (this.signalingState === 'closed') {
  1030. throw new DOMException(
  1031. 'The RTCPeerConnection\'s signalingState is \'closed\'.',
  1032. 'InvalidStateError');
  1033. }
  1034. const streams = [].slice.call(arguments, 1);
  1035. if (streams.length !== 1 ||
  1036. !streams[0].getTracks().find(t => t === track)) {
  1037. // this is not fully correct but all we can manage without
  1038. // [[associated MediaStreams]] internal slot.
  1039. throw new DOMException(
  1040. 'The adapter.js addTrack polyfill only supports a single ' +
  1041. ' stream which is associated with the specified track.',
  1042. 'NotSupportedError');
  1043. }
  1044. const alreadyExists = this.getSenders().find(s => s.track === track);
  1045. if (alreadyExists) {
  1046. throw new DOMException('Track already exists.',
  1047. 'InvalidAccessError');
  1048. }
  1049. this._streams = this._streams || {};
  1050. this._reverseStreams = this._reverseStreams || {};
  1051. const oldStream = this._streams[stream.id];
  1052. if (oldStream) {
  1053. // this is using odd Chrome behaviour, use with caution:
  1054. // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
  1055. // Note: we rely on the high-level addTrack/dtmf shim to
  1056. // create the sender with a dtmf sender.
  1057. oldStream.addTrack(track);
  1058. // Trigger ONN async.
  1059. Promise.resolve().then(() => {
  1060. this.dispatchEvent(new Event('negotiationneeded'));
  1061. });
  1062. } else {
  1063. const newStream = new window.MediaStream([track]);
  1064. this._streams[stream.id] = newStream;
  1065. this._reverseStreams[newStream.id] = stream;
  1066. this.addStream(newStream);
  1067. }
  1068. return this.getSenders().find(s => s.track === track);
  1069. };
  1070. // replace the internal stream id with the external one and
  1071. // vice versa.
  1072. function replaceInternalStreamId(pc, description) {
  1073. let sdp = description.sdp;
  1074. Object.keys(pc._reverseStreams || []).forEach(internalId => {
  1075. const externalStream = pc._reverseStreams[internalId];
  1076. const internalStream = pc._streams[externalStream.id];
  1077. sdp = sdp.replace(new RegExp(internalStream.id, 'g'),
  1078. externalStream.id);
  1079. });
  1080. return new RTCSessionDescription({
  1081. type: description.type,
  1082. sdp
  1083. });
  1084. }
  1085. function replaceExternalStreamId(pc, description) {
  1086. let sdp = description.sdp;
  1087. Object.keys(pc._reverseStreams || []).forEach(internalId => {
  1088. const externalStream = pc._reverseStreams[internalId];
  1089. const internalStream = pc._streams[externalStream.id];
  1090. sdp = sdp.replace(new RegExp(externalStream.id, 'g'),
  1091. internalStream.id);
  1092. });
  1093. return new RTCSessionDescription({
  1094. type: description.type,
  1095. sdp
  1096. });
  1097. }
  1098. ['createOffer', 'createAnswer'].forEach(function(method) {
  1099. const nativeMethod = window.RTCPeerConnection.prototype[method];
  1100. const methodObj = {[method]() {
  1101. const args = arguments;
  1102. const isLegacyCall = arguments.length &&
  1103. typeof arguments[0] === 'function';
  1104. if (isLegacyCall) {
  1105. return nativeMethod.apply(this, [
  1106. (description) => {
  1107. const desc = replaceInternalStreamId(this, description);
  1108. args[0].apply(null, [desc]);
  1109. },
  1110. (err) => {
  1111. if (args[1]) {
  1112. args[1].apply(null, err);
  1113. }
  1114. }, arguments[2]
  1115. ]);
  1116. }
  1117. return nativeMethod.apply(this, arguments)
  1118. .then(description => replaceInternalStreamId(this, description));
  1119. }};
  1120. window.RTCPeerConnection.prototype[method] = methodObj[method];
  1121. });
  1122. const origSetLocalDescription =
  1123. window.RTCPeerConnection.prototype.setLocalDescription;
  1124. window.RTCPeerConnection.prototype.setLocalDescription =
  1125. function setLocalDescription() {
  1126. if (!arguments.length || !arguments[0].type) {
  1127. return origSetLocalDescription.apply(this, arguments);
  1128. }
  1129. arguments[0] = replaceExternalStreamId(this, arguments[0]);
  1130. return origSetLocalDescription.apply(this, arguments);
  1131. };
  1132. // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
  1133. const origLocalDescription = Object.getOwnPropertyDescriptor(
  1134. window.RTCPeerConnection.prototype, 'localDescription');
  1135. Object.defineProperty(window.RTCPeerConnection.prototype,
  1136. 'localDescription', {
  1137. get() {
  1138. const description = origLocalDescription.get.apply(this);
  1139. if (description.type === '') {
  1140. return description;
  1141. }
  1142. return replaceInternalStreamId(this, description);
  1143. }
  1144. });
  1145. window.RTCPeerConnection.prototype.removeTrack =
  1146. function removeTrack(sender) {
  1147. if (this.signalingState === 'closed') {
  1148. throw new DOMException(
  1149. 'The RTCPeerConnection\'s signalingState is \'closed\'.',
  1150. 'InvalidStateError');
  1151. }
  1152. // We can not yet check for sender instanceof RTCRtpSender
  1153. // since we shim RTPSender. So we check if sender._pc is set.
  1154. if (!sender._pc) {
  1155. throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +
  1156. 'does not implement interface RTCRtpSender.', 'TypeError');
  1157. }
  1158. const isLocal = sender._pc === this;
  1159. if (!isLocal) {
  1160. throw new DOMException('Sender was not created by this connection.',
  1161. 'InvalidAccessError');
  1162. }
  1163. // Search for the native stream the senders track belongs to.
  1164. this._streams = this._streams || {};
  1165. let stream;
  1166. Object.keys(this._streams).forEach(streamid => {
  1167. const hasTrack = this._streams[streamid].getTracks()
  1168. .find(track => sender.track === track);
  1169. if (hasTrack) {
  1170. stream = this._streams[streamid];
  1171. }
  1172. });
  1173. if (stream) {
  1174. if (stream.getTracks().length === 1) {
  1175. // if this is the last track of the stream, remove the stream. This
  1176. // takes care of any shimmed _senders.
  1177. this.removeStream(this._reverseStreams[stream.id]);
  1178. } else {
  1179. // relying on the same odd chrome behaviour as above.
  1180. stream.removeTrack(sender.track);
  1181. }
  1182. this.dispatchEvent(new Event('negotiationneeded'));
  1183. }
  1184. };
  1185. }
  1186. function shimPeerConnection$2(window, browserDetails) {
  1187. if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
  1188. // very basic support for old versions.
  1189. window.RTCPeerConnection = window.webkitRTCPeerConnection;
  1190. }
  1191. if (!window.RTCPeerConnection) {
  1192. return;
  1193. }
  1194. // shim implicit creation of RTCSessionDescription/RTCIceCandidate
  1195. if (browserDetails.version < 53) {
  1196. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  1197. .forEach(function(method) {
  1198. const nativeMethod = window.RTCPeerConnection.prototype[method];
  1199. const methodObj = {[method]() {
  1200. arguments[0] = new ((method === 'addIceCandidate') ?
  1201. window.RTCIceCandidate :
  1202. window.RTCSessionDescription)(arguments[0]);
  1203. return nativeMethod.apply(this, arguments);
  1204. }};
  1205. window.RTCPeerConnection.prototype[method] = methodObj[method];
  1206. });
  1207. }
  1208. }
  1209. // Attempt to fix ONN in plan-b mode.
  1210. function fixNegotiationNeeded(window, browserDetails) {
  1211. wrapPeerConnectionEvent(window, 'negotiationneeded', e => {
  1212. const pc = e.target;
  1213. if (browserDetails.version < 72 || (pc.getConfiguration &&
  1214. pc.getConfiguration().sdpSemantics === 'plan-b')) {
  1215. if (pc.signalingState !== 'stable') {
  1216. return;
  1217. }
  1218. }
  1219. return e;
  1220. });
  1221. }
  1222. var chromeShim = /*#__PURE__*/Object.freeze({
  1223. __proto__: null,
  1224. shimMediaStream: shimMediaStream,
  1225. shimOnTrack: shimOnTrack$1,
  1226. shimGetSendersWithDtmf: shimGetSendersWithDtmf,
  1227. shimGetStats: shimGetStats,
  1228. shimSenderReceiverGetStats: shimSenderReceiverGetStats,
  1229. shimAddTrackRemoveTrackWithNative: shimAddTrackRemoveTrackWithNative,
  1230. shimAddTrackRemoveTrack: shimAddTrackRemoveTrack,
  1231. shimPeerConnection: shimPeerConnection$2,
  1232. fixNegotiationNeeded: fixNegotiationNeeded,
  1233. shimGetUserMedia: shimGetUserMedia$3,
  1234. shimGetDisplayMedia: shimGetDisplayMedia$2
  1235. });
  1236. /*
  1237. * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
  1238. *
  1239. * Use of this source code is governed by a BSD-style license
  1240. * that can be found in the LICENSE file in the root of the source
  1241. * tree.
  1242. */
  1243. // Edge does not like
  1244. // 1) stun: filtered after 14393 unless ?transport=udp is present
  1245. // 2) turn: that does not have all of turn:host:port?transport=udp
  1246. // 3) turn: with ipv6 addresses
  1247. // 4) turn: occurring muliple times
  1248. function filterIceServers$1(iceServers, edgeVersion) {
  1249. let hasTurn = false;
  1250. iceServers = JSON.parse(JSON.stringify(iceServers));
  1251. return iceServers.filter(server => {
  1252. if (server && (server.urls || server.url)) {
  1253. let urls = server.urls || server.url;
  1254. if (server.url && !server.urls) {
  1255. deprecated('RTCIceServer.url', 'RTCIceServer.urls');
  1256. }
  1257. const isString = typeof urls === 'string';
  1258. if (isString) {
  1259. urls = [urls];
  1260. }
  1261. urls = urls.filter(url => {
  1262. // filter STUN unconditionally.
  1263. if (url.indexOf('stun:') === 0) {
  1264. return false;
  1265. }
  1266. const validTurn = url.startsWith('turn') &&
  1267. !url.startsWith('turn:[') &&
  1268. url.includes('transport=udp');
  1269. if (validTurn && !hasTurn) {
  1270. hasTurn = true;
  1271. return true;
  1272. }
  1273. return validTurn && !hasTurn;
  1274. });
  1275. delete server.url;
  1276. server.urls = isString ? urls[0] : urls;
  1277. return !!urls.length;
  1278. }
  1279. });
  1280. }
  1281. function createCommonjsModule(fn) {
  1282. var module = { exports: {} };
  1283. return fn(module, module.exports), module.exports;
  1284. }
  1285. /* eslint-env node */
  1286. var sdp = createCommonjsModule(function (module) {
  1287. // SDP helpers.
  1288. var SDPUtils = {};
  1289. // Generate an alphanumeric identifier for cname or mids.
  1290. // TODO: use UUIDs instead? https://gist.github.com/jed/982883
  1291. SDPUtils.generateIdentifier = function() {
  1292. return Math.random().toString(36).substr(2, 10);
  1293. };
  1294. // The RTCP CNAME used by all peerconnections from the same JS.
  1295. SDPUtils.localCName = SDPUtils.generateIdentifier();
  1296. // Splits SDP into lines, dealing with both CRLF and LF.
  1297. SDPUtils.splitLines = function(blob) {
  1298. return blob.trim().split('\n').map(function(line) {
  1299. return line.trim();
  1300. });
  1301. };
  1302. // Splits SDP into sessionpart and mediasections. Ensures CRLF.
  1303. SDPUtils.splitSections = function(blob) {
  1304. var parts = blob.split('\nm=');
  1305. return parts.map(function(part, index) {
  1306. return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
  1307. });
  1308. };
  1309. // returns the session description.
  1310. SDPUtils.getDescription = function(blob) {
  1311. var sections = SDPUtils.splitSections(blob);
  1312. return sections && sections[0];
  1313. };
  1314. // returns the individual media sections.
  1315. SDPUtils.getMediaSections = function(blob) {
  1316. var sections = SDPUtils.splitSections(blob);
  1317. sections.shift();
  1318. return sections;
  1319. };
  1320. // Returns lines that start with a certain prefix.
  1321. SDPUtils.matchPrefix = function(blob, prefix) {
  1322. return SDPUtils.splitLines(blob).filter(function(line) {
  1323. return line.indexOf(prefix) === 0;
  1324. });
  1325. };
  1326. // Parses an ICE candidate line. Sample input:
  1327. // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
  1328. // rport 55996"
  1329. SDPUtils.parseCandidate = function(line) {
  1330. var parts;
  1331. // Parse both variants.
  1332. if (line.indexOf('a=candidate:') === 0) {
  1333. parts = line.substring(12).split(' ');
  1334. } else {
  1335. parts = line.substring(10).split(' ');
  1336. }
  1337. var candidate = {
  1338. foundation: parts[0],
  1339. component: parseInt(parts[1], 10),
  1340. protocol: parts[2].toLowerCase(),
  1341. priority: parseInt(parts[3], 10),
  1342. ip: parts[4],
  1343. address: parts[4], // address is an alias for ip.
  1344. port: parseInt(parts[5], 10),
  1345. // skip parts[6] == 'typ'
  1346. type: parts[7]
  1347. };
  1348. for (var i = 8; i < parts.length; i += 2) {
  1349. switch (parts[i]) {
  1350. case 'raddr':
  1351. candidate.relatedAddress = parts[i + 1];
  1352. break;
  1353. case 'rport':
  1354. candidate.relatedPort = parseInt(parts[i + 1], 10);
  1355. break;
  1356. case 'tcptype':
  1357. candidate.tcpType = parts[i + 1];
  1358. break;
  1359. case 'ufrag':
  1360. candidate.ufrag = parts[i + 1]; // for backward compability.
  1361. candidate.usernameFragment = parts[i + 1];
  1362. break;
  1363. default: // extension handling, in particular ufrag
  1364. candidate[parts[i]] = parts[i + 1];
  1365. break;
  1366. }
  1367. }
  1368. return candidate;
  1369. };
  1370. // Translates a candidate object into SDP candidate attribute.
  1371. SDPUtils.writeCandidate = function(candidate) {
  1372. var sdp = [];
  1373. sdp.push(candidate.foundation);
  1374. sdp.push(candidate.component);
  1375. sdp.push(candidate.protocol.toUpperCase());
  1376. sdp.push(candidate.priority);
  1377. sdp.push(candidate.address || candidate.ip);
  1378. sdp.push(candidate.port);
  1379. var type = candidate.type;
  1380. sdp.push('typ');
  1381. sdp.push(type);
  1382. if (type !== 'host' && candidate.relatedAddress &&
  1383. candidate.relatedPort) {
  1384. sdp.push('raddr');
  1385. sdp.push(candidate.relatedAddress);
  1386. sdp.push('rport');
  1387. sdp.push(candidate.relatedPort);
  1388. }
  1389. if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
  1390. sdp.push('tcptype');
  1391. sdp.push(candidate.tcpType);
  1392. }
  1393. if (candidate.usernameFragment || candidate.ufrag) {
  1394. sdp.push('ufrag');
  1395. sdp.push(candidate.usernameFragment || candidate.ufrag);
  1396. }
  1397. return 'candidate:' + sdp.join(' ');
  1398. };
  1399. // Parses an ice-options line, returns an array of option tags.
  1400. // a=ice-options:foo bar
  1401. SDPUtils.parseIceOptions = function(line) {
  1402. return line.substr(14).split(' ');
  1403. };
  1404. // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
  1405. // a=rtpmap:111 opus/48000/2
  1406. SDPUtils.parseRtpMap = function(line) {
  1407. var parts = line.substr(9).split(' ');
  1408. var parsed = {
  1409. payloadType: parseInt(parts.shift(), 10) // was: id
  1410. };
  1411. parts = parts[0].split('/');
  1412. parsed.name = parts[0];
  1413. parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
  1414. parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
  1415. // legacy alias, got renamed back to channels in ORTC.
  1416. parsed.numChannels = parsed.channels;
  1417. return parsed;
  1418. };
  1419. // Generate an a=rtpmap line from RTCRtpCodecCapability or
  1420. // RTCRtpCodecParameters.
  1421. SDPUtils.writeRtpMap = function(codec) {
  1422. var pt = codec.payloadType;
  1423. if (codec.preferredPayloadType !== undefined) {
  1424. pt = codec.preferredPayloadType;
  1425. }
  1426. var channels = codec.channels || codec.numChannels || 1;
  1427. return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
  1428. (channels !== 1 ? '/' + channels : '') + '\r\n';
  1429. };
  1430. // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
  1431. // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
  1432. // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
  1433. SDPUtils.parseExtmap = function(line) {
  1434. var parts = line.substr(9).split(' ');
  1435. return {
  1436. id: parseInt(parts[0], 10),
  1437. direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
  1438. uri: parts[1]
  1439. };
  1440. };
  1441. // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
  1442. // RTCRtpHeaderExtension.
  1443. SDPUtils.writeExtmap = function(headerExtension) {
  1444. return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
  1445. (headerExtension.direction && headerExtension.direction !== 'sendrecv'
  1446. ? '/' + headerExtension.direction
  1447. : '') +
  1448. ' ' + headerExtension.uri + '\r\n';
  1449. };
  1450. // Parses an ftmp line, returns dictionary. Sample input:
  1451. // a=fmtp:96 vbr=on;cng=on
  1452. // Also deals with vbr=on; cng=on
  1453. SDPUtils.parseFmtp = function(line) {
  1454. var parsed = {};
  1455. var kv;
  1456. var parts = line.substr(line.indexOf(' ') + 1).split(';');
  1457. for (var j = 0; j < parts.length; j++) {
  1458. kv = parts[j].trim().split('=');
  1459. parsed[kv[0].trim()] = kv[1];
  1460. }
  1461. return parsed;
  1462. };
  1463. // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
  1464. SDPUtils.writeFmtp = function(codec) {
  1465. var line = '';
  1466. var pt = codec.payloadType;
  1467. if (codec.preferredPayloadType !== undefined) {
  1468. pt = codec.preferredPayloadType;
  1469. }
  1470. if (codec.parameters && Object.keys(codec.parameters).length) {
  1471. var params = [];
  1472. Object.keys(codec.parameters).forEach(function(param) {
  1473. if (codec.parameters[param]) {
  1474. params.push(param + '=' + codec.parameters[param]);
  1475. } else {
  1476. params.push(param);
  1477. }
  1478. });
  1479. line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
  1480. }
  1481. return line;
  1482. };
  1483. // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
  1484. // a=rtcp-fb:98 nack rpsi
  1485. SDPUtils.parseRtcpFb = function(line) {
  1486. var parts = line.substr(line.indexOf(' ') + 1).split(' ');
  1487. return {
  1488. type: parts.shift(),
  1489. parameter: parts.join(' ')
  1490. };
  1491. };
  1492. // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
  1493. SDPUtils.writeRtcpFb = function(codec) {
  1494. var lines = '';
  1495. var pt = codec.payloadType;
  1496. if (codec.preferredPayloadType !== undefined) {
  1497. pt = codec.preferredPayloadType;
  1498. }
  1499. if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
  1500. // FIXME: special handling for trr-int?
  1501. codec.rtcpFeedback.forEach(function(fb) {
  1502. lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
  1503. (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
  1504. '\r\n';
  1505. });
  1506. }
  1507. return lines;
  1508. };
  1509. // Parses an RFC 5576 ssrc media attribute. Sample input:
  1510. // a=ssrc:3735928559 cname:something
  1511. SDPUtils.parseSsrcMedia = function(line) {
  1512. var sp = line.indexOf(' ');
  1513. var parts = {
  1514. ssrc: parseInt(line.substr(7, sp - 7), 10)
  1515. };
  1516. var colon = line.indexOf(':', sp);
  1517. if (colon > -1) {
  1518. parts.attribute = line.substr(sp + 1, colon - sp - 1);
  1519. parts.value = line.substr(colon + 1);
  1520. } else {
  1521. parts.attribute = line.substr(sp + 1);
  1522. }
  1523. return parts;
  1524. };
  1525. SDPUtils.parseSsrcGroup = function(line) {
  1526. var parts = line.substr(13).split(' ');
  1527. return {
  1528. semantics: parts.shift(),
  1529. ssrcs: parts.map(function(ssrc) {
  1530. return parseInt(ssrc, 10);
  1531. })
  1532. };
  1533. };
  1534. // Extracts the MID (RFC 5888) from a media section.
  1535. // returns the MID or undefined if no mid line was found.
  1536. SDPUtils.getMid = function(mediaSection) {
  1537. var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
  1538. if (mid) {
  1539. return mid.substr(6);
  1540. }
  1541. };
  1542. SDPUtils.parseFingerprint = function(line) {
  1543. var parts = line.substr(14).split(' ');
  1544. return {
  1545. algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
  1546. value: parts[1]
  1547. };
  1548. };
  1549. // Extracts DTLS parameters from SDP media section or sessionpart.
  1550. // FIXME: for consistency with other functions this should only
  1551. // get the fingerprint line as input. See also getIceParameters.
  1552. SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
  1553. var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1554. 'a=fingerprint:');
  1555. // Note: a=setup line is ignored since we use the 'auto' role.
  1556. // Note2: 'algorithm' is not case sensitive except in Edge.
  1557. return {
  1558. role: 'auto',
  1559. fingerprints: lines.map(SDPUtils.parseFingerprint)
  1560. };
  1561. };
  1562. // Serializes DTLS parameters to SDP.
  1563. SDPUtils.writeDtlsParameters = function(params, setupType) {
  1564. var sdp = 'a=setup:' + setupType + '\r\n';
  1565. params.fingerprints.forEach(function(fp) {
  1566. sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
  1567. });
  1568. return sdp;
  1569. };
  1570. // Parses a=crypto lines into
  1571. // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
  1572. SDPUtils.parseCryptoLine = function(line) {
  1573. var parts = line.substr(9).split(' ');
  1574. return {
  1575. tag: parseInt(parts[0], 10),
  1576. cryptoSuite: parts[1],
  1577. keyParams: parts[2],
  1578. sessionParams: parts.slice(3),
  1579. };
  1580. };
  1581. SDPUtils.writeCryptoLine = function(parameters) {
  1582. return 'a=crypto:' + parameters.tag + ' ' +
  1583. parameters.cryptoSuite + ' ' +
  1584. (typeof parameters.keyParams === 'object'
  1585. ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)
  1586. : parameters.keyParams) +
  1587. (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +
  1588. '\r\n';
  1589. };
  1590. // Parses the crypto key parameters into
  1591. // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
  1592. SDPUtils.parseCryptoKeyParams = function(keyParams) {
  1593. if (keyParams.indexOf('inline:') !== 0) {
  1594. return null;
  1595. }
  1596. var parts = keyParams.substr(7).split('|');
  1597. return {
  1598. keyMethod: 'inline',
  1599. keySalt: parts[0],
  1600. lifeTime: parts[1],
  1601. mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
  1602. mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,
  1603. };
  1604. };
  1605. SDPUtils.writeCryptoKeyParams = function(keyParams) {
  1606. return keyParams.keyMethod + ':'
  1607. + keyParams.keySalt +
  1608. (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +
  1609. (keyParams.mkiValue && keyParams.mkiLength
  1610. ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength
  1611. : '');
  1612. };
  1613. // Extracts all SDES paramters.
  1614. SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {
  1615. var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1616. 'a=crypto:');
  1617. return lines.map(SDPUtils.parseCryptoLine);
  1618. };
  1619. // Parses ICE information from SDP media section or sessionpart.
  1620. // FIXME: for consistency with other functions this should only
  1621. // get the ice-ufrag and ice-pwd lines as input.
  1622. SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
  1623. var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1624. 'a=ice-ufrag:')[0];
  1625. var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1626. 'a=ice-pwd:')[0];
  1627. if (!(ufrag && pwd)) {
  1628. return null;
  1629. }
  1630. return {
  1631. usernameFragment: ufrag.substr(12),
  1632. password: pwd.substr(10),
  1633. };
  1634. };
  1635. // Serializes ICE parameters to SDP.
  1636. SDPUtils.writeIceParameters = function(params) {
  1637. return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
  1638. 'a=ice-pwd:' + params.password + '\r\n';
  1639. };
  1640. // Parses the SDP media section and returns RTCRtpParameters.
  1641. SDPUtils.parseRtpParameters = function(mediaSection) {
  1642. var description = {
  1643. codecs: [],
  1644. headerExtensions: [],
  1645. fecMechanisms: [],
  1646. rtcp: []
  1647. };
  1648. var lines = SDPUtils.splitLines(mediaSection);
  1649. var mline = lines[0].split(' ');
  1650. for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
  1651. var pt = mline[i];
  1652. var rtpmapline = SDPUtils.matchPrefix(
  1653. mediaSection, 'a=rtpmap:' + pt + ' ')[0];
  1654. if (rtpmapline) {
  1655. var codec = SDPUtils.parseRtpMap(rtpmapline);
  1656. var fmtps = SDPUtils.matchPrefix(
  1657. mediaSection, 'a=fmtp:' + pt + ' ');
  1658. // Only the first a=fmtp:<pt> is considered.
  1659. codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
  1660. codec.rtcpFeedback = SDPUtils.matchPrefix(
  1661. mediaSection, 'a=rtcp-fb:' + pt + ' ')
  1662. .map(SDPUtils.parseRtcpFb);
  1663. description.codecs.push(codec);
  1664. // parse FEC mechanisms from rtpmap lines.
  1665. switch (codec.name.toUpperCase()) {
  1666. case 'RED':
  1667. case 'ULPFEC':
  1668. description.fecMechanisms.push(codec.name.toUpperCase());
  1669. break;
  1670. }
  1671. }
  1672. }
  1673. SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
  1674. description.headerExtensions.push(SDPUtils.parseExtmap(line));
  1675. });
  1676. // FIXME: parse rtcp.
  1677. return description;
  1678. };
  1679. // Generates parts of the SDP media section describing the capabilities /
  1680. // parameters.
  1681. SDPUtils.writeRtpDescription = function(kind, caps) {
  1682. var sdp = '';
  1683. // Build the mline.
  1684. sdp += 'm=' + kind + ' ';
  1685. sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
  1686. sdp += ' UDP/TLS/RTP/SAVPF ';
  1687. sdp += caps.codecs.map(function(codec) {
  1688. if (codec.preferredPayloadType !== undefined) {
  1689. return codec.preferredPayloadType;
  1690. }
  1691. return codec.payloadType;
  1692. }).join(' ') + '\r\n';
  1693. sdp += 'c=IN IP4 0.0.0.0\r\n';
  1694. sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
  1695. // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
  1696. caps.codecs.forEach(function(codec) {
  1697. sdp += SDPUtils.writeRtpMap(codec);
  1698. sdp += SDPUtils.writeFmtp(codec);
  1699. sdp += SDPUtils.writeRtcpFb(codec);
  1700. });
  1701. var maxptime = 0;
  1702. caps.codecs.forEach(function(codec) {
  1703. if (codec.maxptime > maxptime) {
  1704. maxptime = codec.maxptime;
  1705. }
  1706. });
  1707. if (maxptime > 0) {
  1708. sdp += 'a=maxptime:' + maxptime + '\r\n';
  1709. }
  1710. sdp += 'a=rtcp-mux\r\n';
  1711. if (caps.headerExtensions) {
  1712. caps.headerExtensions.forEach(function(extension) {
  1713. sdp += SDPUtils.writeExtmap(extension);
  1714. });
  1715. }
  1716. // FIXME: write fecMechanisms.
  1717. return sdp;
  1718. };
  1719. // Parses the SDP media section and returns an array of
  1720. // RTCRtpEncodingParameters.
  1721. SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
  1722. var encodingParameters = [];
  1723. var description = SDPUtils.parseRtpParameters(mediaSection);
  1724. var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
  1725. var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
  1726. // filter a=ssrc:... cname:, ignore PlanB-msid
  1727. var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1728. .map(function(line) {
  1729. return SDPUtils.parseSsrcMedia(line);
  1730. })
  1731. .filter(function(parts) {
  1732. return parts.attribute === 'cname';
  1733. });
  1734. var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
  1735. var secondarySsrc;
  1736. var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
  1737. .map(function(line) {
  1738. var parts = line.substr(17).split(' ');
  1739. return parts.map(function(part) {
  1740. return parseInt(part, 10);
  1741. });
  1742. });
  1743. if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
  1744. secondarySsrc = flows[0][1];
  1745. }
  1746. description.codecs.forEach(function(codec) {
  1747. if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
  1748. var encParam = {
  1749. ssrc: primarySsrc,
  1750. codecPayloadType: parseInt(codec.parameters.apt, 10)
  1751. };
  1752. if (primarySsrc && secondarySsrc) {
  1753. encParam.rtx = {ssrc: secondarySsrc};
  1754. }
  1755. encodingParameters.push(encParam);
  1756. if (hasRed) {
  1757. encParam = JSON.parse(JSON.stringify(encParam));
  1758. encParam.fec = {
  1759. ssrc: primarySsrc,
  1760. mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
  1761. };
  1762. encodingParameters.push(encParam);
  1763. }
  1764. }
  1765. });
  1766. if (encodingParameters.length === 0 && primarySsrc) {
  1767. encodingParameters.push({
  1768. ssrc: primarySsrc
  1769. });
  1770. }
  1771. // we support both b=AS and b=TIAS but interpret AS as TIAS.
  1772. var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
  1773. if (bandwidth.length) {
  1774. if (bandwidth[0].indexOf('b=TIAS:') === 0) {
  1775. bandwidth = parseInt(bandwidth[0].substr(7), 10);
  1776. } else if (bandwidth[0].indexOf('b=AS:') === 0) {
  1777. // use formula from JSEP to convert b=AS to TIAS value.
  1778. bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
  1779. - (50 * 40 * 8);
  1780. } else {
  1781. bandwidth = undefined;
  1782. }
  1783. encodingParameters.forEach(function(params) {
  1784. params.maxBitrate = bandwidth;
  1785. });
  1786. }
  1787. return encodingParameters;
  1788. };
  1789. // parses http://draft.ortc.org/#rtcrtcpparameters*
  1790. SDPUtils.parseRtcpParameters = function(mediaSection) {
  1791. var rtcpParameters = {};
  1792. // Gets the first SSRC. Note tha with RTX there might be multiple
  1793. // SSRCs.
  1794. var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1795. .map(function(line) {
  1796. return SDPUtils.parseSsrcMedia(line);
  1797. })
  1798. .filter(function(obj) {
  1799. return obj.attribute === 'cname';
  1800. })[0];
  1801. if (remoteSsrc) {
  1802. rtcpParameters.cname = remoteSsrc.value;
  1803. rtcpParameters.ssrc = remoteSsrc.ssrc;
  1804. }
  1805. // Edge uses the compound attribute instead of reducedSize
  1806. // compound is !reducedSize
  1807. var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
  1808. rtcpParameters.reducedSize = rsize.length > 0;
  1809. rtcpParameters.compound = rsize.length === 0;
  1810. // parses the rtcp-mux attrіbute.
  1811. // Note that Edge does not support unmuxed RTCP.
  1812. var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
  1813. rtcpParameters.mux = mux.length > 0;
  1814. return rtcpParameters;
  1815. };
  1816. // parses either a=msid: or a=ssrc:... msid lines and returns
  1817. // the id of the MediaStream and MediaStreamTrack.
  1818. SDPUtils.parseMsid = function(mediaSection) {
  1819. var parts;
  1820. var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
  1821. if (spec.length === 1) {
  1822. parts = spec[0].substr(7).split(' ');
  1823. return {stream: parts[0], track: parts[1]};
  1824. }
  1825. var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1826. .map(function(line) {
  1827. return SDPUtils.parseSsrcMedia(line);
  1828. })
  1829. .filter(function(msidParts) {
  1830. return msidParts.attribute === 'msid';
  1831. });
  1832. if (planB.length > 0) {
  1833. parts = planB[0].value.split(' ');
  1834. return {stream: parts[0], track: parts[1]};
  1835. }
  1836. };
  1837. // SCTP
  1838. // parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
  1839. // to draft-ietf-mmusic-sctp-sdp-05
  1840. SDPUtils.parseSctpDescription = function(mediaSection) {
  1841. var mline = SDPUtils.parseMLine(mediaSection);
  1842. var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
  1843. var maxMessageSize;
  1844. if (maxSizeLine.length > 0) {
  1845. maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);
  1846. }
  1847. if (isNaN(maxMessageSize)) {
  1848. maxMessageSize = 65536;
  1849. }
  1850. var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
  1851. if (sctpPort.length > 0) {
  1852. return {
  1853. port: parseInt(sctpPort[0].substr(12), 10),
  1854. protocol: mline.fmt,
  1855. maxMessageSize: maxMessageSize
  1856. };
  1857. }
  1858. var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
  1859. if (sctpMapLines.length > 0) {
  1860. var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0]
  1861. .substr(10)
  1862. .split(' ');
  1863. return {
  1864. port: parseInt(parts[0], 10),
  1865. protocol: parts[1],
  1866. maxMessageSize: maxMessageSize
  1867. };
  1868. }
  1869. };
  1870. // SCTP
  1871. // outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
  1872. // support by now receiving in this format, unless we originally parsed
  1873. // as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line
  1874. // protocol of DTLS/SCTP -- without UDP/ or TCP/)
  1875. SDPUtils.writeSctpDescription = function(media, sctp) {
  1876. var output = [];
  1877. if (media.protocol !== 'DTLS/SCTP') {
  1878. output = [
  1879. 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n',
  1880. 'c=IN IP4 0.0.0.0\r\n',
  1881. 'a=sctp-port:' + sctp.port + '\r\n'
  1882. ];
  1883. } else {
  1884. output = [
  1885. 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n',
  1886. 'c=IN IP4 0.0.0.0\r\n',
  1887. 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n'
  1888. ];
  1889. }
  1890. if (sctp.maxMessageSize !== undefined) {
  1891. output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
  1892. }
  1893. return output.join('');
  1894. };
  1895. // Generate a session ID for SDP.
  1896. // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
  1897. // recommends using a cryptographically random +ve 64-bit value
  1898. // but right now this should be acceptable and within the right range
  1899. SDPUtils.generateSessionId = function() {
  1900. return Math.random().toString().substr(2, 21);
  1901. };
  1902. // Write boilder plate for start of SDP
  1903. // sessId argument is optional - if not supplied it will
  1904. // be generated randomly
  1905. // sessVersion is optional and defaults to 2
  1906. // sessUser is optional and defaults to 'thisisadapterortc'
  1907. SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {
  1908. var sessionId;
  1909. var version = sessVer !== undefined ? sessVer : 2;
  1910. if (sessId) {
  1911. sessionId = sessId;
  1912. } else {
  1913. sessionId = SDPUtils.generateSessionId();
  1914. }
  1915. var user = sessUser || 'thisisadapterortc';
  1916. // FIXME: sess-id should be an NTP timestamp.
  1917. return 'v=0\r\n' +
  1918. 'o=' + user + ' ' + sessionId + ' ' + version +
  1919. ' IN IP4 127.0.0.1\r\n' +
  1920. 's=-\r\n' +
  1921. 't=0 0\r\n';
  1922. };
  1923. SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
  1924. var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
  1925. // Map ICE parameters (ufrag, pwd) to SDP.
  1926. sdp += SDPUtils.writeIceParameters(
  1927. transceiver.iceGatherer.getLocalParameters());
  1928. // Map DTLS parameters to SDP.
  1929. sdp += SDPUtils.writeDtlsParameters(
  1930. transceiver.dtlsTransport.getLocalParameters(),
  1931. type === 'offer' ? 'actpass' : 'active');
  1932. sdp += 'a=mid:' + transceiver.mid + '\r\n';
  1933. if (transceiver.direction) {
  1934. sdp += 'a=' + transceiver.direction + '\r\n';
  1935. } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
  1936. sdp += 'a=sendrecv\r\n';
  1937. } else if (transceiver.rtpSender) {
  1938. sdp += 'a=sendonly\r\n';
  1939. } else if (transceiver.rtpReceiver) {
  1940. sdp += 'a=recvonly\r\n';
  1941. } else {
  1942. sdp += 'a=inactive\r\n';
  1943. }
  1944. if (transceiver.rtpSender) {
  1945. // spec.
  1946. var msid = 'msid:' + stream.id + ' ' +
  1947. transceiver.rtpSender.track.id + '\r\n';
  1948. sdp += 'a=' + msid;
  1949. // for Chrome.
  1950. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  1951. ' ' + msid;
  1952. if (transceiver.sendEncodingParameters[0].rtx) {
  1953. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  1954. ' ' + msid;
  1955. sdp += 'a=ssrc-group:FID ' +
  1956. transceiver.sendEncodingParameters[0].ssrc + ' ' +
  1957. transceiver.sendEncodingParameters[0].rtx.ssrc +
  1958. '\r\n';
  1959. }
  1960. }
  1961. // FIXME: this should be written by writeRtpDescription.
  1962. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  1963. ' cname:' + SDPUtils.localCName + '\r\n';
  1964. if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
  1965. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  1966. ' cname:' + SDPUtils.localCName + '\r\n';
  1967. }
  1968. return sdp;
  1969. };
  1970. // Gets the direction from the mediaSection or the sessionpart.
  1971. SDPUtils.getDirection = function(mediaSection, sessionpart) {
  1972. // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
  1973. var lines = SDPUtils.splitLines(mediaSection);
  1974. for (var i = 0; i < lines.length; i++) {
  1975. switch (lines[i]) {
  1976. case 'a=sendrecv':
  1977. case 'a=sendonly':
  1978. case 'a=recvonly':
  1979. case 'a=inactive':
  1980. return lines[i].substr(2);
  1981. // FIXME: What should happen here?
  1982. }
  1983. }
  1984. if (sessionpart) {
  1985. return SDPUtils.getDirection(sessionpart);
  1986. }
  1987. return 'sendrecv';
  1988. };
  1989. SDPUtils.getKind = function(mediaSection) {
  1990. var lines = SDPUtils.splitLines(mediaSection);
  1991. var mline = lines[0].split(' ');
  1992. return mline[0].substr(2);
  1993. };
  1994. SDPUtils.isRejected = function(mediaSection) {
  1995. return mediaSection.split(' ', 2)[1] === '0';
  1996. };
  1997. SDPUtils.parseMLine = function(mediaSection) {
  1998. var lines = SDPUtils.splitLines(mediaSection);
  1999. var parts = lines[0].substr(2).split(' ');
  2000. return {
  2001. kind: parts[0],
  2002. port: parseInt(parts[1], 10),
  2003. protocol: parts[2],
  2004. fmt: parts.slice(3).join(' ')
  2005. };
  2006. };
  2007. SDPUtils.parseOLine = function(mediaSection) {
  2008. var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
  2009. var parts = line.substr(2).split(' ');
  2010. return {
  2011. username: parts[0],
  2012. sessionId: parts[1],
  2013. sessionVersion: parseInt(parts[2], 10),
  2014. netType: parts[3],
  2015. addressType: parts[4],
  2016. address: parts[5]
  2017. };
  2018. };
  2019. // a very naive interpretation of a valid SDP.
  2020. SDPUtils.isValidSDP = function(blob) {
  2021. if (typeof blob !== 'string' || blob.length === 0) {
  2022. return false;
  2023. }
  2024. var lines = SDPUtils.splitLines(blob);
  2025. for (var i = 0; i < lines.length; i++) {
  2026. if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
  2027. return false;
  2028. }
  2029. // TODO: check the modifier a bit more.
  2030. }
  2031. return true;
  2032. };
  2033. // Expose public methods.
  2034. {
  2035. module.exports = SDPUtils;
  2036. }
  2037. });
  2038. /*
  2039. * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  2040. *
  2041. * Use of this source code is governed by a BSD-style license
  2042. * that can be found in the LICENSE file in the root of the source
  2043. * tree.
  2044. */
  2045. function fixStatsType(stat) {
  2046. return {
  2047. inboundrtp: 'inbound-rtp',
  2048. outboundrtp: 'outbound-rtp',
  2049. candidatepair: 'candidate-pair',
  2050. localcandidate: 'local-candidate',
  2051. remotecandidate: 'remote-candidate'
  2052. }[stat.type] || stat.type;
  2053. }
  2054. function writeMediaSection(transceiver, caps, type, stream, dtlsRole) {
  2055. var sdp$1 = sdp.writeRtpDescription(transceiver.kind, caps);
  2056. // Map ICE parameters (ufrag, pwd) to SDP.
  2057. sdp$1 += sdp.writeIceParameters(
  2058. transceiver.iceGatherer.getLocalParameters());
  2059. // Map DTLS parameters to SDP.
  2060. sdp$1 += sdp.writeDtlsParameters(
  2061. transceiver.dtlsTransport.getLocalParameters(),
  2062. type === 'offer' ? 'actpass' : dtlsRole || 'active');
  2063. sdp$1 += 'a=mid:' + transceiver.mid + '\r\n';
  2064. if (transceiver.rtpSender && transceiver.rtpReceiver) {
  2065. sdp$1 += 'a=sendrecv\r\n';
  2066. } else if (transceiver.rtpSender) {
  2067. sdp$1 += 'a=sendonly\r\n';
  2068. } else if (transceiver.rtpReceiver) {
  2069. sdp$1 += 'a=recvonly\r\n';
  2070. } else {
  2071. sdp$1 += 'a=inactive\r\n';
  2072. }
  2073. if (transceiver.rtpSender) {
  2074. var trackId = transceiver.rtpSender._initialTrackId ||
  2075. transceiver.rtpSender.track.id;
  2076. transceiver.rtpSender._initialTrackId = trackId;
  2077. // spec.
  2078. var msid = 'msid:' + (stream ? stream.id : '-') + ' ' +
  2079. trackId + '\r\n';
  2080. sdp$1 += 'a=' + msid;
  2081. // for Chrome. Legacy should no longer be required.
  2082. sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2083. ' ' + msid;
  2084. // RTX
  2085. if (transceiver.sendEncodingParameters[0].rtx) {
  2086. sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2087. ' ' + msid;
  2088. sdp$1 += 'a=ssrc-group:FID ' +
  2089. transceiver.sendEncodingParameters[0].ssrc + ' ' +
  2090. transceiver.sendEncodingParameters[0].rtx.ssrc +
  2091. '\r\n';
  2092. }
  2093. }
  2094. // FIXME: this should be written by writeRtpDescription.
  2095. sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2096. ' cname:' + sdp.localCName + '\r\n';
  2097. if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
  2098. sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2099. ' cname:' + sdp.localCName + '\r\n';
  2100. }
  2101. return sdp$1;
  2102. }
  2103. // Edge does not like
  2104. // 1) stun: filtered after 14393 unless ?transport=udp is present
  2105. // 2) turn: that does not have all of turn:host:port?transport=udp
  2106. // 3) turn: with ipv6 addresses
  2107. // 4) turn: occurring muliple times
  2108. function filterIceServers(iceServers, edgeVersion) {
  2109. var hasTurn = false;
  2110. iceServers = JSON.parse(JSON.stringify(iceServers));
  2111. return iceServers.filter(function(server) {
  2112. if (server && (server.urls || server.url)) {
  2113. var urls = server.urls || server.url;
  2114. if (server.url && !server.urls) {
  2115. console.warn('RTCIceServer.url is deprecated! Use urls instead.');
  2116. }
  2117. var isString = typeof urls === 'string';
  2118. if (isString) {
  2119. urls = [urls];
  2120. }
  2121. urls = urls.filter(function(url) {
  2122. var validTurn = url.indexOf('turn:') === 0 &&
  2123. url.indexOf('transport=udp') !== -1 &&
  2124. url.indexOf('turn:[') === -1 &&
  2125. !hasTurn;
  2126. if (validTurn) {
  2127. hasTurn = true;
  2128. return true;
  2129. }
  2130. return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&
  2131. url.indexOf('?transport=udp') === -1;
  2132. });
  2133. delete server.url;
  2134. server.urls = isString ? urls[0] : urls;
  2135. return !!urls.length;
  2136. }
  2137. });
  2138. }
  2139. // Determines the intersection of local and remote capabilities.
  2140. function getCommonCapabilities(localCapabilities, remoteCapabilities) {
  2141. var commonCapabilities = {
  2142. codecs: [],
  2143. headerExtensions: [],
  2144. fecMechanisms: []
  2145. };
  2146. var findCodecByPayloadType = function(pt, codecs) {
  2147. pt = parseInt(pt, 10);
  2148. for (var i = 0; i < codecs.length; i++) {
  2149. if (codecs[i].payloadType === pt ||
  2150. codecs[i].preferredPayloadType === pt) {
  2151. return codecs[i];
  2152. }
  2153. }
  2154. };
  2155. var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
  2156. var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
  2157. var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
  2158. return lCodec && rCodec &&
  2159. lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
  2160. };
  2161. localCapabilities.codecs.forEach(function(lCodec) {
  2162. for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
  2163. var rCodec = remoteCapabilities.codecs[i];
  2164. if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
  2165. lCodec.clockRate === rCodec.clockRate) {
  2166. if (lCodec.name.toLowerCase() === 'rtx' &&
  2167. lCodec.parameters && rCodec.parameters.apt) {
  2168. // for RTX we need to find the local rtx that has a apt
  2169. // which points to the same local codec as the remote one.
  2170. if (!rtxCapabilityMatches(lCodec, rCodec,
  2171. localCapabilities.codecs, remoteCapabilities.codecs)) {
  2172. continue;
  2173. }
  2174. }
  2175. rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
  2176. // number of channels is the highest common number of channels
  2177. rCodec.numChannels = Math.min(lCodec.numChannels,
  2178. rCodec.numChannels);
  2179. // push rCodec so we reply with offerer payload type
  2180. commonCapabilities.codecs.push(rCodec);
  2181. // determine common feedback mechanisms
  2182. rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
  2183. for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
  2184. if (lCodec.rtcpFeedback[j].type === fb.type &&
  2185. lCodec.rtcpFeedback[j].parameter === fb.parameter) {
  2186. return true;
  2187. }
  2188. }
  2189. return false;
  2190. });
  2191. // FIXME: also need to determine .parameters
  2192. // see https://github.com/openpeer/ortc/issues/569
  2193. break;
  2194. }
  2195. }
  2196. });
  2197. localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
  2198. for (var i = 0; i < remoteCapabilities.headerExtensions.length;
  2199. i++) {
  2200. var rHeaderExtension = remoteCapabilities.headerExtensions[i];
  2201. if (lHeaderExtension.uri === rHeaderExtension.uri) {
  2202. commonCapabilities.headerExtensions.push(rHeaderExtension);
  2203. break;
  2204. }
  2205. }
  2206. });
  2207. // FIXME: fecMechanisms
  2208. return commonCapabilities;
  2209. }
  2210. // is action=setLocalDescription with type allowed in signalingState
  2211. function isActionAllowedInSignalingState(action, type, signalingState) {
  2212. return {
  2213. offer: {
  2214. setLocalDescription: ['stable', 'have-local-offer'],
  2215. setRemoteDescription: ['stable', 'have-remote-offer']
  2216. },
  2217. answer: {
  2218. setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
  2219. setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
  2220. }
  2221. }[type][action].indexOf(signalingState) !== -1;
  2222. }
  2223. function maybeAddCandidate(iceTransport, candidate) {
  2224. // Edge's internal representation adds some fields therefore
  2225. // not all fieldѕ are taken into account.
  2226. var alreadyAdded = iceTransport.getRemoteCandidates()
  2227. .find(function(remoteCandidate) {
  2228. return candidate.foundation === remoteCandidate.foundation &&
  2229. candidate.ip === remoteCandidate.ip &&
  2230. candidate.port === remoteCandidate.port &&
  2231. candidate.priority === remoteCandidate.priority &&
  2232. candidate.protocol === remoteCandidate.protocol &&
  2233. candidate.type === remoteCandidate.type;
  2234. });
  2235. if (!alreadyAdded) {
  2236. iceTransport.addRemoteCandidate(candidate);
  2237. }
  2238. return !alreadyAdded;
  2239. }
  2240. function makeError(name, description) {
  2241. var e = new Error(description);
  2242. e.name = name;
  2243. // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names
  2244. e.code = {
  2245. NotSupportedError: 9,
  2246. InvalidStateError: 11,
  2247. InvalidAccessError: 15,
  2248. TypeError: undefined,
  2249. OperationError: undefined
  2250. }[name];
  2251. return e;
  2252. }
  2253. var rtcpeerconnection = function(window, edgeVersion) {
  2254. // https://w3c.github.io/mediacapture-main/#mediastream
  2255. // Helper function to add the track to the stream and
  2256. // dispatch the event ourselves.
  2257. function addTrackToStreamAndFireEvent(track, stream) {
  2258. stream.addTrack(track);
  2259. stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack',
  2260. {track: track}));
  2261. }
  2262. function removeTrackFromStreamAndFireEvent(track, stream) {
  2263. stream.removeTrack(track);
  2264. stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack',
  2265. {track: track}));
  2266. }
  2267. function fireAddTrack(pc, track, receiver, streams) {
  2268. var trackEvent = new Event('track');
  2269. trackEvent.track = track;
  2270. trackEvent.receiver = receiver;
  2271. trackEvent.transceiver = {receiver: receiver};
  2272. trackEvent.streams = streams;
  2273. window.setTimeout(function() {
  2274. pc._dispatchEvent('track', trackEvent);
  2275. });
  2276. }
  2277. var RTCPeerConnection = function(config) {
  2278. var pc = this;
  2279. var _eventTarget = document.createDocumentFragment();
  2280. ['addEventListener', 'removeEventListener', 'dispatchEvent']
  2281. .forEach(function(method) {
  2282. pc[method] = _eventTarget[method].bind(_eventTarget);
  2283. });
  2284. this.canTrickleIceCandidates = null;
  2285. this.needNegotiation = false;
  2286. this.localStreams = [];
  2287. this.remoteStreams = [];
  2288. this._localDescription = null;
  2289. this._remoteDescription = null;
  2290. this.signalingState = 'stable';
  2291. this.iceConnectionState = 'new';
  2292. this.connectionState = 'new';
  2293. this.iceGatheringState = 'new';
  2294. config = JSON.parse(JSON.stringify(config || {}));
  2295. this.usingBundle = config.bundlePolicy === 'max-bundle';
  2296. if (config.rtcpMuxPolicy === 'negotiate') {
  2297. throw(makeError('NotSupportedError',
  2298. 'rtcpMuxPolicy \'negotiate\' is not supported'));
  2299. } else if (!config.rtcpMuxPolicy) {
  2300. config.rtcpMuxPolicy = 'require';
  2301. }
  2302. switch (config.iceTransportPolicy) {
  2303. case 'all':
  2304. case 'relay':
  2305. break;
  2306. default:
  2307. config.iceTransportPolicy = 'all';
  2308. break;
  2309. }
  2310. switch (config.bundlePolicy) {
  2311. case 'balanced':
  2312. case 'max-compat':
  2313. case 'max-bundle':
  2314. break;
  2315. default:
  2316. config.bundlePolicy = 'balanced';
  2317. break;
  2318. }
  2319. config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);
  2320. this._iceGatherers = [];
  2321. if (config.iceCandidatePoolSize) {
  2322. for (var i = config.iceCandidatePoolSize; i > 0; i--) {
  2323. this._iceGatherers.push(new window.RTCIceGatherer({
  2324. iceServers: config.iceServers,
  2325. gatherPolicy: config.iceTransportPolicy
  2326. }));
  2327. }
  2328. } else {
  2329. config.iceCandidatePoolSize = 0;
  2330. }
  2331. this._config = config;
  2332. // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
  2333. // everything that is needed to describe a SDP m-line.
  2334. this.transceivers = [];
  2335. this._sdpSessionId = sdp.generateSessionId();
  2336. this._sdpSessionVersion = 0;
  2337. this._dtlsRole = undefined; // role for a=setup to use in answers.
  2338. this._isClosed = false;
  2339. };
  2340. Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {
  2341. configurable: true,
  2342. get: function() {
  2343. return this._localDescription;
  2344. }
  2345. });
  2346. Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {
  2347. configurable: true,
  2348. get: function() {
  2349. return this._remoteDescription;
  2350. }
  2351. });
  2352. // set up event handlers on prototype
  2353. RTCPeerConnection.prototype.onicecandidate = null;
  2354. RTCPeerConnection.prototype.onaddstream = null;
  2355. RTCPeerConnection.prototype.ontrack = null;
  2356. RTCPeerConnection.prototype.onremovestream = null;
  2357. RTCPeerConnection.prototype.onsignalingstatechange = null;
  2358. RTCPeerConnection.prototype.oniceconnectionstatechange = null;
  2359. RTCPeerConnection.prototype.onconnectionstatechange = null;
  2360. RTCPeerConnection.prototype.onicegatheringstatechange = null;
  2361. RTCPeerConnection.prototype.onnegotiationneeded = null;
  2362. RTCPeerConnection.prototype.ondatachannel = null;
  2363. RTCPeerConnection.prototype._dispatchEvent = function(name, event) {
  2364. if (this._isClosed) {
  2365. return;
  2366. }
  2367. this.dispatchEvent(event);
  2368. if (typeof this['on' + name] === 'function') {
  2369. this['on' + name](event);
  2370. }
  2371. };
  2372. RTCPeerConnection.prototype._emitGatheringStateChange = function() {
  2373. var event = new Event('icegatheringstatechange');
  2374. this._dispatchEvent('icegatheringstatechange', event);
  2375. };
  2376. RTCPeerConnection.prototype.getConfiguration = function() {
  2377. return this._config;
  2378. };
  2379. RTCPeerConnection.prototype.getLocalStreams = function() {
  2380. return this.localStreams;
  2381. };
  2382. RTCPeerConnection.prototype.getRemoteStreams = function() {
  2383. return this.remoteStreams;
  2384. };
  2385. // internal helper to create a transceiver object.
  2386. // (which is not yet the same as the WebRTC 1.0 transceiver)
  2387. RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) {
  2388. var hasBundleTransport = this.transceivers.length > 0;
  2389. var transceiver = {
  2390. track: null,
  2391. iceGatherer: null,
  2392. iceTransport: null,
  2393. dtlsTransport: null,
  2394. localCapabilities: null,
  2395. remoteCapabilities: null,
  2396. rtpSender: null,
  2397. rtpReceiver: null,
  2398. kind: kind,
  2399. mid: null,
  2400. sendEncodingParameters: null,
  2401. recvEncodingParameters: null,
  2402. stream: null,
  2403. associatedRemoteMediaStreams: [],
  2404. wantReceive: true
  2405. };
  2406. if (this.usingBundle && hasBundleTransport) {
  2407. transceiver.iceTransport = this.transceivers[0].iceTransport;
  2408. transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
  2409. } else {
  2410. var transports = this._createIceAndDtlsTransports();
  2411. transceiver.iceTransport = transports.iceTransport;
  2412. transceiver.dtlsTransport = transports.dtlsTransport;
  2413. }
  2414. if (!doNotAdd) {
  2415. this.transceivers.push(transceiver);
  2416. }
  2417. return transceiver;
  2418. };
  2419. RTCPeerConnection.prototype.addTrack = function(track, stream) {
  2420. if (this._isClosed) {
  2421. throw makeError('InvalidStateError',
  2422. 'Attempted to call addTrack on a closed peerconnection.');
  2423. }
  2424. var alreadyExists = this.transceivers.find(function(s) {
  2425. return s.track === track;
  2426. });
  2427. if (alreadyExists) {
  2428. throw makeError('InvalidAccessError', 'Track already exists.');
  2429. }
  2430. var transceiver;
  2431. for (var i = 0; i < this.transceivers.length; i++) {
  2432. if (!this.transceivers[i].track &&
  2433. this.transceivers[i].kind === track.kind) {
  2434. transceiver = this.transceivers[i];
  2435. }
  2436. }
  2437. if (!transceiver) {
  2438. transceiver = this._createTransceiver(track.kind);
  2439. }
  2440. this._maybeFireNegotiationNeeded();
  2441. if (this.localStreams.indexOf(stream) === -1) {
  2442. this.localStreams.push(stream);
  2443. }
  2444. transceiver.track = track;
  2445. transceiver.stream = stream;
  2446. transceiver.rtpSender = new window.RTCRtpSender(track,
  2447. transceiver.dtlsTransport);
  2448. return transceiver.rtpSender;
  2449. };
  2450. RTCPeerConnection.prototype.addStream = function(stream) {
  2451. var pc = this;
  2452. if (edgeVersion >= 15025) {
  2453. stream.getTracks().forEach(function(track) {
  2454. pc.addTrack(track, stream);
  2455. });
  2456. } else {
  2457. // Clone is necessary for local demos mostly, attaching directly
  2458. // to two different senders does not work (build 10547).
  2459. // Fixed in 15025 (or earlier)
  2460. var clonedStream = stream.clone();
  2461. stream.getTracks().forEach(function(track, idx) {
  2462. var clonedTrack = clonedStream.getTracks()[idx];
  2463. track.addEventListener('enabled', function(event) {
  2464. clonedTrack.enabled = event.enabled;
  2465. });
  2466. });
  2467. clonedStream.getTracks().forEach(function(track) {
  2468. pc.addTrack(track, clonedStream);
  2469. });
  2470. }
  2471. };
  2472. RTCPeerConnection.prototype.removeTrack = function(sender) {
  2473. if (this._isClosed) {
  2474. throw makeError('InvalidStateError',
  2475. 'Attempted to call removeTrack on a closed peerconnection.');
  2476. }
  2477. if (!(sender instanceof window.RTCRtpSender)) {
  2478. throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +
  2479. 'does not implement interface RTCRtpSender.');
  2480. }
  2481. var transceiver = this.transceivers.find(function(t) {
  2482. return t.rtpSender === sender;
  2483. });
  2484. if (!transceiver) {
  2485. throw makeError('InvalidAccessError',
  2486. 'Sender was not created by this connection.');
  2487. }
  2488. var stream = transceiver.stream;
  2489. transceiver.rtpSender.stop();
  2490. transceiver.rtpSender = null;
  2491. transceiver.track = null;
  2492. transceiver.stream = null;
  2493. // remove the stream from the set of local streams
  2494. var localStreams = this.transceivers.map(function(t) {
  2495. return t.stream;
  2496. });
  2497. if (localStreams.indexOf(stream) === -1 &&
  2498. this.localStreams.indexOf(stream) > -1) {
  2499. this.localStreams.splice(this.localStreams.indexOf(stream), 1);
  2500. }
  2501. this._maybeFireNegotiationNeeded();
  2502. };
  2503. RTCPeerConnection.prototype.removeStream = function(stream) {
  2504. var pc = this;
  2505. stream.getTracks().forEach(function(track) {
  2506. var sender = pc.getSenders().find(function(s) {
  2507. return s.track === track;
  2508. });
  2509. if (sender) {
  2510. pc.removeTrack(sender);
  2511. }
  2512. });
  2513. };
  2514. RTCPeerConnection.prototype.getSenders = function() {
  2515. return this.transceivers.filter(function(transceiver) {
  2516. return !!transceiver.rtpSender;
  2517. })
  2518. .map(function(transceiver) {
  2519. return transceiver.rtpSender;
  2520. });
  2521. };
  2522. RTCPeerConnection.prototype.getReceivers = function() {
  2523. return this.transceivers.filter(function(transceiver) {
  2524. return !!transceiver.rtpReceiver;
  2525. })
  2526. .map(function(transceiver) {
  2527. return transceiver.rtpReceiver;
  2528. });
  2529. };
  2530. RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,
  2531. usingBundle) {
  2532. var pc = this;
  2533. if (usingBundle && sdpMLineIndex > 0) {
  2534. return this.transceivers[0].iceGatherer;
  2535. } else if (this._iceGatherers.length) {
  2536. return this._iceGatherers.shift();
  2537. }
  2538. var iceGatherer = new window.RTCIceGatherer({
  2539. iceServers: this._config.iceServers,
  2540. gatherPolicy: this._config.iceTransportPolicy
  2541. });
  2542. Object.defineProperty(iceGatherer, 'state',
  2543. {value: 'new', writable: true}
  2544. );
  2545. this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];
  2546. this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {
  2547. var end = !event.candidate || Object.keys(event.candidate).length === 0;
  2548. // polyfill since RTCIceGatherer.state is not implemented in
  2549. // Edge 10547 yet.
  2550. iceGatherer.state = end ? 'completed' : 'gathering';
  2551. if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {
  2552. pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);
  2553. }
  2554. };
  2555. iceGatherer.addEventListener('localcandidate',
  2556. this.transceivers[sdpMLineIndex].bufferCandidates);
  2557. return iceGatherer;
  2558. };
  2559. // start gathering from an RTCIceGatherer.
  2560. RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {
  2561. var pc = this;
  2562. var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
  2563. if (iceGatherer.onlocalcandidate) {
  2564. return;
  2565. }
  2566. var bufferedCandidateEvents =
  2567. this.transceivers[sdpMLineIndex].bufferedCandidateEvents;
  2568. this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;
  2569. iceGatherer.removeEventListener('localcandidate',
  2570. this.transceivers[sdpMLineIndex].bufferCandidates);
  2571. iceGatherer.onlocalcandidate = function(evt) {
  2572. if (pc.usingBundle && sdpMLineIndex > 0) {
  2573. // if we know that we use bundle we can drop candidates with
  2574. // ѕdpMLineIndex > 0. If we don't do this then our state gets
  2575. // confused since we dispose the extra ice gatherer.
  2576. return;
  2577. }
  2578. var event = new Event('icecandidate');
  2579. event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
  2580. var cand = evt.candidate;
  2581. // Edge emits an empty object for RTCIceCandidateComplete‥
  2582. var end = !cand || Object.keys(cand).length === 0;
  2583. if (end) {
  2584. // polyfill since RTCIceGatherer.state is not implemented in
  2585. // Edge 10547 yet.
  2586. if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {
  2587. iceGatherer.state = 'completed';
  2588. }
  2589. } else {
  2590. if (iceGatherer.state === 'new') {
  2591. iceGatherer.state = 'gathering';
  2592. }
  2593. // RTCIceCandidate doesn't have a component, needs to be added
  2594. cand.component = 1;
  2595. // also the usernameFragment. TODO: update SDP to take both variants.
  2596. cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;
  2597. var serializedCandidate = sdp.writeCandidate(cand);
  2598. event.candidate = Object.assign(event.candidate,
  2599. sdp.parseCandidate(serializedCandidate));
  2600. event.candidate.candidate = serializedCandidate;
  2601. event.candidate.toJSON = function() {
  2602. return {
  2603. candidate: event.candidate.candidate,
  2604. sdpMid: event.candidate.sdpMid,
  2605. sdpMLineIndex: event.candidate.sdpMLineIndex,
  2606. usernameFragment: event.candidate.usernameFragment
  2607. };
  2608. };
  2609. }
  2610. // update local description.
  2611. var sections = sdp.getMediaSections(pc._localDescription.sdp);
  2612. if (!end) {
  2613. sections[event.candidate.sdpMLineIndex] +=
  2614. 'a=' + event.candidate.candidate + '\r\n';
  2615. } else {
  2616. sections[event.candidate.sdpMLineIndex] +=
  2617. 'a=end-of-candidates\r\n';
  2618. }
  2619. pc._localDescription.sdp =
  2620. sdp.getDescription(pc._localDescription.sdp) +
  2621. sections.join('');
  2622. var complete = pc.transceivers.every(function(transceiver) {
  2623. return transceiver.iceGatherer &&
  2624. transceiver.iceGatherer.state === 'completed';
  2625. });
  2626. if (pc.iceGatheringState !== 'gathering') {
  2627. pc.iceGatheringState = 'gathering';
  2628. pc._emitGatheringStateChange();
  2629. }
  2630. // Emit candidate. Also emit null candidate when all gatherers are
  2631. // complete.
  2632. if (!end) {
  2633. pc._dispatchEvent('icecandidate', event);
  2634. }
  2635. if (complete) {
  2636. pc._dispatchEvent('icecandidate', new Event('icecandidate'));
  2637. pc.iceGatheringState = 'complete';
  2638. pc._emitGatheringStateChange();
  2639. }
  2640. };
  2641. // emit already gathered candidates.
  2642. window.setTimeout(function() {
  2643. bufferedCandidateEvents.forEach(function(e) {
  2644. iceGatherer.onlocalcandidate(e);
  2645. });
  2646. }, 0);
  2647. };
  2648. // Create ICE transport and DTLS transport.
  2649. RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
  2650. var pc = this;
  2651. var iceTransport = new window.RTCIceTransport(null);
  2652. iceTransport.onicestatechange = function() {
  2653. pc._updateIceConnectionState();
  2654. pc._updateConnectionState();
  2655. };
  2656. var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
  2657. dtlsTransport.ondtlsstatechange = function() {
  2658. pc._updateConnectionState();
  2659. };
  2660. dtlsTransport.onerror = function() {
  2661. // onerror does not set state to failed by itself.
  2662. Object.defineProperty(dtlsTransport, 'state',
  2663. {value: 'failed', writable: true});
  2664. pc._updateConnectionState();
  2665. };
  2666. return {
  2667. iceTransport: iceTransport,
  2668. dtlsTransport: dtlsTransport
  2669. };
  2670. };
  2671. // Destroy ICE gatherer, ICE transport and DTLS transport.
  2672. // Without triggering the callbacks.
  2673. RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
  2674. sdpMLineIndex) {
  2675. var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
  2676. if (iceGatherer) {
  2677. delete iceGatherer.onlocalcandidate;
  2678. delete this.transceivers[sdpMLineIndex].iceGatherer;
  2679. }
  2680. var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
  2681. if (iceTransport) {
  2682. delete iceTransport.onicestatechange;
  2683. delete this.transceivers[sdpMLineIndex].iceTransport;
  2684. }
  2685. var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
  2686. if (dtlsTransport) {
  2687. delete dtlsTransport.ondtlsstatechange;
  2688. delete dtlsTransport.onerror;
  2689. delete this.transceivers[sdpMLineIndex].dtlsTransport;
  2690. }
  2691. };
  2692. // Start the RTP Sender and Receiver for a transceiver.
  2693. RTCPeerConnection.prototype._transceive = function(transceiver,
  2694. send, recv) {
  2695. var params = getCommonCapabilities(transceiver.localCapabilities,
  2696. transceiver.remoteCapabilities);
  2697. if (send && transceiver.rtpSender) {
  2698. params.encodings = transceiver.sendEncodingParameters;
  2699. params.rtcp = {
  2700. cname: sdp.localCName,
  2701. compound: transceiver.rtcpParameters.compound
  2702. };
  2703. if (transceiver.recvEncodingParameters.length) {
  2704. params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
  2705. }
  2706. transceiver.rtpSender.send(params);
  2707. }
  2708. if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {
  2709. // remove RTX field in Edge 14942
  2710. if (transceiver.kind === 'video'
  2711. && transceiver.recvEncodingParameters
  2712. && edgeVersion < 15019) {
  2713. transceiver.recvEncodingParameters.forEach(function(p) {
  2714. delete p.rtx;
  2715. });
  2716. }
  2717. if (transceiver.recvEncodingParameters.length) {
  2718. params.encodings = transceiver.recvEncodingParameters;
  2719. } else {
  2720. params.encodings = [{}];
  2721. }
  2722. params.rtcp = {
  2723. compound: transceiver.rtcpParameters.compound
  2724. };
  2725. if (transceiver.rtcpParameters.cname) {
  2726. params.rtcp.cname = transceiver.rtcpParameters.cname;
  2727. }
  2728. if (transceiver.sendEncodingParameters.length) {
  2729. params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
  2730. }
  2731. transceiver.rtpReceiver.receive(params);
  2732. }
  2733. };
  2734. RTCPeerConnection.prototype.setLocalDescription = function(description) {
  2735. var pc = this;
  2736. // Note: pranswer is not supported.
  2737. if (['offer', 'answer'].indexOf(description.type) === -1) {
  2738. return Promise.reject(makeError('TypeError',
  2739. 'Unsupported type "' + description.type + '"'));
  2740. }
  2741. if (!isActionAllowedInSignalingState('setLocalDescription',
  2742. description.type, pc.signalingState) || pc._isClosed) {
  2743. return Promise.reject(makeError('InvalidStateError',
  2744. 'Can not set local ' + description.type +
  2745. ' in state ' + pc.signalingState));
  2746. }
  2747. var sections;
  2748. var sessionpart;
  2749. if (description.type === 'offer') {
  2750. // VERY limited support for SDP munging. Limited to:
  2751. // * changing the order of codecs
  2752. sections = sdp.splitSections(description.sdp);
  2753. sessionpart = sections.shift();
  2754. sections.forEach(function(mediaSection, sdpMLineIndex) {
  2755. var caps = sdp.parseRtpParameters(mediaSection);
  2756. pc.transceivers[sdpMLineIndex].localCapabilities = caps;
  2757. });
  2758. pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  2759. pc._gather(transceiver.mid, sdpMLineIndex);
  2760. });
  2761. } else if (description.type === 'answer') {
  2762. sections = sdp.splitSections(pc._remoteDescription.sdp);
  2763. sessionpart = sections.shift();
  2764. var isIceLite = sdp.matchPrefix(sessionpart,
  2765. 'a=ice-lite').length > 0;
  2766. sections.forEach(function(mediaSection, sdpMLineIndex) {
  2767. var transceiver = pc.transceivers[sdpMLineIndex];
  2768. var iceGatherer = transceiver.iceGatherer;
  2769. var iceTransport = transceiver.iceTransport;
  2770. var dtlsTransport = transceiver.dtlsTransport;
  2771. var localCapabilities = transceiver.localCapabilities;
  2772. var remoteCapabilities = transceiver.remoteCapabilities;
  2773. // treat bundle-only as not-rejected.
  2774. var rejected = sdp.isRejected(mediaSection) &&
  2775. sdp.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
  2776. if (!rejected && !transceiver.rejected) {
  2777. var remoteIceParameters = sdp.getIceParameters(
  2778. mediaSection, sessionpart);
  2779. var remoteDtlsParameters = sdp.getDtlsParameters(
  2780. mediaSection, sessionpart);
  2781. if (isIceLite) {
  2782. remoteDtlsParameters.role = 'server';
  2783. }
  2784. if (!pc.usingBundle || sdpMLineIndex === 0) {
  2785. pc._gather(transceiver.mid, sdpMLineIndex);
  2786. if (iceTransport.state === 'new') {
  2787. iceTransport.start(iceGatherer, remoteIceParameters,
  2788. isIceLite ? 'controlling' : 'controlled');
  2789. }
  2790. if (dtlsTransport.state === 'new') {
  2791. dtlsTransport.start(remoteDtlsParameters);
  2792. }
  2793. }
  2794. // Calculate intersection of capabilities.
  2795. var params = getCommonCapabilities(localCapabilities,
  2796. remoteCapabilities);
  2797. // Start the RTCRtpSender. The RTCRtpReceiver for this
  2798. // transceiver has already been started in setRemoteDescription.
  2799. pc._transceive(transceiver,
  2800. params.codecs.length > 0,
  2801. false);
  2802. }
  2803. });
  2804. }
  2805. pc._localDescription = {
  2806. type: description.type,
  2807. sdp: description.sdp
  2808. };
  2809. if (description.type === 'offer') {
  2810. pc._updateSignalingState('have-local-offer');
  2811. } else {
  2812. pc._updateSignalingState('stable');
  2813. }
  2814. return Promise.resolve();
  2815. };
  2816. RTCPeerConnection.prototype.setRemoteDescription = function(description) {
  2817. var pc = this;
  2818. // Note: pranswer is not supported.
  2819. if (['offer', 'answer'].indexOf(description.type) === -1) {
  2820. return Promise.reject(makeError('TypeError',
  2821. 'Unsupported type "' + description.type + '"'));
  2822. }
  2823. if (!isActionAllowedInSignalingState('setRemoteDescription',
  2824. description.type, pc.signalingState) || pc._isClosed) {
  2825. return Promise.reject(makeError('InvalidStateError',
  2826. 'Can not set remote ' + description.type +
  2827. ' in state ' + pc.signalingState));
  2828. }
  2829. var streams = {};
  2830. pc.remoteStreams.forEach(function(stream) {
  2831. streams[stream.id] = stream;
  2832. });
  2833. var receiverList = [];
  2834. var sections = sdp.splitSections(description.sdp);
  2835. var sessionpart = sections.shift();
  2836. var isIceLite = sdp.matchPrefix(sessionpart,
  2837. 'a=ice-lite').length > 0;
  2838. var usingBundle = sdp.matchPrefix(sessionpart,
  2839. 'a=group:BUNDLE ').length > 0;
  2840. pc.usingBundle = usingBundle;
  2841. var iceOptions = sdp.matchPrefix(sessionpart,
  2842. 'a=ice-options:')[0];
  2843. if (iceOptions) {
  2844. pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
  2845. .indexOf('trickle') >= 0;
  2846. } else {
  2847. pc.canTrickleIceCandidates = false;
  2848. }
  2849. sections.forEach(function(mediaSection, sdpMLineIndex) {
  2850. var lines = sdp.splitLines(mediaSection);
  2851. var kind = sdp.getKind(mediaSection);
  2852. // treat bundle-only as not-rejected.
  2853. var rejected = sdp.isRejected(mediaSection) &&
  2854. sdp.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
  2855. var protocol = lines[0].substr(2).split(' ')[2];
  2856. var direction = sdp.getDirection(mediaSection, sessionpart);
  2857. var remoteMsid = sdp.parseMsid(mediaSection);
  2858. var mid = sdp.getMid(mediaSection) || sdp.generateIdentifier();
  2859. // Reject datachannels which are not implemented yet.
  2860. if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' ||
  2861. protocol === 'UDP/DTLS/SCTP'))) {
  2862. // TODO: this is dangerous in the case where a non-rejected m-line
  2863. // becomes rejected.
  2864. pc.transceivers[sdpMLineIndex] = {
  2865. mid: mid,
  2866. kind: kind,
  2867. protocol: protocol,
  2868. rejected: true
  2869. };
  2870. return;
  2871. }
  2872. if (!rejected && pc.transceivers[sdpMLineIndex] &&
  2873. pc.transceivers[sdpMLineIndex].rejected) {
  2874. // recycle a rejected transceiver.
  2875. pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);
  2876. }
  2877. var transceiver;
  2878. var iceGatherer;
  2879. var iceTransport;
  2880. var dtlsTransport;
  2881. var rtpReceiver;
  2882. var sendEncodingParameters;
  2883. var recvEncodingParameters;
  2884. var localCapabilities;
  2885. var track;
  2886. // FIXME: ensure the mediaSection has rtcp-mux set.
  2887. var remoteCapabilities = sdp.parseRtpParameters(mediaSection);
  2888. var remoteIceParameters;
  2889. var remoteDtlsParameters;
  2890. if (!rejected) {
  2891. remoteIceParameters = sdp.getIceParameters(mediaSection,
  2892. sessionpart);
  2893. remoteDtlsParameters = sdp.getDtlsParameters(mediaSection,
  2894. sessionpart);
  2895. remoteDtlsParameters.role = 'client';
  2896. }
  2897. recvEncodingParameters =
  2898. sdp.parseRtpEncodingParameters(mediaSection);
  2899. var rtcpParameters = sdp.parseRtcpParameters(mediaSection);
  2900. var isComplete = sdp.matchPrefix(mediaSection,
  2901. 'a=end-of-candidates', sessionpart).length > 0;
  2902. var cands = sdp.matchPrefix(mediaSection, 'a=candidate:')
  2903. .map(function(cand) {
  2904. return sdp.parseCandidate(cand);
  2905. })
  2906. .filter(function(cand) {
  2907. return cand.component === 1;
  2908. });
  2909. // Check if we can use BUNDLE and dispose transports.
  2910. if ((description.type === 'offer' || description.type === 'answer') &&
  2911. !rejected && usingBundle && sdpMLineIndex > 0 &&
  2912. pc.transceivers[sdpMLineIndex]) {
  2913. pc._disposeIceAndDtlsTransports(sdpMLineIndex);
  2914. pc.transceivers[sdpMLineIndex].iceGatherer =
  2915. pc.transceivers[0].iceGatherer;
  2916. pc.transceivers[sdpMLineIndex].iceTransport =
  2917. pc.transceivers[0].iceTransport;
  2918. pc.transceivers[sdpMLineIndex].dtlsTransport =
  2919. pc.transceivers[0].dtlsTransport;
  2920. if (pc.transceivers[sdpMLineIndex].rtpSender) {
  2921. pc.transceivers[sdpMLineIndex].rtpSender.setTransport(
  2922. pc.transceivers[0].dtlsTransport);
  2923. }
  2924. if (pc.transceivers[sdpMLineIndex].rtpReceiver) {
  2925. pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
  2926. pc.transceivers[0].dtlsTransport);
  2927. }
  2928. }
  2929. if (description.type === 'offer' && !rejected) {
  2930. transceiver = pc.transceivers[sdpMLineIndex] ||
  2931. pc._createTransceiver(kind);
  2932. transceiver.mid = mid;
  2933. if (!transceiver.iceGatherer) {
  2934. transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
  2935. usingBundle);
  2936. }
  2937. if (cands.length && transceiver.iceTransport.state === 'new') {
  2938. if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
  2939. transceiver.iceTransport.setRemoteCandidates(cands);
  2940. } else {
  2941. cands.forEach(function(candidate) {
  2942. maybeAddCandidate(transceiver.iceTransport, candidate);
  2943. });
  2944. }
  2945. }
  2946. localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
  2947. // filter RTX until additional stuff needed for RTX is implemented
  2948. // in adapter.js
  2949. if (edgeVersion < 15019) {
  2950. localCapabilities.codecs = localCapabilities.codecs.filter(
  2951. function(codec) {
  2952. return codec.name !== 'rtx';
  2953. });
  2954. }
  2955. sendEncodingParameters = transceiver.sendEncodingParameters || [{
  2956. ssrc: (2 * sdpMLineIndex + 2) * 1001
  2957. }];
  2958. // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
  2959. var isNewTrack = false;
  2960. if (direction === 'sendrecv' || direction === 'sendonly') {
  2961. isNewTrack = !transceiver.rtpReceiver;
  2962. rtpReceiver = transceiver.rtpReceiver ||
  2963. new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);
  2964. if (isNewTrack) {
  2965. var stream;
  2966. track = rtpReceiver.track;
  2967. // FIXME: does not work with Plan B.
  2968. if (remoteMsid && remoteMsid.stream === '-') ; else if (remoteMsid) {
  2969. if (!streams[remoteMsid.stream]) {
  2970. streams[remoteMsid.stream] = new window.MediaStream();
  2971. Object.defineProperty(streams[remoteMsid.stream], 'id', {
  2972. get: function() {
  2973. return remoteMsid.stream;
  2974. }
  2975. });
  2976. }
  2977. Object.defineProperty(track, 'id', {
  2978. get: function() {
  2979. return remoteMsid.track;
  2980. }
  2981. });
  2982. stream = streams[remoteMsid.stream];
  2983. } else {
  2984. if (!streams.default) {
  2985. streams.default = new window.MediaStream();
  2986. }
  2987. stream = streams.default;
  2988. }
  2989. if (stream) {
  2990. addTrackToStreamAndFireEvent(track, stream);
  2991. transceiver.associatedRemoteMediaStreams.push(stream);
  2992. }
  2993. receiverList.push([track, rtpReceiver, stream]);
  2994. }
  2995. } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {
  2996. transceiver.associatedRemoteMediaStreams.forEach(function(s) {
  2997. var nativeTrack = s.getTracks().find(function(t) {
  2998. return t.id === transceiver.rtpReceiver.track.id;
  2999. });
  3000. if (nativeTrack) {
  3001. removeTrackFromStreamAndFireEvent(nativeTrack, s);
  3002. }
  3003. });
  3004. transceiver.associatedRemoteMediaStreams = [];
  3005. }
  3006. transceiver.localCapabilities = localCapabilities;
  3007. transceiver.remoteCapabilities = remoteCapabilities;
  3008. transceiver.rtpReceiver = rtpReceiver;
  3009. transceiver.rtcpParameters = rtcpParameters;
  3010. transceiver.sendEncodingParameters = sendEncodingParameters;
  3011. transceiver.recvEncodingParameters = recvEncodingParameters;
  3012. // Start the RTCRtpReceiver now. The RTPSender is started in
  3013. // setLocalDescription.
  3014. pc._transceive(pc.transceivers[sdpMLineIndex],
  3015. false,
  3016. isNewTrack);
  3017. } else if (description.type === 'answer' && !rejected) {
  3018. transceiver = pc.transceivers[sdpMLineIndex];
  3019. iceGatherer = transceiver.iceGatherer;
  3020. iceTransport = transceiver.iceTransport;
  3021. dtlsTransport = transceiver.dtlsTransport;
  3022. rtpReceiver = transceiver.rtpReceiver;
  3023. sendEncodingParameters = transceiver.sendEncodingParameters;
  3024. localCapabilities = transceiver.localCapabilities;
  3025. pc.transceivers[sdpMLineIndex].recvEncodingParameters =
  3026. recvEncodingParameters;
  3027. pc.transceivers[sdpMLineIndex].remoteCapabilities =
  3028. remoteCapabilities;
  3029. pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
  3030. if (cands.length && iceTransport.state === 'new') {
  3031. if ((isIceLite || isComplete) &&
  3032. (!usingBundle || sdpMLineIndex === 0)) {
  3033. iceTransport.setRemoteCandidates(cands);
  3034. } else {
  3035. cands.forEach(function(candidate) {
  3036. maybeAddCandidate(transceiver.iceTransport, candidate);
  3037. });
  3038. }
  3039. }
  3040. if (!usingBundle || sdpMLineIndex === 0) {
  3041. if (iceTransport.state === 'new') {
  3042. iceTransport.start(iceGatherer, remoteIceParameters,
  3043. 'controlling');
  3044. }
  3045. if (dtlsTransport.state === 'new') {
  3046. dtlsTransport.start(remoteDtlsParameters);
  3047. }
  3048. }
  3049. // If the offer contained RTX but the answer did not,
  3050. // remove RTX from sendEncodingParameters.
  3051. var commonCapabilities = getCommonCapabilities(
  3052. transceiver.localCapabilities,
  3053. transceiver.remoteCapabilities);
  3054. var hasRtx = commonCapabilities.codecs.filter(function(c) {
  3055. return c.name.toLowerCase() === 'rtx';
  3056. }).length;
  3057. if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
  3058. delete transceiver.sendEncodingParameters[0].rtx;
  3059. }
  3060. pc._transceive(transceiver,
  3061. direction === 'sendrecv' || direction === 'recvonly',
  3062. direction === 'sendrecv' || direction === 'sendonly');
  3063. // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
  3064. if (rtpReceiver &&
  3065. (direction === 'sendrecv' || direction === 'sendonly')) {
  3066. track = rtpReceiver.track;
  3067. if (remoteMsid) {
  3068. if (!streams[remoteMsid.stream]) {
  3069. streams[remoteMsid.stream] = new window.MediaStream();
  3070. }
  3071. addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);
  3072. receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
  3073. } else {
  3074. if (!streams.default) {
  3075. streams.default = new window.MediaStream();
  3076. }
  3077. addTrackToStreamAndFireEvent(track, streams.default);
  3078. receiverList.push([track, rtpReceiver, streams.default]);
  3079. }
  3080. } else {
  3081. // FIXME: actually the receiver should be created later.
  3082. delete transceiver.rtpReceiver;
  3083. }
  3084. }
  3085. });
  3086. if (pc._dtlsRole === undefined) {
  3087. pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';
  3088. }
  3089. pc._remoteDescription = {
  3090. type: description.type,
  3091. sdp: description.sdp
  3092. };
  3093. if (description.type === 'offer') {
  3094. pc._updateSignalingState('have-remote-offer');
  3095. } else {
  3096. pc._updateSignalingState('stable');
  3097. }
  3098. Object.keys(streams).forEach(function(sid) {
  3099. var stream = streams[sid];
  3100. if (stream.getTracks().length) {
  3101. if (pc.remoteStreams.indexOf(stream) === -1) {
  3102. pc.remoteStreams.push(stream);
  3103. var event = new Event('addstream');
  3104. event.stream = stream;
  3105. window.setTimeout(function() {
  3106. pc._dispatchEvent('addstream', event);
  3107. });
  3108. }
  3109. receiverList.forEach(function(item) {
  3110. var track = item[0];
  3111. var receiver = item[1];
  3112. if (stream.id !== item[2].id) {
  3113. return;
  3114. }
  3115. fireAddTrack(pc, track, receiver, [stream]);
  3116. });
  3117. }
  3118. });
  3119. receiverList.forEach(function(item) {
  3120. if (item[2]) {
  3121. return;
  3122. }
  3123. fireAddTrack(pc, item[0], item[1], []);
  3124. });
  3125. // check whether addIceCandidate({}) was called within four seconds after
  3126. // setRemoteDescription.
  3127. window.setTimeout(function() {
  3128. if (!(pc && pc.transceivers)) {
  3129. return;
  3130. }
  3131. pc.transceivers.forEach(function(transceiver) {
  3132. if (transceiver.iceTransport &&
  3133. transceiver.iceTransport.state === 'new' &&
  3134. transceiver.iceTransport.getRemoteCandidates().length > 0) {
  3135. console.warn('Timeout for addRemoteCandidate. Consider sending ' +
  3136. 'an end-of-candidates notification');
  3137. transceiver.iceTransport.addRemoteCandidate({});
  3138. }
  3139. });
  3140. }, 4000);
  3141. return Promise.resolve();
  3142. };
  3143. RTCPeerConnection.prototype.close = function() {
  3144. this.transceivers.forEach(function(transceiver) {
  3145. /* not yet
  3146. if (transceiver.iceGatherer) {
  3147. transceiver.iceGatherer.close();
  3148. }
  3149. */
  3150. if (transceiver.iceTransport) {
  3151. transceiver.iceTransport.stop();
  3152. }
  3153. if (transceiver.dtlsTransport) {
  3154. transceiver.dtlsTransport.stop();
  3155. }
  3156. if (transceiver.rtpSender) {
  3157. transceiver.rtpSender.stop();
  3158. }
  3159. if (transceiver.rtpReceiver) {
  3160. transceiver.rtpReceiver.stop();
  3161. }
  3162. });
  3163. // FIXME: clean up tracks, local streams, remote streams, etc
  3164. this._isClosed = true;
  3165. this._updateSignalingState('closed');
  3166. };
  3167. // Update the signaling state.
  3168. RTCPeerConnection.prototype._updateSignalingState = function(newState) {
  3169. this.signalingState = newState;
  3170. var event = new Event('signalingstatechange');
  3171. this._dispatchEvent('signalingstatechange', event);
  3172. };
  3173. // Determine whether to fire the negotiationneeded event.
  3174. RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
  3175. var pc = this;
  3176. if (this.signalingState !== 'stable' || this.needNegotiation === true) {
  3177. return;
  3178. }
  3179. this.needNegotiation = true;
  3180. window.setTimeout(function() {
  3181. if (pc.needNegotiation) {
  3182. pc.needNegotiation = false;
  3183. var event = new Event('negotiationneeded');
  3184. pc._dispatchEvent('negotiationneeded', event);
  3185. }
  3186. }, 0);
  3187. };
  3188. // Update the ice connection state.
  3189. RTCPeerConnection.prototype._updateIceConnectionState = function() {
  3190. var newState;
  3191. var states = {
  3192. 'new': 0,
  3193. closed: 0,
  3194. checking: 0,
  3195. connected: 0,
  3196. completed: 0,
  3197. disconnected: 0,
  3198. failed: 0
  3199. };
  3200. this.transceivers.forEach(function(transceiver) {
  3201. if (transceiver.iceTransport && !transceiver.rejected) {
  3202. states[transceiver.iceTransport.state]++;
  3203. }
  3204. });
  3205. newState = 'new';
  3206. if (states.failed > 0) {
  3207. newState = 'failed';
  3208. } else if (states.checking > 0) {
  3209. newState = 'checking';
  3210. } else if (states.disconnected > 0) {
  3211. newState = 'disconnected';
  3212. } else if (states.new > 0) {
  3213. newState = 'new';
  3214. } else if (states.connected > 0) {
  3215. newState = 'connected';
  3216. } else if (states.completed > 0) {
  3217. newState = 'completed';
  3218. }
  3219. if (newState !== this.iceConnectionState) {
  3220. this.iceConnectionState = newState;
  3221. var event = new Event('iceconnectionstatechange');
  3222. this._dispatchEvent('iceconnectionstatechange', event);
  3223. }
  3224. };
  3225. // Update the connection state.
  3226. RTCPeerConnection.prototype._updateConnectionState = function() {
  3227. var newState;
  3228. var states = {
  3229. 'new': 0,
  3230. closed: 0,
  3231. connecting: 0,
  3232. connected: 0,
  3233. completed: 0,
  3234. disconnected: 0,
  3235. failed: 0
  3236. };
  3237. this.transceivers.forEach(function(transceiver) {
  3238. if (transceiver.iceTransport && transceiver.dtlsTransport &&
  3239. !transceiver.rejected) {
  3240. states[transceiver.iceTransport.state]++;
  3241. states[transceiver.dtlsTransport.state]++;
  3242. }
  3243. });
  3244. // ICETransport.completed and connected are the same for this purpose.
  3245. states.connected += states.completed;
  3246. newState = 'new';
  3247. if (states.failed > 0) {
  3248. newState = 'failed';
  3249. } else if (states.connecting > 0) {
  3250. newState = 'connecting';
  3251. } else if (states.disconnected > 0) {
  3252. newState = 'disconnected';
  3253. } else if (states.new > 0) {
  3254. newState = 'new';
  3255. } else if (states.connected > 0) {
  3256. newState = 'connected';
  3257. }
  3258. if (newState !== this.connectionState) {
  3259. this.connectionState = newState;
  3260. var event = new Event('connectionstatechange');
  3261. this._dispatchEvent('connectionstatechange', event);
  3262. }
  3263. };
  3264. RTCPeerConnection.prototype.createOffer = function() {
  3265. var pc = this;
  3266. if (pc._isClosed) {
  3267. return Promise.reject(makeError('InvalidStateError',
  3268. 'Can not call createOffer after close'));
  3269. }
  3270. var numAudioTracks = pc.transceivers.filter(function(t) {
  3271. return t.kind === 'audio';
  3272. }).length;
  3273. var numVideoTracks = pc.transceivers.filter(function(t) {
  3274. return t.kind === 'video';
  3275. }).length;
  3276. // Determine number of audio and video tracks we need to send/recv.
  3277. var offerOptions = arguments[0];
  3278. if (offerOptions) {
  3279. // Reject Chrome legacy constraints.
  3280. if (offerOptions.mandatory || offerOptions.optional) {
  3281. throw new TypeError(
  3282. 'Legacy mandatory/optional constraints not supported.');
  3283. }
  3284. if (offerOptions.offerToReceiveAudio !== undefined) {
  3285. if (offerOptions.offerToReceiveAudio === true) {
  3286. numAudioTracks = 1;
  3287. } else if (offerOptions.offerToReceiveAudio === false) {
  3288. numAudioTracks = 0;
  3289. } else {
  3290. numAudioTracks = offerOptions.offerToReceiveAudio;
  3291. }
  3292. }
  3293. if (offerOptions.offerToReceiveVideo !== undefined) {
  3294. if (offerOptions.offerToReceiveVideo === true) {
  3295. numVideoTracks = 1;
  3296. } else if (offerOptions.offerToReceiveVideo === false) {
  3297. numVideoTracks = 0;
  3298. } else {
  3299. numVideoTracks = offerOptions.offerToReceiveVideo;
  3300. }
  3301. }
  3302. }
  3303. pc.transceivers.forEach(function(transceiver) {
  3304. if (transceiver.kind === 'audio') {
  3305. numAudioTracks--;
  3306. if (numAudioTracks < 0) {
  3307. transceiver.wantReceive = false;
  3308. }
  3309. } else if (transceiver.kind === 'video') {
  3310. numVideoTracks--;
  3311. if (numVideoTracks < 0) {
  3312. transceiver.wantReceive = false;
  3313. }
  3314. }
  3315. });
  3316. // Create M-lines for recvonly streams.
  3317. while (numAudioTracks > 0 || numVideoTracks > 0) {
  3318. if (numAudioTracks > 0) {
  3319. pc._createTransceiver('audio');
  3320. numAudioTracks--;
  3321. }
  3322. if (numVideoTracks > 0) {
  3323. pc._createTransceiver('video');
  3324. numVideoTracks--;
  3325. }
  3326. }
  3327. var sdp$1 = sdp.writeSessionBoilerplate(pc._sdpSessionId,
  3328. pc._sdpSessionVersion++);
  3329. pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3330. // For each track, create an ice gatherer, ice transport,
  3331. // dtls transport, potentially rtpsender and rtpreceiver.
  3332. var track = transceiver.track;
  3333. var kind = transceiver.kind;
  3334. var mid = transceiver.mid || sdp.generateIdentifier();
  3335. transceiver.mid = mid;
  3336. if (!transceiver.iceGatherer) {
  3337. transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
  3338. pc.usingBundle);
  3339. }
  3340. var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
  3341. // filter RTX until additional stuff needed for RTX is implemented
  3342. // in adapter.js
  3343. if (edgeVersion < 15019) {
  3344. localCapabilities.codecs = localCapabilities.codecs.filter(
  3345. function(codec) {
  3346. return codec.name !== 'rtx';
  3347. });
  3348. }
  3349. localCapabilities.codecs.forEach(function(codec) {
  3350. // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
  3351. // by adding level-asymmetry-allowed=1
  3352. if (codec.name === 'H264' &&
  3353. codec.parameters['level-asymmetry-allowed'] === undefined) {
  3354. codec.parameters['level-asymmetry-allowed'] = '1';
  3355. }
  3356. // for subsequent offers, we might have to re-use the payload
  3357. // type of the last offer.
  3358. if (transceiver.remoteCapabilities &&
  3359. transceiver.remoteCapabilities.codecs) {
  3360. transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) {
  3361. if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() &&
  3362. codec.clockRate === remoteCodec.clockRate) {
  3363. codec.preferredPayloadType = remoteCodec.payloadType;
  3364. }
  3365. });
  3366. }
  3367. });
  3368. localCapabilities.headerExtensions.forEach(function(hdrExt) {
  3369. var remoteExtensions = transceiver.remoteCapabilities &&
  3370. transceiver.remoteCapabilities.headerExtensions || [];
  3371. remoteExtensions.forEach(function(rHdrExt) {
  3372. if (hdrExt.uri === rHdrExt.uri) {
  3373. hdrExt.id = rHdrExt.id;
  3374. }
  3375. });
  3376. });
  3377. // generate an ssrc now, to be used later in rtpSender.send
  3378. var sendEncodingParameters = transceiver.sendEncodingParameters || [{
  3379. ssrc: (2 * sdpMLineIndex + 1) * 1001
  3380. }];
  3381. if (track) {
  3382. // add RTX
  3383. if (edgeVersion >= 15019 && kind === 'video' &&
  3384. !sendEncodingParameters[0].rtx) {
  3385. sendEncodingParameters[0].rtx = {
  3386. ssrc: sendEncodingParameters[0].ssrc + 1
  3387. };
  3388. }
  3389. }
  3390. if (transceiver.wantReceive) {
  3391. transceiver.rtpReceiver = new window.RTCRtpReceiver(
  3392. transceiver.dtlsTransport, kind);
  3393. }
  3394. transceiver.localCapabilities = localCapabilities;
  3395. transceiver.sendEncodingParameters = sendEncodingParameters;
  3396. });
  3397. // always offer BUNDLE and dispose on return if not supported.
  3398. if (pc._config.bundlePolicy !== 'max-compat') {
  3399. sdp$1 += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {
  3400. return t.mid;
  3401. }).join(' ') + '\r\n';
  3402. }
  3403. sdp$1 += 'a=ice-options:trickle\r\n';
  3404. pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3405. sdp$1 += writeMediaSection(transceiver, transceiver.localCapabilities,
  3406. 'offer', transceiver.stream, pc._dtlsRole);
  3407. sdp$1 += 'a=rtcp-rsize\r\n';
  3408. if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&
  3409. (sdpMLineIndex === 0 || !pc.usingBundle)) {
  3410. transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {
  3411. cand.component = 1;
  3412. sdp$1 += 'a=' + sdp.writeCandidate(cand) + '\r\n';
  3413. });
  3414. if (transceiver.iceGatherer.state === 'completed') {
  3415. sdp$1 += 'a=end-of-candidates\r\n';
  3416. }
  3417. }
  3418. });
  3419. var desc = new window.RTCSessionDescription({
  3420. type: 'offer',
  3421. sdp: sdp$1
  3422. });
  3423. return Promise.resolve(desc);
  3424. };
  3425. RTCPeerConnection.prototype.createAnswer = function() {
  3426. var pc = this;
  3427. if (pc._isClosed) {
  3428. return Promise.reject(makeError('InvalidStateError',
  3429. 'Can not call createAnswer after close'));
  3430. }
  3431. if (!(pc.signalingState === 'have-remote-offer' ||
  3432. pc.signalingState === 'have-local-pranswer')) {
  3433. return Promise.reject(makeError('InvalidStateError',
  3434. 'Can not call createAnswer in signalingState ' + pc.signalingState));
  3435. }
  3436. var sdp$1 = sdp.writeSessionBoilerplate(pc._sdpSessionId,
  3437. pc._sdpSessionVersion++);
  3438. if (pc.usingBundle) {
  3439. sdp$1 += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {
  3440. return t.mid;
  3441. }).join(' ') + '\r\n';
  3442. }
  3443. sdp$1 += 'a=ice-options:trickle\r\n';
  3444. var mediaSectionsInOffer = sdp.getMediaSections(
  3445. pc._remoteDescription.sdp).length;
  3446. pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3447. if (sdpMLineIndex + 1 > mediaSectionsInOffer) {
  3448. return;
  3449. }
  3450. if (transceiver.rejected) {
  3451. if (transceiver.kind === 'application') {
  3452. if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt
  3453. sdp$1 += 'm=application 0 DTLS/SCTP 5000\r\n';
  3454. } else {
  3455. sdp$1 += 'm=application 0 ' + transceiver.protocol +
  3456. ' webrtc-datachannel\r\n';
  3457. }
  3458. } else if (transceiver.kind === 'audio') {
  3459. sdp$1 += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' +
  3460. 'a=rtpmap:0 PCMU/8000\r\n';
  3461. } else if (transceiver.kind === 'video') {
  3462. sdp$1 += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' +
  3463. 'a=rtpmap:120 VP8/90000\r\n';
  3464. }
  3465. sdp$1 += 'c=IN IP4 0.0.0.0\r\n' +
  3466. 'a=inactive\r\n' +
  3467. 'a=mid:' + transceiver.mid + '\r\n';
  3468. return;
  3469. }
  3470. // FIXME: look at direction.
  3471. if (transceiver.stream) {
  3472. var localTrack;
  3473. if (transceiver.kind === 'audio') {
  3474. localTrack = transceiver.stream.getAudioTracks()[0];
  3475. } else if (transceiver.kind === 'video') {
  3476. localTrack = transceiver.stream.getVideoTracks()[0];
  3477. }
  3478. if (localTrack) {
  3479. // add RTX
  3480. if (edgeVersion >= 15019 && transceiver.kind === 'video' &&
  3481. !transceiver.sendEncodingParameters[0].rtx) {
  3482. transceiver.sendEncodingParameters[0].rtx = {
  3483. ssrc: transceiver.sendEncodingParameters[0].ssrc + 1
  3484. };
  3485. }
  3486. }
  3487. }
  3488. // Calculate intersection of capabilities.
  3489. var commonCapabilities = getCommonCapabilities(
  3490. transceiver.localCapabilities,
  3491. transceiver.remoteCapabilities);
  3492. var hasRtx = commonCapabilities.codecs.filter(function(c) {
  3493. return c.name.toLowerCase() === 'rtx';
  3494. }).length;
  3495. if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
  3496. delete transceiver.sendEncodingParameters[0].rtx;
  3497. }
  3498. sdp$1 += writeMediaSection(transceiver, commonCapabilities,
  3499. 'answer', transceiver.stream, pc._dtlsRole);
  3500. if (transceiver.rtcpParameters &&
  3501. transceiver.rtcpParameters.reducedSize) {
  3502. sdp$1 += 'a=rtcp-rsize\r\n';
  3503. }
  3504. });
  3505. var desc = new window.RTCSessionDescription({
  3506. type: 'answer',
  3507. sdp: sdp$1
  3508. });
  3509. return Promise.resolve(desc);
  3510. };
  3511. RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
  3512. var pc = this;
  3513. var sections;
  3514. if (candidate && !(candidate.sdpMLineIndex !== undefined ||
  3515. candidate.sdpMid)) {
  3516. return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));
  3517. }
  3518. // TODO: needs to go into ops queue.
  3519. return new Promise(function(resolve, reject) {
  3520. if (!pc._remoteDescription) {
  3521. return reject(makeError('InvalidStateError',
  3522. 'Can not add ICE candidate without a remote description'));
  3523. } else if (!candidate || candidate.candidate === '') {
  3524. for (var j = 0; j < pc.transceivers.length; j++) {
  3525. if (pc.transceivers[j].rejected) {
  3526. continue;
  3527. }
  3528. pc.transceivers[j].iceTransport.addRemoteCandidate({});
  3529. sections = sdp.getMediaSections(pc._remoteDescription.sdp);
  3530. sections[j] += 'a=end-of-candidates\r\n';
  3531. pc._remoteDescription.sdp =
  3532. sdp.getDescription(pc._remoteDescription.sdp) +
  3533. sections.join('');
  3534. if (pc.usingBundle) {
  3535. break;
  3536. }
  3537. }
  3538. } else {
  3539. var sdpMLineIndex = candidate.sdpMLineIndex;
  3540. if (candidate.sdpMid) {
  3541. for (var i = 0; i < pc.transceivers.length; i++) {
  3542. if (pc.transceivers[i].mid === candidate.sdpMid) {
  3543. sdpMLineIndex = i;
  3544. break;
  3545. }
  3546. }
  3547. }
  3548. var transceiver = pc.transceivers[sdpMLineIndex];
  3549. if (transceiver) {
  3550. if (transceiver.rejected) {
  3551. return resolve();
  3552. }
  3553. var cand = Object.keys(candidate.candidate).length > 0 ?
  3554. sdp.parseCandidate(candidate.candidate) : {};
  3555. // Ignore Chrome's invalid candidates since Edge does not like them.
  3556. if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
  3557. return resolve();
  3558. }
  3559. // Ignore RTCP candidates, we assume RTCP-MUX.
  3560. if (cand.component && cand.component !== 1) {
  3561. return resolve();
  3562. }
  3563. // when using bundle, avoid adding candidates to the wrong
  3564. // ice transport. And avoid adding candidates added in the SDP.
  3565. if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&
  3566. transceiver.iceTransport !== pc.transceivers[0].iceTransport)) {
  3567. if (!maybeAddCandidate(transceiver.iceTransport, cand)) {
  3568. return reject(makeError('OperationError',
  3569. 'Can not add ICE candidate'));
  3570. }
  3571. }
  3572. // update the remoteDescription.
  3573. var candidateString = candidate.candidate.trim();
  3574. if (candidateString.indexOf('a=') === 0) {
  3575. candidateString = candidateString.substr(2);
  3576. }
  3577. sections = sdp.getMediaSections(pc._remoteDescription.sdp);
  3578. sections[sdpMLineIndex] += 'a=' +
  3579. (cand.type ? candidateString : 'end-of-candidates')
  3580. + '\r\n';
  3581. pc._remoteDescription.sdp =
  3582. sdp.getDescription(pc._remoteDescription.sdp) +
  3583. sections.join('');
  3584. } else {
  3585. return reject(makeError('OperationError',
  3586. 'Can not add ICE candidate'));
  3587. }
  3588. }
  3589. resolve();
  3590. });
  3591. };
  3592. RTCPeerConnection.prototype.getStats = function(selector) {
  3593. if (selector && selector instanceof window.MediaStreamTrack) {
  3594. var senderOrReceiver = null;
  3595. this.transceivers.forEach(function(transceiver) {
  3596. if (transceiver.rtpSender &&
  3597. transceiver.rtpSender.track === selector) {
  3598. senderOrReceiver = transceiver.rtpSender;
  3599. } else if (transceiver.rtpReceiver &&
  3600. transceiver.rtpReceiver.track === selector) {
  3601. senderOrReceiver = transceiver.rtpReceiver;
  3602. }
  3603. });
  3604. if (!senderOrReceiver) {
  3605. throw makeError('InvalidAccessError', 'Invalid selector.');
  3606. }
  3607. return senderOrReceiver.getStats();
  3608. }
  3609. var promises = [];
  3610. this.transceivers.forEach(function(transceiver) {
  3611. ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
  3612. 'dtlsTransport'].forEach(function(method) {
  3613. if (transceiver[method]) {
  3614. promises.push(transceiver[method].getStats());
  3615. }
  3616. });
  3617. });
  3618. return Promise.all(promises).then(function(allStats) {
  3619. var results = new Map();
  3620. allStats.forEach(function(stats) {
  3621. stats.forEach(function(stat) {
  3622. results.set(stat.id, stat);
  3623. });
  3624. });
  3625. return results;
  3626. });
  3627. };
  3628. // fix low-level stat names and return Map instead of object.
  3629. var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer',
  3630. 'RTCIceTransport', 'RTCDtlsTransport'];
  3631. ortcObjects.forEach(function(ortcObjectName) {
  3632. var obj = window[ortcObjectName];
  3633. if (obj && obj.prototype && obj.prototype.getStats) {
  3634. var nativeGetstats = obj.prototype.getStats;
  3635. obj.prototype.getStats = function() {
  3636. return nativeGetstats.apply(this)
  3637. .then(function(nativeStats) {
  3638. var mapStats = new Map();
  3639. Object.keys(nativeStats).forEach(function(id) {
  3640. nativeStats[id].type = fixStatsType(nativeStats[id]);
  3641. mapStats.set(id, nativeStats[id]);
  3642. });
  3643. return mapStats;
  3644. });
  3645. };
  3646. }
  3647. });
  3648. // legacy callback shims. Should be moved to adapter.js some days.
  3649. var methods = ['createOffer', 'createAnswer'];
  3650. methods.forEach(function(method) {
  3651. var nativeMethod = RTCPeerConnection.prototype[method];
  3652. RTCPeerConnection.prototype[method] = function() {
  3653. var args = arguments;
  3654. if (typeof args[0] === 'function' ||
  3655. typeof args[1] === 'function') { // legacy
  3656. return nativeMethod.apply(this, [arguments[2]])
  3657. .then(function(description) {
  3658. if (typeof args[0] === 'function') {
  3659. args[0].apply(null, [description]);
  3660. }
  3661. }, function(error) {
  3662. if (typeof args[1] === 'function') {
  3663. args[1].apply(null, [error]);
  3664. }
  3665. });
  3666. }
  3667. return nativeMethod.apply(this, arguments);
  3668. };
  3669. });
  3670. methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];
  3671. methods.forEach(function(method) {
  3672. var nativeMethod = RTCPeerConnection.prototype[method];
  3673. RTCPeerConnection.prototype[method] = function() {
  3674. var args = arguments;
  3675. if (typeof args[1] === 'function' ||
  3676. typeof args[2] === 'function') { // legacy
  3677. return nativeMethod.apply(this, arguments)
  3678. .then(function() {
  3679. if (typeof args[1] === 'function') {
  3680. args[1].apply(null);
  3681. }
  3682. }, function(error) {
  3683. if (typeof args[2] === 'function') {
  3684. args[2].apply(null, [error]);
  3685. }
  3686. });
  3687. }
  3688. return nativeMethod.apply(this, arguments);
  3689. };
  3690. });
  3691. // getStats is special. It doesn't have a spec legacy method yet we support
  3692. // getStats(something, cb) without error callbacks.
  3693. ['getStats'].forEach(function(method) {
  3694. var nativeMethod = RTCPeerConnection.prototype[method];
  3695. RTCPeerConnection.prototype[method] = function() {
  3696. var args = arguments;
  3697. if (typeof args[1] === 'function') {
  3698. return nativeMethod.apply(this, arguments)
  3699. .then(function() {
  3700. if (typeof args[1] === 'function') {
  3701. args[1].apply(null);
  3702. }
  3703. });
  3704. }
  3705. return nativeMethod.apply(this, arguments);
  3706. };
  3707. });
  3708. return RTCPeerConnection;
  3709. };
  3710. /*
  3711. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3712. *
  3713. * Use of this source code is governed by a BSD-style license
  3714. * that can be found in the LICENSE file in the root of the source
  3715. * tree.
  3716. */
  3717. function shimGetUserMedia$2(window) {
  3718. const navigator = window && window.navigator;
  3719. const shimError_ = function(e) {
  3720. return {
  3721. name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
  3722. message: e.message,
  3723. constraint: e.constraint,
  3724. toString() {
  3725. return this.name;
  3726. }
  3727. };
  3728. };
  3729. // getUserMedia error shim.
  3730. const origGetUserMedia = navigator.mediaDevices.getUserMedia.
  3731. bind(navigator.mediaDevices);
  3732. navigator.mediaDevices.getUserMedia = function(c) {
  3733. return origGetUserMedia(c).catch(e => Promise.reject(shimError_(e)));
  3734. };
  3735. }
  3736. /*
  3737. * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
  3738. *
  3739. * Use of this source code is governed by a BSD-style license
  3740. * that can be found in the LICENSE file in the root of the source
  3741. * tree.
  3742. */
  3743. function shimGetDisplayMedia$1(window) {
  3744. if (!('getDisplayMedia' in window.navigator)) {
  3745. return;
  3746. }
  3747. if (!(window.navigator.mediaDevices)) {
  3748. return;
  3749. }
  3750. if (window.navigator.mediaDevices &&
  3751. 'getDisplayMedia' in window.navigator.mediaDevices) {
  3752. return;
  3753. }
  3754. window.navigator.mediaDevices.getDisplayMedia =
  3755. window.navigator.getDisplayMedia.bind(window.navigator);
  3756. }
  3757. /*
  3758. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3759. *
  3760. * Use of this source code is governed by a BSD-style license
  3761. * that can be found in the LICENSE file in the root of the source
  3762. * tree.
  3763. */
  3764. function shimPeerConnection$1(window, browserDetails) {
  3765. if (window.RTCIceGatherer) {
  3766. if (!window.RTCIceCandidate) {
  3767. window.RTCIceCandidate = function RTCIceCandidate(args) {
  3768. return args;
  3769. };
  3770. }
  3771. if (!window.RTCSessionDescription) {
  3772. window.RTCSessionDescription = function RTCSessionDescription(args) {
  3773. return args;
  3774. };
  3775. }
  3776. // this adds an additional event listener to MediaStrackTrack that signals
  3777. // when a tracks enabled property was changed. Workaround for a bug in
  3778. // addStream, see below. No longer required in 15025+
  3779. if (browserDetails.version < 15025) {
  3780. const origMSTEnabled = Object.getOwnPropertyDescriptor(
  3781. window.MediaStreamTrack.prototype, 'enabled');
  3782. Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
  3783. set(value) {
  3784. origMSTEnabled.set.call(this, value);
  3785. const ev = new Event('enabled');
  3786. ev.enabled = value;
  3787. this.dispatchEvent(ev);
  3788. }
  3789. });
  3790. }
  3791. }
  3792. // ORTC defines the DTMF sender a bit different.
  3793. // https://github.com/w3c/ortc/issues/714
  3794. if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
  3795. Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  3796. get() {
  3797. if (this._dtmf === undefined) {
  3798. if (this.track.kind === 'audio') {
  3799. this._dtmf = new window.RTCDtmfSender(this);
  3800. } else if (this.track.kind === 'video') {
  3801. this._dtmf = null;
  3802. }
  3803. }
  3804. return this._dtmf;
  3805. }
  3806. });
  3807. }
  3808. // Edge currently only implements the RTCDtmfSender, not the
  3809. // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*
  3810. if (window.RTCDtmfSender && !window.RTCDTMFSender) {
  3811. window.RTCDTMFSender = window.RTCDtmfSender;
  3812. }
  3813. const RTCPeerConnectionShim = rtcpeerconnection(window,
  3814. browserDetails.version);
  3815. window.RTCPeerConnection = function RTCPeerConnection(config) {
  3816. if (config && config.iceServers) {
  3817. config.iceServers = filterIceServers$1(config.iceServers,
  3818. browserDetails.version);
  3819. log$1('ICE servers after filtering:', config.iceServers);
  3820. }
  3821. return new RTCPeerConnectionShim(config);
  3822. };
  3823. window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype;
  3824. }
  3825. function shimReplaceTrack(window) {
  3826. // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
  3827. if (window.RTCRtpSender &&
  3828. !('replaceTrack' in window.RTCRtpSender.prototype)) {
  3829. window.RTCRtpSender.prototype.replaceTrack =
  3830. window.RTCRtpSender.prototype.setTrack;
  3831. }
  3832. }
  3833. var edgeShim = /*#__PURE__*/Object.freeze({
  3834. __proto__: null,
  3835. shimPeerConnection: shimPeerConnection$1,
  3836. shimReplaceTrack: shimReplaceTrack,
  3837. shimGetUserMedia: shimGetUserMedia$2,
  3838. shimGetDisplayMedia: shimGetDisplayMedia$1
  3839. });
  3840. /*
  3841. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3842. *
  3843. * Use of this source code is governed by a BSD-style license
  3844. * that can be found in the LICENSE file in the root of the source
  3845. * tree.
  3846. */
  3847. function shimGetUserMedia$1(window, browserDetails) {
  3848. const navigator = window && window.navigator;
  3849. const MediaStreamTrack = window && window.MediaStreamTrack;
  3850. navigator.getUserMedia = function(constraints, onSuccess, onError) {
  3851. // Replace Firefox 44+'s deprecation warning with unprefixed version.
  3852. deprecated('navigator.getUserMedia',
  3853. 'navigator.mediaDevices.getUserMedia');
  3854. navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
  3855. };
  3856. if (!(browserDetails.version > 55 &&
  3857. 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
  3858. const remap = function(obj, a, b) {
  3859. if (a in obj && !(b in obj)) {
  3860. obj[b] = obj[a];
  3861. delete obj[a];
  3862. }
  3863. };
  3864. const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
  3865. bind(navigator.mediaDevices);
  3866. navigator.mediaDevices.getUserMedia = function(c) {
  3867. if (typeof c === 'object' && typeof c.audio === 'object') {
  3868. c = JSON.parse(JSON.stringify(c));
  3869. remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
  3870. remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
  3871. }
  3872. return nativeGetUserMedia(c);
  3873. };
  3874. if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
  3875. const nativeGetSettings = MediaStreamTrack.prototype.getSettings;
  3876. MediaStreamTrack.prototype.getSettings = function() {
  3877. const obj = nativeGetSettings.apply(this, arguments);
  3878. remap(obj, 'mozAutoGainControl', 'autoGainControl');
  3879. remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
  3880. return obj;
  3881. };
  3882. }
  3883. if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
  3884. const nativeApplyConstraints =
  3885. MediaStreamTrack.prototype.applyConstraints;
  3886. MediaStreamTrack.prototype.applyConstraints = function(c) {
  3887. if (this.kind === 'audio' && typeof c === 'object') {
  3888. c = JSON.parse(JSON.stringify(c));
  3889. remap(c, 'autoGainControl', 'mozAutoGainControl');
  3890. remap(c, 'noiseSuppression', 'mozNoiseSuppression');
  3891. }
  3892. return nativeApplyConstraints.apply(this, [c]);
  3893. };
  3894. }
  3895. }
  3896. }
  3897. /*
  3898. * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
  3899. *
  3900. * Use of this source code is governed by a BSD-style license
  3901. * that can be found in the LICENSE file in the root of the source
  3902. * tree.
  3903. */
  3904. function shimGetDisplayMedia(window, preferredMediaSource) {
  3905. if (window.navigator.mediaDevices &&
  3906. 'getDisplayMedia' in window.navigator.mediaDevices) {
  3907. return;
  3908. }
  3909. if (!(window.navigator.mediaDevices)) {
  3910. return;
  3911. }
  3912. window.navigator.mediaDevices.getDisplayMedia =
  3913. function getDisplayMedia(constraints) {
  3914. if (!(constraints && constraints.video)) {
  3915. const err = new DOMException('getDisplayMedia without video ' +
  3916. 'constraints is undefined');
  3917. err.name = 'NotFoundError';
  3918. // from https://heycam.github.io/webidl/#idl-DOMException-error-names
  3919. err.code = 8;
  3920. return Promise.reject(err);
  3921. }
  3922. if (constraints.video === true) {
  3923. constraints.video = {mediaSource: preferredMediaSource};
  3924. } else {
  3925. constraints.video.mediaSource = preferredMediaSource;
  3926. }
  3927. return window.navigator.mediaDevices.getUserMedia(constraints);
  3928. };
  3929. }
  3930. /*
  3931. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3932. *
  3933. * Use of this source code is governed by a BSD-style license
  3934. * that can be found in the LICENSE file in the root of the source
  3935. * tree.
  3936. */
  3937. function shimOnTrack(window) {
  3938. if (typeof window === 'object' && window.RTCTrackEvent &&
  3939. ('receiver' in window.RTCTrackEvent.prototype) &&
  3940. !('transceiver' in window.RTCTrackEvent.prototype)) {
  3941. Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  3942. get() {
  3943. return {receiver: this.receiver};
  3944. }
  3945. });
  3946. }
  3947. }
  3948. function shimPeerConnection(window, browserDetails) {
  3949. if (typeof window !== 'object' ||
  3950. !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
  3951. return; // probably media.peerconnection.enabled=false in about:config
  3952. }
  3953. if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
  3954. // very basic support for old versions.
  3955. window.RTCPeerConnection = window.mozRTCPeerConnection;
  3956. }
  3957. if (browserDetails.version < 53) {
  3958. // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
  3959. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  3960. .forEach(function(method) {
  3961. const nativeMethod = window.RTCPeerConnection.prototype[method];
  3962. const methodObj = {[method]() {
  3963. arguments[0] = new ((method === 'addIceCandidate') ?
  3964. window.RTCIceCandidate :
  3965. window.RTCSessionDescription)(arguments[0]);
  3966. return nativeMethod.apply(this, arguments);
  3967. }};
  3968. window.RTCPeerConnection.prototype[method] = methodObj[method];
  3969. });
  3970. }
  3971. const modernStatsTypes = {
  3972. inboundrtp: 'inbound-rtp',
  3973. outboundrtp: 'outbound-rtp',
  3974. candidatepair: 'candidate-pair',
  3975. localcandidate: 'local-candidate',
  3976. remotecandidate: 'remote-candidate'
  3977. };
  3978. const nativeGetStats = window.RTCPeerConnection.prototype.getStats;
  3979. window.RTCPeerConnection.prototype.getStats = function getStats() {
  3980. const [selector, onSucc, onErr] = arguments;
  3981. return nativeGetStats.apply(this, [selector || null])
  3982. .then(stats => {
  3983. if (browserDetails.version < 53 && !onSucc) {
  3984. // Shim only promise getStats with spec-hyphens in type names
  3985. // Leave callback version alone; misc old uses of forEach before Map
  3986. try {
  3987. stats.forEach(stat => {
  3988. stat.type = modernStatsTypes[stat.type] || stat.type;
  3989. });
  3990. } catch (e) {
  3991. if (e.name !== 'TypeError') {
  3992. throw e;
  3993. }
  3994. // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
  3995. stats.forEach((stat, i) => {
  3996. stats.set(i, Object.assign({}, stat, {
  3997. type: modernStatsTypes[stat.type] || stat.type
  3998. }));
  3999. });
  4000. }
  4001. }
  4002. return stats;
  4003. })
  4004. .then(onSucc, onErr);
  4005. };
  4006. }
  4007. function shimSenderGetStats(window) {
  4008. if (!(typeof window === 'object' && window.RTCPeerConnection &&
  4009. window.RTCRtpSender)) {
  4010. return;
  4011. }
  4012. if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
  4013. return;
  4014. }
  4015. const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  4016. if (origGetSenders) {
  4017. window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  4018. const senders = origGetSenders.apply(this, []);
  4019. senders.forEach(sender => sender._pc = this);
  4020. return senders;
  4021. };
  4022. }
  4023. const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  4024. if (origAddTrack) {
  4025. window.RTCPeerConnection.prototype.addTrack = function addTrack() {
  4026. const sender = origAddTrack.apply(this, arguments);
  4027. sender._pc = this;
  4028. return sender;
  4029. };
  4030. }
  4031. window.RTCRtpSender.prototype.getStats = function getStats() {
  4032. return this.track ? this._pc.getStats(this.track) :
  4033. Promise.resolve(new Map());
  4034. };
  4035. }
  4036. function shimReceiverGetStats(window) {
  4037. if (!(typeof window === 'object' && window.RTCPeerConnection &&
  4038. window.RTCRtpSender)) {
  4039. return;
  4040. }
  4041. if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
  4042. return;
  4043. }
  4044. const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
  4045. if (origGetReceivers) {
  4046. window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
  4047. const receivers = origGetReceivers.apply(this, []);
  4048. receivers.forEach(receiver => receiver._pc = this);
  4049. return receivers;
  4050. };
  4051. }
  4052. wrapPeerConnectionEvent(window, 'track', e => {
  4053. e.receiver._pc = e.srcElement;
  4054. return e;
  4055. });
  4056. window.RTCRtpReceiver.prototype.getStats = function getStats() {
  4057. return this._pc.getStats(this.track);
  4058. };
  4059. }
  4060. function shimRemoveStream(window) {
  4061. if (!window.RTCPeerConnection ||
  4062. 'removeStream' in window.RTCPeerConnection.prototype) {
  4063. return;
  4064. }
  4065. window.RTCPeerConnection.prototype.removeStream =
  4066. function removeStream(stream) {
  4067. deprecated('removeStream', 'removeTrack');
  4068. this.getSenders().forEach(sender => {
  4069. if (sender.track && stream.getTracks().includes(sender.track)) {
  4070. this.removeTrack(sender);
  4071. }
  4072. });
  4073. };
  4074. }
  4075. function shimRTCDataChannel(window) {
  4076. // rename DataChannel to RTCDataChannel (native fix in FF60):
  4077. // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
  4078. if (window.DataChannel && !window.RTCDataChannel) {
  4079. window.RTCDataChannel = window.DataChannel;
  4080. }
  4081. }
  4082. function shimAddTransceiver(window) {
  4083. // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  4084. // Firefox ignores the init sendEncodings options passed to addTransceiver
  4085. // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  4086. if (!(typeof window === 'object' && window.RTCPeerConnection)) {
  4087. return;
  4088. }
  4089. const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
  4090. if (origAddTransceiver) {
  4091. window.RTCPeerConnection.prototype.addTransceiver =
  4092. function addTransceiver() {
  4093. this.setParametersPromises = [];
  4094. const initParameters = arguments[1];
  4095. const shouldPerformCheck = initParameters &&
  4096. 'sendEncodings' in initParameters;
  4097. if (shouldPerformCheck) {
  4098. // If sendEncodings params are provided, validate grammar
  4099. initParameters.sendEncodings.forEach((encodingParam) => {
  4100. if ('rid' in encodingParam) {
  4101. const ridRegex = /^[a-z0-9]{0,16}$/i;
  4102. if (!ridRegex.test(encodingParam.rid)) {
  4103. throw new TypeError('Invalid RID value provided.');
  4104. }
  4105. }
  4106. if ('scaleResolutionDownBy' in encodingParam) {
  4107. if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
  4108. throw new RangeError('scale_resolution_down_by must be >= 1.0');
  4109. }
  4110. }
  4111. if ('maxFramerate' in encodingParam) {
  4112. if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
  4113. throw new RangeError('max_framerate must be >= 0.0');
  4114. }
  4115. }
  4116. });
  4117. }
  4118. const transceiver = origAddTransceiver.apply(this, arguments);
  4119. if (shouldPerformCheck) {
  4120. // Check if the init options were applied. If not we do this in an
  4121. // asynchronous way and save the promise reference in a global object.
  4122. // This is an ugly hack, but at the same time is way more robust than
  4123. // checking the sender parameters before and after the createOffer
  4124. // Also note that after the createoffer we are not 100% sure that
  4125. // the params were asynchronously applied so we might miss the
  4126. // opportunity to recreate offer.
  4127. const {sender} = transceiver;
  4128. const params = sender.getParameters();
  4129. if (!('encodings' in params) ||
  4130. // Avoid being fooled by patched getParameters() below.
  4131. (params.encodings.length === 1 &&
  4132. Object.keys(params.encodings[0]).length === 0)) {
  4133. params.encodings = initParameters.sendEncodings;
  4134. sender.sendEncodings = initParameters.sendEncodings;
  4135. this.setParametersPromises.push(sender.setParameters(params)
  4136. .then(() => {
  4137. delete sender.sendEncodings;
  4138. }).catch(() => {
  4139. delete sender.sendEncodings;
  4140. })
  4141. );
  4142. }
  4143. }
  4144. return transceiver;
  4145. };
  4146. }
  4147. }
  4148. function shimGetParameters(window) {
  4149. if (!(typeof window === 'object' && window.RTCRtpSender)) {
  4150. return;
  4151. }
  4152. const origGetParameters = window.RTCRtpSender.prototype.getParameters;
  4153. if (origGetParameters) {
  4154. window.RTCRtpSender.prototype.getParameters =
  4155. function getParameters() {
  4156. const params = origGetParameters.apply(this, arguments);
  4157. if (!('encodings' in params)) {
  4158. params.encodings = [].concat(this.sendEncodings || [{}]);
  4159. }
  4160. return params;
  4161. };
  4162. }
  4163. }
  4164. function shimCreateOffer(window) {
  4165. // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  4166. // Firefox ignores the init sendEncodings options passed to addTransceiver
  4167. // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  4168. if (!(typeof window === 'object' && window.RTCPeerConnection)) {
  4169. return;
  4170. }
  4171. const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
  4172. window.RTCPeerConnection.prototype.createOffer = function createOffer() {
  4173. if (this.setParametersPromises && this.setParametersPromises.length) {
  4174. return Promise.all(this.setParametersPromises)
  4175. .then(() => {
  4176. return origCreateOffer.apply(this, arguments);
  4177. })
  4178. .finally(() => {
  4179. this.setParametersPromises = [];
  4180. });
  4181. }
  4182. return origCreateOffer.apply(this, arguments);
  4183. };
  4184. }
  4185. function shimCreateAnswer(window) {
  4186. // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  4187. // Firefox ignores the init sendEncodings options passed to addTransceiver
  4188. // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  4189. if (!(typeof window === 'object' && window.RTCPeerConnection)) {
  4190. return;
  4191. }
  4192. const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
  4193. window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
  4194. if (this.setParametersPromises && this.setParametersPromises.length) {
  4195. return Promise.all(this.setParametersPromises)
  4196. .then(() => {
  4197. return origCreateAnswer.apply(this, arguments);
  4198. })
  4199. .finally(() => {
  4200. this.setParametersPromises = [];
  4201. });
  4202. }
  4203. return origCreateAnswer.apply(this, arguments);
  4204. };
  4205. }
  4206. var firefoxShim = /*#__PURE__*/Object.freeze({
  4207. __proto__: null,
  4208. shimOnTrack: shimOnTrack,
  4209. shimPeerConnection: shimPeerConnection,
  4210. shimSenderGetStats: shimSenderGetStats,
  4211. shimReceiverGetStats: shimReceiverGetStats,
  4212. shimRemoveStream: shimRemoveStream,
  4213. shimRTCDataChannel: shimRTCDataChannel,
  4214. shimAddTransceiver: shimAddTransceiver,
  4215. shimGetParameters: shimGetParameters,
  4216. shimCreateOffer: shimCreateOffer,
  4217. shimCreateAnswer: shimCreateAnswer,
  4218. shimGetUserMedia: shimGetUserMedia$1,
  4219. shimGetDisplayMedia: shimGetDisplayMedia
  4220. });
  4221. /*
  4222. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4223. *
  4224. * Use of this source code is governed by a BSD-style license
  4225. * that can be found in the LICENSE file in the root of the source
  4226. * tree.
  4227. */
  4228. function shimLocalStreamsAPI(window) {
  4229. if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4230. return;
  4231. }
  4232. if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
  4233. window.RTCPeerConnection.prototype.getLocalStreams =
  4234. function getLocalStreams() {
  4235. if (!this._localStreams) {
  4236. this._localStreams = [];
  4237. }
  4238. return this._localStreams;
  4239. };
  4240. }
  4241. if (!('addStream' in window.RTCPeerConnection.prototype)) {
  4242. const _addTrack = window.RTCPeerConnection.prototype.addTrack;
  4243. window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  4244. if (!this._localStreams) {
  4245. this._localStreams = [];
  4246. }
  4247. if (!this._localStreams.includes(stream)) {
  4248. this._localStreams.push(stream);
  4249. }
  4250. // Try to emulate Chrome's behaviour of adding in audio-video order.
  4251. // Safari orders by track id.
  4252. stream.getAudioTracks().forEach(track => _addTrack.call(this, track,
  4253. stream));
  4254. stream.getVideoTracks().forEach(track => _addTrack.call(this, track,
  4255. stream));
  4256. };
  4257. window.RTCPeerConnection.prototype.addTrack =
  4258. function addTrack(track, ...streams) {
  4259. if (streams) {
  4260. streams.forEach((stream) => {
  4261. if (!this._localStreams) {
  4262. this._localStreams = [stream];
  4263. } else if (!this._localStreams.includes(stream)) {
  4264. this._localStreams.push(stream);
  4265. }
  4266. });
  4267. }
  4268. return _addTrack.apply(this, arguments);
  4269. };
  4270. }
  4271. if (!('removeStream' in window.RTCPeerConnection.prototype)) {
  4272. window.RTCPeerConnection.prototype.removeStream =
  4273. function removeStream(stream) {
  4274. if (!this._localStreams) {
  4275. this._localStreams = [];
  4276. }
  4277. const index = this._localStreams.indexOf(stream);
  4278. if (index === -1) {
  4279. return;
  4280. }
  4281. this._localStreams.splice(index, 1);
  4282. const tracks = stream.getTracks();
  4283. this.getSenders().forEach(sender => {
  4284. if (tracks.includes(sender.track)) {
  4285. this.removeTrack(sender);
  4286. }
  4287. });
  4288. };
  4289. }
  4290. }
  4291. function shimRemoteStreamsAPI(window) {
  4292. if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4293. return;
  4294. }
  4295. if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
  4296. window.RTCPeerConnection.prototype.getRemoteStreams =
  4297. function getRemoteStreams() {
  4298. return this._remoteStreams ? this._remoteStreams : [];
  4299. };
  4300. }
  4301. if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
  4302. Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
  4303. get() {
  4304. return this._onaddstream;
  4305. },
  4306. set(f) {
  4307. if (this._onaddstream) {
  4308. this.removeEventListener('addstream', this._onaddstream);
  4309. this.removeEventListener('track', this._onaddstreampoly);
  4310. }
  4311. this.addEventListener('addstream', this._onaddstream = f);
  4312. this.addEventListener('track', this._onaddstreampoly = (e) => {
  4313. e.streams.forEach(stream => {
  4314. if (!this._remoteStreams) {
  4315. this._remoteStreams = [];
  4316. }
  4317. if (this._remoteStreams.includes(stream)) {
  4318. return;
  4319. }
  4320. this._remoteStreams.push(stream);
  4321. const event = new Event('addstream');
  4322. event.stream = stream;
  4323. this.dispatchEvent(event);
  4324. });
  4325. });
  4326. }
  4327. });
  4328. const origSetRemoteDescription =
  4329. window.RTCPeerConnection.prototype.setRemoteDescription;
  4330. window.RTCPeerConnection.prototype.setRemoteDescription =
  4331. function setRemoteDescription() {
  4332. const pc = this;
  4333. if (!this._onaddstreampoly) {
  4334. this.addEventListener('track', this._onaddstreampoly = function(e) {
  4335. e.streams.forEach(stream => {
  4336. if (!pc._remoteStreams) {
  4337. pc._remoteStreams = [];
  4338. }
  4339. if (pc._remoteStreams.indexOf(stream) >= 0) {
  4340. return;
  4341. }
  4342. pc._remoteStreams.push(stream);
  4343. const event = new Event('addstream');
  4344. event.stream = stream;
  4345. pc.dispatchEvent(event);
  4346. });
  4347. });
  4348. }
  4349. return origSetRemoteDescription.apply(pc, arguments);
  4350. };
  4351. }
  4352. }
  4353. function shimCallbacksAPI(window) {
  4354. if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4355. return;
  4356. }
  4357. const prototype = window.RTCPeerConnection.prototype;
  4358. const origCreateOffer = prototype.createOffer;
  4359. const origCreateAnswer = prototype.createAnswer;
  4360. const setLocalDescription = prototype.setLocalDescription;
  4361. const setRemoteDescription = prototype.setRemoteDescription;
  4362. const addIceCandidate = prototype.addIceCandidate;
  4363. prototype.createOffer =
  4364. function createOffer(successCallback, failureCallback) {
  4365. const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
  4366. const promise = origCreateOffer.apply(this, [options]);
  4367. if (!failureCallback) {
  4368. return promise;
  4369. }
  4370. promise.then(successCallback, failureCallback);
  4371. return Promise.resolve();
  4372. };
  4373. prototype.createAnswer =
  4374. function createAnswer(successCallback, failureCallback) {
  4375. const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
  4376. const promise = origCreateAnswer.apply(this, [options]);
  4377. if (!failureCallback) {
  4378. return promise;
  4379. }
  4380. promise.then(successCallback, failureCallback);
  4381. return Promise.resolve();
  4382. };
  4383. let withCallback = function(description, successCallback, failureCallback) {
  4384. const promise = setLocalDescription.apply(this, [description]);
  4385. if (!failureCallback) {
  4386. return promise;
  4387. }
  4388. promise.then(successCallback, failureCallback);
  4389. return Promise.resolve();
  4390. };
  4391. prototype.setLocalDescription = withCallback;
  4392. withCallback = function(description, successCallback, failureCallback) {
  4393. const promise = setRemoteDescription.apply(this, [description]);
  4394. if (!failureCallback) {
  4395. return promise;
  4396. }
  4397. promise.then(successCallback, failureCallback);
  4398. return Promise.resolve();
  4399. };
  4400. prototype.setRemoteDescription = withCallback;
  4401. withCallback = function(candidate, successCallback, failureCallback) {
  4402. const promise = addIceCandidate.apply(this, [candidate]);
  4403. if (!failureCallback) {
  4404. return promise;
  4405. }
  4406. promise.then(successCallback, failureCallback);
  4407. return Promise.resolve();
  4408. };
  4409. prototype.addIceCandidate = withCallback;
  4410. }
  4411. function shimGetUserMedia(window) {
  4412. const navigator = window && window.navigator;
  4413. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  4414. // shim not needed in Safari 12.1
  4415. const mediaDevices = navigator.mediaDevices;
  4416. const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
  4417. navigator.mediaDevices.getUserMedia = (constraints) => {
  4418. return _getUserMedia(shimConstraints(constraints));
  4419. };
  4420. }
  4421. if (!navigator.getUserMedia && navigator.mediaDevices &&
  4422. navigator.mediaDevices.getUserMedia) {
  4423. navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
  4424. navigator.mediaDevices.getUserMedia(constraints)
  4425. .then(cb, errcb);
  4426. }.bind(navigator);
  4427. }
  4428. }
  4429. function shimConstraints(constraints) {
  4430. if (constraints && constraints.video !== undefined) {
  4431. return Object.assign({},
  4432. constraints,
  4433. {video: compactObject(constraints.video)}
  4434. );
  4435. }
  4436. return constraints;
  4437. }
  4438. function shimRTCIceServerUrls(window) {
  4439. if (!window.RTCPeerConnection) {
  4440. return;
  4441. }
  4442. // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
  4443. const OrigPeerConnection = window.RTCPeerConnection;
  4444. window.RTCPeerConnection =
  4445. function RTCPeerConnection(pcConfig, pcConstraints) {
  4446. if (pcConfig && pcConfig.iceServers) {
  4447. const newIceServers = [];
  4448. for (let i = 0; i < pcConfig.iceServers.length; i++) {
  4449. let server = pcConfig.iceServers[i];
  4450. if (!server.hasOwnProperty('urls') &&
  4451. server.hasOwnProperty('url')) {
  4452. deprecated('RTCIceServer.url', 'RTCIceServer.urls');
  4453. server = JSON.parse(JSON.stringify(server));
  4454. server.urls = server.url;
  4455. delete server.url;
  4456. newIceServers.push(server);
  4457. } else {
  4458. newIceServers.push(pcConfig.iceServers[i]);
  4459. }
  4460. }
  4461. pcConfig.iceServers = newIceServers;
  4462. }
  4463. return new OrigPeerConnection(pcConfig, pcConstraints);
  4464. };
  4465. window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
  4466. // wrap static methods. Currently just generateCertificate.
  4467. if ('generateCertificate' in OrigPeerConnection) {
  4468. Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
  4469. get() {
  4470. return OrigPeerConnection.generateCertificate;
  4471. }
  4472. });
  4473. }
  4474. }
  4475. function shimTrackEventTransceiver(window) {
  4476. // Add event.transceiver member over deprecated event.receiver
  4477. if (typeof window === 'object' && window.RTCTrackEvent &&
  4478. 'receiver' in window.RTCTrackEvent.prototype &&
  4479. !('transceiver' in window.RTCTrackEvent.prototype)) {
  4480. Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  4481. get() {
  4482. return {receiver: this.receiver};
  4483. }
  4484. });
  4485. }
  4486. }
  4487. function shimCreateOfferLegacy(window) {
  4488. const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
  4489. window.RTCPeerConnection.prototype.createOffer =
  4490. function createOffer(offerOptions) {
  4491. if (offerOptions) {
  4492. if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
  4493. // support bit values
  4494. offerOptions.offerToReceiveAudio =
  4495. !!offerOptions.offerToReceiveAudio;
  4496. }
  4497. const audioTransceiver = this.getTransceivers().find(transceiver =>
  4498. transceiver.receiver.track.kind === 'audio');
  4499. if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
  4500. if (audioTransceiver.direction === 'sendrecv') {
  4501. if (audioTransceiver.setDirection) {
  4502. audioTransceiver.setDirection('sendonly');
  4503. } else {
  4504. audioTransceiver.direction = 'sendonly';
  4505. }
  4506. } else if (audioTransceiver.direction === 'recvonly') {
  4507. if (audioTransceiver.setDirection) {
  4508. audioTransceiver.setDirection('inactive');
  4509. } else {
  4510. audioTransceiver.direction = 'inactive';
  4511. }
  4512. }
  4513. } else if (offerOptions.offerToReceiveAudio === true &&
  4514. !audioTransceiver) {
  4515. this.addTransceiver('audio');
  4516. }
  4517. if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
  4518. // support bit values
  4519. offerOptions.offerToReceiveVideo =
  4520. !!offerOptions.offerToReceiveVideo;
  4521. }
  4522. const videoTransceiver = this.getTransceivers().find(transceiver =>
  4523. transceiver.receiver.track.kind === 'video');
  4524. if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
  4525. if (videoTransceiver.direction === 'sendrecv') {
  4526. if (videoTransceiver.setDirection) {
  4527. videoTransceiver.setDirection('sendonly');
  4528. } else {
  4529. videoTransceiver.direction = 'sendonly';
  4530. }
  4531. } else if (videoTransceiver.direction === 'recvonly') {
  4532. if (videoTransceiver.setDirection) {
  4533. videoTransceiver.setDirection('inactive');
  4534. } else {
  4535. videoTransceiver.direction = 'inactive';
  4536. }
  4537. }
  4538. } else if (offerOptions.offerToReceiveVideo === true &&
  4539. !videoTransceiver) {
  4540. this.addTransceiver('video');
  4541. }
  4542. }
  4543. return origCreateOffer.apply(this, arguments);
  4544. };
  4545. }
  4546. function shimAudioContext(window) {
  4547. if (typeof window !== 'object' || window.AudioContext) {
  4548. return;
  4549. }
  4550. window.AudioContext = window.webkitAudioContext;
  4551. }
  4552. var safariShim = /*#__PURE__*/Object.freeze({
  4553. __proto__: null,
  4554. shimLocalStreamsAPI: shimLocalStreamsAPI,
  4555. shimRemoteStreamsAPI: shimRemoteStreamsAPI,
  4556. shimCallbacksAPI: shimCallbacksAPI,
  4557. shimGetUserMedia: shimGetUserMedia,
  4558. shimConstraints: shimConstraints,
  4559. shimRTCIceServerUrls: shimRTCIceServerUrls,
  4560. shimTrackEventTransceiver: shimTrackEventTransceiver,
  4561. shimCreateOfferLegacy: shimCreateOfferLegacy,
  4562. shimAudioContext: shimAudioContext
  4563. });
  4564. /*
  4565. * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  4566. *
  4567. * Use of this source code is governed by a BSD-style license
  4568. * that can be found in the LICENSE file in the root of the source
  4569. * tree.
  4570. */
  4571. function shimRTCIceCandidate(window) {
  4572. // foundation is arbitrarily chosen as an indicator for full support for
  4573. // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
  4574. if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in
  4575. window.RTCIceCandidate.prototype)) {
  4576. return;
  4577. }
  4578. const NativeRTCIceCandidate = window.RTCIceCandidate;
  4579. window.RTCIceCandidate = function RTCIceCandidate(args) {
  4580. // Remove the a= which shouldn't be part of the candidate string.
  4581. if (typeof args === 'object' && args.candidate &&
  4582. args.candidate.indexOf('a=') === 0) {
  4583. args = JSON.parse(JSON.stringify(args));
  4584. args.candidate = args.candidate.substr(2);
  4585. }
  4586. if (args.candidate && args.candidate.length) {
  4587. // Augment the native candidate with the parsed fields.
  4588. const nativeCandidate = new NativeRTCIceCandidate(args);
  4589. const parsedCandidate = sdp.parseCandidate(args.candidate);
  4590. const augmentedCandidate = Object.assign(nativeCandidate,
  4591. parsedCandidate);
  4592. // Add a serializer that does not serialize the extra attributes.
  4593. augmentedCandidate.toJSON = function toJSON() {
  4594. return {
  4595. candidate: augmentedCandidate.candidate,
  4596. sdpMid: augmentedCandidate.sdpMid,
  4597. sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
  4598. usernameFragment: augmentedCandidate.usernameFragment,
  4599. };
  4600. };
  4601. return augmentedCandidate;
  4602. }
  4603. return new NativeRTCIceCandidate(args);
  4604. };
  4605. window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
  4606. // Hook up the augmented candidate in onicecandidate and
  4607. // addEventListener('icecandidate', ...)
  4608. wrapPeerConnectionEvent(window, 'icecandidate', e => {
  4609. if (e.candidate) {
  4610. Object.defineProperty(e, 'candidate', {
  4611. value: new window.RTCIceCandidate(e.candidate),
  4612. writable: 'false'
  4613. });
  4614. }
  4615. return e;
  4616. });
  4617. }
  4618. function shimMaxMessageSize(window, browserDetails) {
  4619. if (!window.RTCPeerConnection) {
  4620. return;
  4621. }
  4622. if (!('sctp' in window.RTCPeerConnection.prototype)) {
  4623. Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
  4624. get() {
  4625. return typeof this._sctp === 'undefined' ? null : this._sctp;
  4626. }
  4627. });
  4628. }
  4629. const sctpInDescription = function(description) {
  4630. if (!description || !description.sdp) {
  4631. return false;
  4632. }
  4633. const sections = sdp.splitSections(description.sdp);
  4634. sections.shift();
  4635. return sections.some(mediaSection => {
  4636. const mLine = sdp.parseMLine(mediaSection);
  4637. return mLine && mLine.kind === 'application'
  4638. && mLine.protocol.indexOf('SCTP') !== -1;
  4639. });
  4640. };
  4641. const getRemoteFirefoxVersion = function(description) {
  4642. // TODO: Is there a better solution for detecting Firefox?
  4643. const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
  4644. if (match === null || match.length < 2) {
  4645. return -1;
  4646. }
  4647. const version = parseInt(match[1], 10);
  4648. // Test for NaN (yes, this is ugly)
  4649. return version !== version ? -1 : version;
  4650. };
  4651. const getCanSendMaxMessageSize = function(remoteIsFirefox) {
  4652. // Every implementation we know can send at least 64 KiB.
  4653. // Note: Although Chrome is technically able to send up to 256 KiB, the
  4654. // data does not reach the other peer reliably.
  4655. // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
  4656. let canSendMaxMessageSize = 65536;
  4657. if (browserDetails.browser === 'firefox') {
  4658. if (browserDetails.version < 57) {
  4659. if (remoteIsFirefox === -1) {
  4660. // FF < 57 will send in 16 KiB chunks using the deprecated PPID
  4661. // fragmentation.
  4662. canSendMaxMessageSize = 16384;
  4663. } else {
  4664. // However, other FF (and RAWRTC) can reassemble PPID-fragmented
  4665. // messages. Thus, supporting ~2 GiB when sending.
  4666. canSendMaxMessageSize = 2147483637;
  4667. }
  4668. } else if (browserDetails.version < 60) {
  4669. // Currently, all FF >= 57 will reset the remote maximum message size
  4670. // to the default value when a data channel is created at a later
  4671. // stage. :(
  4672. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
  4673. canSendMaxMessageSize =
  4674. browserDetails.version === 57 ? 65535 : 65536;
  4675. } else {
  4676. // FF >= 60 supports sending ~2 GiB
  4677. canSendMaxMessageSize = 2147483637;
  4678. }
  4679. }
  4680. return canSendMaxMessageSize;
  4681. };
  4682. const getMaxMessageSize = function(description, remoteIsFirefox) {
  4683. // Note: 65536 bytes is the default value from the SDP spec. Also,
  4684. // every implementation we know supports receiving 65536 bytes.
  4685. let maxMessageSize = 65536;
  4686. // FF 57 has a slightly incorrect default remote max message size, so
  4687. // we need to adjust it here to avoid a failure when sending.
  4688. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
  4689. if (browserDetails.browser === 'firefox'
  4690. && browserDetails.version === 57) {
  4691. maxMessageSize = 65535;
  4692. }
  4693. const match = sdp.matchPrefix(description.sdp,
  4694. 'a=max-message-size:');
  4695. if (match.length > 0) {
  4696. maxMessageSize = parseInt(match[0].substr(19), 10);
  4697. } else if (browserDetails.browser === 'firefox' &&
  4698. remoteIsFirefox !== -1) {
  4699. // If the maximum message size is not present in the remote SDP and
  4700. // both local and remote are Firefox, the remote peer can receive
  4701. // ~2 GiB.
  4702. maxMessageSize = 2147483637;
  4703. }
  4704. return maxMessageSize;
  4705. };
  4706. const origSetRemoteDescription =
  4707. window.RTCPeerConnection.prototype.setRemoteDescription;
  4708. window.RTCPeerConnection.prototype.setRemoteDescription =
  4709. function setRemoteDescription() {
  4710. this._sctp = null;
  4711. // Chrome decided to not expose .sctp in plan-b mode.
  4712. // As usual, adapter.js has to do an 'ugly worakaround'
  4713. // to cover up the mess.
  4714. if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
  4715. const {sdpSemantics} = this.getConfiguration();
  4716. if (sdpSemantics === 'plan-b') {
  4717. Object.defineProperty(this, 'sctp', {
  4718. get() {
  4719. return typeof this._sctp === 'undefined' ? null : this._sctp;
  4720. },
  4721. enumerable: true,
  4722. configurable: true,
  4723. });
  4724. }
  4725. }
  4726. if (sctpInDescription(arguments[0])) {
  4727. // Check if the remote is FF.
  4728. const isFirefox = getRemoteFirefoxVersion(arguments[0]);
  4729. // Get the maximum message size the local peer is capable of sending
  4730. const canSendMMS = getCanSendMaxMessageSize(isFirefox);
  4731. // Get the maximum message size of the remote peer.
  4732. const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
  4733. // Determine final maximum message size
  4734. let maxMessageSize;
  4735. if (canSendMMS === 0 && remoteMMS === 0) {
  4736. maxMessageSize = Number.POSITIVE_INFINITY;
  4737. } else if (canSendMMS === 0 || remoteMMS === 0) {
  4738. maxMessageSize = Math.max(canSendMMS, remoteMMS);
  4739. } else {
  4740. maxMessageSize = Math.min(canSendMMS, remoteMMS);
  4741. }
  4742. // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
  4743. // attribute.
  4744. const sctp = {};
  4745. Object.defineProperty(sctp, 'maxMessageSize', {
  4746. get() {
  4747. return maxMessageSize;
  4748. }
  4749. });
  4750. this._sctp = sctp;
  4751. }
  4752. return origSetRemoteDescription.apply(this, arguments);
  4753. };
  4754. }
  4755. function shimSendThrowTypeError(window) {
  4756. if (!(window.RTCPeerConnection &&
  4757. 'createDataChannel' in window.RTCPeerConnection.prototype)) {
  4758. return;
  4759. }
  4760. // Note: Although Firefox >= 57 has a native implementation, the maximum
  4761. // message size can be reset for all data channels at a later stage.
  4762. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
  4763. function wrapDcSend(dc, pc) {
  4764. const origDataChannelSend = dc.send;
  4765. dc.send = function send() {
  4766. const data = arguments[0];
  4767. const length = data.length || data.size || data.byteLength;
  4768. if (dc.readyState === 'open' &&
  4769. pc.sctp && length > pc.sctp.maxMessageSize) {
  4770. throw new TypeError('Message too large (can send a maximum of ' +
  4771. pc.sctp.maxMessageSize + ' bytes)');
  4772. }
  4773. return origDataChannelSend.apply(dc, arguments);
  4774. };
  4775. }
  4776. const origCreateDataChannel =
  4777. window.RTCPeerConnection.prototype.createDataChannel;
  4778. window.RTCPeerConnection.prototype.createDataChannel =
  4779. function createDataChannel() {
  4780. const dataChannel = origCreateDataChannel.apply(this, arguments);
  4781. wrapDcSend(dataChannel, this);
  4782. return dataChannel;
  4783. };
  4784. wrapPeerConnectionEvent(window, 'datachannel', e => {
  4785. wrapDcSend(e.channel, e.target);
  4786. return e;
  4787. });
  4788. }
  4789. /* shims RTCConnectionState by pretending it is the same as iceConnectionState.
  4790. * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
  4791. * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
  4792. * since DTLS failures would be hidden. See
  4793. * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
  4794. * for the Firefox tracking bug.
  4795. */
  4796. function shimConnectionState(window) {
  4797. if (!window.RTCPeerConnection ||
  4798. 'connectionState' in window.RTCPeerConnection.prototype) {
  4799. return;
  4800. }
  4801. const proto = window.RTCPeerConnection.prototype;
  4802. Object.defineProperty(proto, 'connectionState', {
  4803. get() {
  4804. return {
  4805. completed: 'connected',
  4806. checking: 'connecting'
  4807. }[this.iceConnectionState] || this.iceConnectionState;
  4808. },
  4809. enumerable: true,
  4810. configurable: true
  4811. });
  4812. Object.defineProperty(proto, 'onconnectionstatechange', {
  4813. get() {
  4814. return this._onconnectionstatechange || null;
  4815. },
  4816. set(cb) {
  4817. if (this._onconnectionstatechange) {
  4818. this.removeEventListener('connectionstatechange',
  4819. this._onconnectionstatechange);
  4820. delete this._onconnectionstatechange;
  4821. }
  4822. if (cb) {
  4823. this.addEventListener('connectionstatechange',
  4824. this._onconnectionstatechange = cb);
  4825. }
  4826. },
  4827. enumerable: true,
  4828. configurable: true
  4829. });
  4830. ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {
  4831. const origMethod = proto[method];
  4832. proto[method] = function() {
  4833. if (!this._connectionstatechangepoly) {
  4834. this._connectionstatechangepoly = e => {
  4835. const pc = e.target;
  4836. if (pc._lastConnectionState !== pc.connectionState) {
  4837. pc._lastConnectionState = pc.connectionState;
  4838. const newEvent = new Event('connectionstatechange', e);
  4839. pc.dispatchEvent(newEvent);
  4840. }
  4841. return e;
  4842. };
  4843. this.addEventListener('iceconnectionstatechange',
  4844. this._connectionstatechangepoly);
  4845. }
  4846. return origMethod.apply(this, arguments);
  4847. };
  4848. });
  4849. }
  4850. function removeExtmapAllowMixed(window, browserDetails) {
  4851. /* remove a=extmap-allow-mixed for webrtc.org < M71 */
  4852. if (!window.RTCPeerConnection) {
  4853. return;
  4854. }
  4855. if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
  4856. return;
  4857. }
  4858. if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
  4859. return;
  4860. }
  4861. const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
  4862. window.RTCPeerConnection.prototype.setRemoteDescription =
  4863. function setRemoteDescription(desc) {
  4864. if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
  4865. const sdp = desc.sdp.split('\n').filter((line) => {
  4866. return line.trim() !== 'a=extmap-allow-mixed';
  4867. }).join('\n');
  4868. // Safari enforces read-only-ness of RTCSessionDescription fields.
  4869. if (window.RTCSessionDescription &&
  4870. desc instanceof window.RTCSessionDescription) {
  4871. arguments[0] = new window.RTCSessionDescription({
  4872. type: desc.type,
  4873. sdp,
  4874. });
  4875. } else {
  4876. desc.sdp = sdp;
  4877. }
  4878. }
  4879. return nativeSRD.apply(this, arguments);
  4880. };
  4881. }
  4882. function shimAddIceCandidateNullOrEmpty(window, browserDetails) {
  4883. // Support for addIceCandidate(null or undefined)
  4884. // as well as addIceCandidate({candidate: "", ...})
  4885. // https://bugs.chromium.org/p/chromium/issues/detail?id=978582
  4886. // Note: must be called before other polyfills which change the signature.
  4887. if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
  4888. return;
  4889. }
  4890. const nativeAddIceCandidate =
  4891. window.RTCPeerConnection.prototype.addIceCandidate;
  4892. if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {
  4893. return;
  4894. }
  4895. window.RTCPeerConnection.prototype.addIceCandidate =
  4896. function addIceCandidate() {
  4897. if (!arguments[0]) {
  4898. if (arguments[1]) {
  4899. arguments[1].apply(null);
  4900. }
  4901. return Promise.resolve();
  4902. }
  4903. // Firefox 68+ emits and processes {candidate: "", ...}, ignore
  4904. // in older versions.
  4905. // Native support for ignoring exists for Chrome M77+.
  4906. // Safari ignores as well, exact version unknown but works in the same
  4907. // version that also ignores addIceCandidate(null).
  4908. if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)
  4909. || (browserDetails.browser === 'firefox'
  4910. && browserDetails.version < 68)
  4911. || (browserDetails.browser === 'safari'))
  4912. && arguments[0] && arguments[0].candidate === '') {
  4913. return Promise.resolve();
  4914. }
  4915. return nativeAddIceCandidate.apply(this, arguments);
  4916. };
  4917. }
  4918. var commonShim = /*#__PURE__*/Object.freeze({
  4919. __proto__: null,
  4920. shimRTCIceCandidate: shimRTCIceCandidate,
  4921. shimMaxMessageSize: shimMaxMessageSize,
  4922. shimSendThrowTypeError: shimSendThrowTypeError,
  4923. shimConnectionState: shimConnectionState,
  4924. removeExtmapAllowMixed: removeExtmapAllowMixed,
  4925. shimAddIceCandidateNullOrEmpty: shimAddIceCandidateNullOrEmpty
  4926. });
  4927. /*
  4928. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4929. *
  4930. * Use of this source code is governed by a BSD-style license
  4931. * that can be found in the LICENSE file in the root of the source
  4932. * tree.
  4933. */
  4934. // Shimming starts here.
  4935. function adapterFactory({window} = {}, options = {
  4936. shimChrome: true,
  4937. shimFirefox: true,
  4938. shimEdge: true,
  4939. shimSafari: true,
  4940. }) {
  4941. // Utils.
  4942. const logging = log$1;
  4943. const browserDetails = detectBrowser(window);
  4944. const adapter = {
  4945. browserDetails,
  4946. commonShim,
  4947. extractVersion: extractVersion,
  4948. disableLog: disableLog,
  4949. disableWarnings: disableWarnings
  4950. };
  4951. // Shim browser if found.
  4952. switch (browserDetails.browser) {
  4953. case 'chrome':
  4954. if (!chromeShim || !shimPeerConnection$2 ||
  4955. !options.shimChrome) {
  4956. logging('Chrome shim is not included in this adapter release.');
  4957. return adapter;
  4958. }
  4959. if (browserDetails.version === null) {
  4960. logging('Chrome shim can not determine version, not shimming.');
  4961. return adapter;
  4962. }
  4963. logging('adapter.js shimming chrome.');
  4964. // Export to the adapter global object visible in the browser.
  4965. adapter.browserShim = chromeShim;
  4966. // Must be called before shimPeerConnection.
  4967. shimAddIceCandidateNullOrEmpty(window, browserDetails);
  4968. shimGetUserMedia$3(window, browserDetails);
  4969. shimMediaStream(window);
  4970. shimPeerConnection$2(window, browserDetails);
  4971. shimOnTrack$1(window);
  4972. shimAddTrackRemoveTrack(window, browserDetails);
  4973. shimGetSendersWithDtmf(window);
  4974. shimGetStats(window);
  4975. shimSenderReceiverGetStats(window);
  4976. fixNegotiationNeeded(window, browserDetails);
  4977. shimRTCIceCandidate(window);
  4978. shimConnectionState(window);
  4979. shimMaxMessageSize(window, browserDetails);
  4980. shimSendThrowTypeError(window);
  4981. removeExtmapAllowMixed(window, browserDetails);
  4982. break;
  4983. case 'firefox':
  4984. if (!firefoxShim || !shimPeerConnection ||
  4985. !options.shimFirefox) {
  4986. logging('Firefox shim is not included in this adapter release.');
  4987. return adapter;
  4988. }
  4989. logging('adapter.js shimming firefox.');
  4990. // Export to the adapter global object visible in the browser.
  4991. adapter.browserShim = firefoxShim;
  4992. // Must be called before shimPeerConnection.
  4993. shimAddIceCandidateNullOrEmpty(window, browserDetails);
  4994. shimGetUserMedia$1(window, browserDetails);
  4995. shimPeerConnection(window, browserDetails);
  4996. shimOnTrack(window);
  4997. shimRemoveStream(window);
  4998. shimSenderGetStats(window);
  4999. shimReceiverGetStats(window);
  5000. shimRTCDataChannel(window);
  5001. shimAddTransceiver(window);
  5002. shimGetParameters(window);
  5003. shimCreateOffer(window);
  5004. shimCreateAnswer(window);
  5005. shimRTCIceCandidate(window);
  5006. shimConnectionState(window);
  5007. shimMaxMessageSize(window, browserDetails);
  5008. shimSendThrowTypeError(window);
  5009. break;
  5010. case 'edge':
  5011. if (!edgeShim || !shimPeerConnection$1 || !options.shimEdge) {
  5012. logging('MS edge shim is not included in this adapter release.');
  5013. return adapter;
  5014. }
  5015. logging('adapter.js shimming edge.');
  5016. // Export to the adapter global object visible in the browser.
  5017. adapter.browserShim = edgeShim;
  5018. shimGetUserMedia$2(window);
  5019. shimGetDisplayMedia$1(window);
  5020. shimPeerConnection$1(window, browserDetails);
  5021. shimReplaceTrack(window);
  5022. // the edge shim implements the full RTCIceCandidate object.
  5023. shimMaxMessageSize(window, browserDetails);
  5024. shimSendThrowTypeError(window);
  5025. break;
  5026. case 'safari':
  5027. if (!safariShim || !options.shimSafari) {
  5028. logging('Safari shim is not included in this adapter release.');
  5029. return adapter;
  5030. }
  5031. logging('adapter.js shimming safari.');
  5032. // Export to the adapter global object visible in the browser.
  5033. adapter.browserShim = safariShim;
  5034. // Must be called before shimCallbackAPI.
  5035. shimAddIceCandidateNullOrEmpty(window, browserDetails);
  5036. shimRTCIceServerUrls(window);
  5037. shimCreateOfferLegacy(window);
  5038. shimCallbacksAPI(window);
  5039. shimLocalStreamsAPI(window);
  5040. shimRemoteStreamsAPI(window);
  5041. shimTrackEventTransceiver(window);
  5042. shimGetUserMedia(window);
  5043. shimAudioContext(window);
  5044. shimRTCIceCandidate(window);
  5045. shimMaxMessageSize(window, browserDetails);
  5046. shimSendThrowTypeError(window);
  5047. removeExtmapAllowMixed(window, browserDetails);
  5048. break;
  5049. default:
  5050. logging('Unsupported browser!');
  5051. break;
  5052. }
  5053. return adapter;
  5054. }
  5055. /*
  5056. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  5057. *
  5058. * Use of this source code is governed by a BSD-style license
  5059. * that can be found in the LICENSE file in the root of the source
  5060. * tree.
  5061. */
  5062. adapterFactory({window: typeof window === 'undefined' ? undefined : window});
  5063. /**
  5064. * @class AudioTrackConstraints
  5065. * @classDesc Constraints for creating an audio MediaStreamTrack.
  5066. * @memberof Owt.Base
  5067. * @constructor
  5068. * @param {Owt.Base.AudioSourceInfo} source Source info of this audio track.
  5069. */
  5070. class AudioTrackConstraints {
  5071. // eslint-disable-next-line require-jsdoc
  5072. constructor(source) {
  5073. if (!Object.values(AudioSourceInfo).some(v => v === source)) {
  5074. throw new TypeError('Invalid source.');
  5075. }
  5076. /**
  5077. * @member {string} source
  5078. * @memberof Owt.Base.AudioTrackConstraints
  5079. * @desc Values could be "mic", "screen-cast", "file" or "mixed".
  5080. * @instance
  5081. */
  5082. this.source = source;
  5083. /**
  5084. * @member {string} deviceId
  5085. * @memberof Owt.Base.AudioTrackConstraints
  5086. * @desc Do not provide deviceId if source is not "mic".
  5087. * @instance
  5088. * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId
  5089. */
  5090. this.deviceId = undefined;
  5091. }
  5092. }
  5093. /**
  5094. * @class VideoTrackConstraints
  5095. * @classDesc Constraints for creating a video MediaStreamTrack.
  5096. * @memberof Owt.Base
  5097. * @constructor
  5098. * @param {Owt.Base.VideoSourceInfo} source Source info of this video track.
  5099. */
  5100. class VideoTrackConstraints {
  5101. // eslint-disable-next-line require-jsdoc
  5102. constructor(source) {
  5103. if (!Object.values(VideoSourceInfo).some(v => v === source)) {
  5104. throw new TypeError('Invalid source.');
  5105. }
  5106. /**
  5107. * @member {string} source
  5108. * @memberof Owt.Base.VideoTrackConstraints
  5109. * @desc Values could be "camera", "screen-cast", "file" or "mixed".
  5110. * @instance
  5111. */
  5112. this.source = source;
  5113. /**
  5114. * @member {string} deviceId
  5115. * @memberof Owt.Base.VideoTrackConstraints
  5116. * @desc Do not provide deviceId if source is not "camera".
  5117. * @instance
  5118. * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId
  5119. */
  5120. this.deviceId = undefined;
  5121. /**
  5122. * @member {Owt.Base.Resolution} resolution
  5123. * @memberof Owt.Base.VideoTrackConstraints
  5124. * @instance
  5125. */
  5126. this.resolution = undefined;
  5127. /**
  5128. * @member {number} frameRate
  5129. * @memberof Owt.Base.VideoTrackConstraints
  5130. * @instance
  5131. */
  5132. this.frameRate = undefined;
  5133. }
  5134. }
  5135. /**
  5136. * @class StreamConstraints
  5137. * @classDesc Constraints for creating a MediaStream from screen mic and camera.
  5138. * @memberof Owt.Base
  5139. * @constructor
  5140. * @param {?Owt.Base.AudioTrackConstraints} audioConstraints
  5141. * @param {?Owt.Base.VideoTrackConstraints} videoConstraints
  5142. */
  5143. class StreamConstraints {
  5144. // eslint-disable-next-line require-jsdoc
  5145. constructor(audioConstraints = false, videoConstraints = false) {
  5146. /**
  5147. * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForAudio} audio
  5148. * @memberof Owt.Base.MediaStreamDeviceConstraints
  5149. * @instance
  5150. */
  5151. this.audio = audioConstraints;
  5152. /**
  5153. * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForVideo} Video
  5154. * @memberof Owt.Base.MediaStreamDeviceConstraints
  5155. * @instance
  5156. */
  5157. this.video = videoConstraints;
  5158. }
  5159. } // eslint-disable-next-line require-jsdoc
  5160. function isVideoConstrainsForScreenCast(constraints) {
  5161. return typeof constraints.video === 'object' && constraints.video.source === VideoSourceInfo.SCREENCAST;
  5162. }
  5163. /**
  5164. * @class MediaStreamFactory
  5165. * @classDesc A factory to create MediaStream. You can also create MediaStream by yourself.
  5166. * @memberof Owt.Base
  5167. */
  5168. class MediaStreamFactory {
  5169. /**
  5170. * @function createMediaStream
  5171. * @static
  5172. * @desc Create a MediaStream with given constraints. If you want to create a MediaStream for screen cast, please make sure both audio and video's source are "screen-cast".
  5173. * @memberof Owt.Base.MediaStreamFactory
  5174. * @return {Promise<MediaStream, Error>} Return a promise that is resolved when stream is successfully created, or rejected if one of the following error happened:
  5175. * - One or more parameters cannot be satisfied.
  5176. * - Specified device is busy.
  5177. * - Cannot obtain necessary permission or operation is canceled by user.
  5178. * - Video source is screen cast, while audio source is not.
  5179. * - Audio source is screen cast, while video source is disabled.
  5180. * @param {Owt.Base.StreamConstraints} constraints
  5181. */
  5182. static createMediaStream(constraints) {
  5183. if (typeof constraints !== 'object' || !constraints.audio && !constraints.video) {
  5184. return Promise.reject(new TypeError('Invalid constrains'));
  5185. }
  5186. if (!isVideoConstrainsForScreenCast(constraints) && typeof constraints.audio === 'object' && constraints.audio.source === AudioSourceInfo.SCREENCAST) {
  5187. return Promise.reject(new TypeError('Cannot share screen without video.'));
  5188. }
  5189. if (isVideoConstrainsForScreenCast(constraints) && !isChrome() && !isFirefox()) {
  5190. return Promise.reject(new TypeError('Screen sharing only supports Chrome and Firefox.'));
  5191. }
  5192. if (isVideoConstrainsForScreenCast(constraints) && typeof constraints.audio === 'object' && constraints.audio.source !== AudioSourceInfo.SCREENCAST) {
  5193. return Promise.reject(new TypeError('Cannot capture video from screen cast while capture audio from' + ' other source.'));
  5194. } // Check and convert constraints.
  5195. if (!constraints.audio && !constraints.video) {
  5196. return Promise.reject(new TypeError('At least one of audio and video must be requested.'));
  5197. }
  5198. const mediaConstraints = Object.create({});
  5199. if (typeof constraints.audio === 'object' && constraints.audio.source === AudioSourceInfo.MIC) {
  5200. mediaConstraints.audio = Object.create({});
  5201. if (isEdge()) {
  5202. mediaConstraints.audio.deviceId = constraints.audio.deviceId;
  5203. } else {
  5204. mediaConstraints.audio.deviceId = {
  5205. exact: constraints.audio.deviceId
  5206. };
  5207. }
  5208. } else {
  5209. if (constraints.audio.source === AudioSourceInfo.SCREENCAST) {
  5210. mediaConstraints.audio = true;
  5211. } else {
  5212. mediaConstraints.audio = constraints.audio;
  5213. }
  5214. }
  5215. if (typeof constraints.video === 'object') {
  5216. mediaConstraints.video = Object.create({});
  5217. if (typeof constraints.video.frameRate === 'number') {
  5218. mediaConstraints.video.frameRate = constraints.video.frameRate;
  5219. }
  5220. if (constraints.video.resolution && constraints.video.resolution.width && constraints.video.resolution.height) {
  5221. if (constraints.video.source === VideoSourceInfo.SCREENCAST) {
  5222. mediaConstraints.video.width = constraints.video.resolution.width;
  5223. mediaConstraints.video.height = constraints.video.resolution.height;
  5224. } else {
  5225. mediaConstraints.video.width = Object.create({});
  5226. mediaConstraints.video.width.exact = constraints.video.resolution.width;
  5227. mediaConstraints.video.height = Object.create({});
  5228. mediaConstraints.video.height.exact = constraints.video.resolution.height;
  5229. }
  5230. }
  5231. if (typeof constraints.video.deviceId === 'string') {
  5232. mediaConstraints.video.deviceId = {
  5233. exact: constraints.video.deviceId
  5234. };
  5235. }
  5236. if (isFirefox() && constraints.video.source === VideoSourceInfo.SCREENCAST) {
  5237. mediaConstraints.video.mediaSource = 'screen';
  5238. }
  5239. } else {
  5240. mediaConstraints.video = constraints.video;
  5241. }
  5242. if (isVideoConstrainsForScreenCast(constraints)) {
  5243. return navigator.mediaDevices.getDisplayMedia(mediaConstraints);
  5244. } else {
  5245. return navigator.mediaDevices.getUserMedia(mediaConstraints);
  5246. }
  5247. }
  5248. }
  5249. // Copyright (C) <2018> Intel Corporation
  5250. var media = /*#__PURE__*/Object.freeze({
  5251. __proto__: null,
  5252. AudioTrackConstraints: AudioTrackConstraints,
  5253. VideoTrackConstraints: VideoTrackConstraints,
  5254. StreamConstraints: StreamConstraints,
  5255. MediaStreamFactory: MediaStreamFactory,
  5256. AudioSourceInfo: AudioSourceInfo,
  5257. VideoSourceInfo: VideoSourceInfo,
  5258. TrackKind: TrackKind,
  5259. Resolution: Resolution
  5260. });
  5261. let logger;
  5262. let errorLogger;
  5263. function setLogger() {
  5264. /*eslint-disable */
  5265. logger = console.log;
  5266. errorLogger = console.error;
  5267. /*eslint-enable */
  5268. }
  5269. function log(message, ...optionalParams) {
  5270. if (logger) {
  5271. logger(message, ...optionalParams);
  5272. }
  5273. }
  5274. function error(message, ...optionalParams) {
  5275. if (errorLogger) {
  5276. errorLogger(message, ...optionalParams);
  5277. }
  5278. }
  5279. class Event$1 {
  5280. constructor(type) {
  5281. this.listener = {};
  5282. this.type = type | '';
  5283. }
  5284. on(event, fn) {
  5285. if (!this.listener[event]) {
  5286. this.listener[event] = [];
  5287. }
  5288. this.listener[event].push(fn);
  5289. return true;
  5290. }
  5291. off(event, fn) {
  5292. if (this.listener[event]) {
  5293. var index = this.listener[event].indexOf(fn);
  5294. if (index > -1) {
  5295. this.listener[event].splice(index, 1);
  5296. }
  5297. return true;
  5298. }
  5299. return false;
  5300. }
  5301. offAll() {
  5302. this.listener = {};
  5303. }
  5304. dispatch(event, data) {
  5305. if (this.listener[event]) {
  5306. this.listener[event].map(each => {
  5307. each.apply(null, [data]);
  5308. });
  5309. return true;
  5310. }
  5311. return false;
  5312. }
  5313. }
  5314. var bind = function bind(fn, thisArg) {
  5315. return function wrap() {
  5316. var args = new Array(arguments.length);
  5317. for (var i = 0; i < args.length; i++) {
  5318. args[i] = arguments[i];
  5319. }
  5320. return fn.apply(thisArg, args);
  5321. };
  5322. };
  5323. // utils is a library of generic helper functions non-specific to axios
  5324. var toString = Object.prototype.toString;
  5325. // eslint-disable-next-line func-names
  5326. var kindOf = (function(cache) {
  5327. // eslint-disable-next-line func-names
  5328. return function(thing) {
  5329. var str = toString.call(thing);
  5330. return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
  5331. };
  5332. })(Object.create(null));
  5333. function kindOfTest(type) {
  5334. type = type.toLowerCase();
  5335. return function isKindOf(thing) {
  5336. return kindOf(thing) === type;
  5337. };
  5338. }
  5339. /**
  5340. * Determine if a value is an Array
  5341. *
  5342. * @param {Object} val The value to test
  5343. * @returns {boolean} True if value is an Array, otherwise false
  5344. */
  5345. function isArray(val) {
  5346. return Array.isArray(val);
  5347. }
  5348. /**
  5349. * Determine if a value is undefined
  5350. *
  5351. * @param {Object} val The value to test
  5352. * @returns {boolean} True if the value is undefined, otherwise false
  5353. */
  5354. function isUndefined(val) {
  5355. return typeof val === 'undefined';
  5356. }
  5357. /**
  5358. * Determine if a value is a Buffer
  5359. *
  5360. * @param {Object} val The value to test
  5361. * @returns {boolean} True if value is a Buffer, otherwise false
  5362. */
  5363. function isBuffer(val) {
  5364. return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
  5365. && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
  5366. }
  5367. /**
  5368. * Determine if a value is an ArrayBuffer
  5369. *
  5370. * @function
  5371. * @param {Object} val The value to test
  5372. * @returns {boolean} True if value is an ArrayBuffer, otherwise false
  5373. */
  5374. var isArrayBuffer = kindOfTest('ArrayBuffer');
  5375. /**
  5376. * Determine if a value is a view on an ArrayBuffer
  5377. *
  5378. * @param {Object} val The value to test
  5379. * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
  5380. */
  5381. function isArrayBufferView(val) {
  5382. var result;
  5383. if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
  5384. result = ArrayBuffer.isView(val);
  5385. } else {
  5386. result = (val) && (val.buffer) && (isArrayBuffer(val.buffer));
  5387. }
  5388. return result;
  5389. }
  5390. /**
  5391. * Determine if a value is a String
  5392. *
  5393. * @param {Object} val The value to test
  5394. * @returns {boolean} True if value is a String, otherwise false
  5395. */
  5396. function isString(val) {
  5397. return typeof val === 'string';
  5398. }
  5399. /**
  5400. * Determine if a value is a Number
  5401. *
  5402. * @param {Object} val The value to test
  5403. * @returns {boolean} True if value is a Number, otherwise false
  5404. */
  5405. function isNumber(val) {
  5406. return typeof val === 'number';
  5407. }
  5408. /**
  5409. * Determine if a value is an Object
  5410. *
  5411. * @param {Object} val The value to test
  5412. * @returns {boolean} True if value is an Object, otherwise false
  5413. */
  5414. function isObject(val) {
  5415. return val !== null && typeof val === 'object';
  5416. }
  5417. /**
  5418. * Determine if a value is a plain Object
  5419. *
  5420. * @param {Object} val The value to test
  5421. * @return {boolean} True if value is a plain Object, otherwise false
  5422. */
  5423. function isPlainObject(val) {
  5424. if (kindOf(val) !== 'object') {
  5425. return false;
  5426. }
  5427. var prototype = Object.getPrototypeOf(val);
  5428. return prototype === null || prototype === Object.prototype;
  5429. }
  5430. /**
  5431. * Determine if a value is a Date
  5432. *
  5433. * @function
  5434. * @param {Object} val The value to test
  5435. * @returns {boolean} True if value is a Date, otherwise false
  5436. */
  5437. var isDate = kindOfTest('Date');
  5438. /**
  5439. * Determine if a value is a File
  5440. *
  5441. * @function
  5442. * @param {Object} val The value to test
  5443. * @returns {boolean} True if value is a File, otherwise false
  5444. */
  5445. var isFile = kindOfTest('File');
  5446. /**
  5447. * Determine if a value is a Blob
  5448. *
  5449. * @function
  5450. * @param {Object} val The value to test
  5451. * @returns {boolean} True if value is a Blob, otherwise false
  5452. */
  5453. var isBlob = kindOfTest('Blob');
  5454. /**
  5455. * Determine if a value is a FileList
  5456. *
  5457. * @function
  5458. * @param {Object} val The value to test
  5459. * @returns {boolean} True if value is a File, otherwise false
  5460. */
  5461. var isFileList = kindOfTest('FileList');
  5462. /**
  5463. * Determine if a value is a Function
  5464. *
  5465. * @param {Object} val The value to test
  5466. * @returns {boolean} True if value is a Function, otherwise false
  5467. */
  5468. function isFunction(val) {
  5469. return toString.call(val) === '[object Function]';
  5470. }
  5471. /**
  5472. * Determine if a value is a Stream
  5473. *
  5474. * @param {Object} val The value to test
  5475. * @returns {boolean} True if value is a Stream, otherwise false
  5476. */
  5477. function isStream(val) {
  5478. return isObject(val) && isFunction(val.pipe);
  5479. }
  5480. /**
  5481. * Determine if a value is a FormData
  5482. *
  5483. * @param {Object} thing The value to test
  5484. * @returns {boolean} True if value is an FormData, otherwise false
  5485. */
  5486. function isFormData(thing) {
  5487. var pattern = '[object FormData]';
  5488. return thing && (
  5489. (typeof FormData === 'function' && thing instanceof FormData) ||
  5490. toString.call(thing) === pattern ||
  5491. (isFunction(thing.toString) && thing.toString() === pattern)
  5492. );
  5493. }
  5494. /**
  5495. * Determine if a value is a URLSearchParams object
  5496. * @function
  5497. * @param {Object} val The value to test
  5498. * @returns {boolean} True if value is a URLSearchParams object, otherwise false
  5499. */
  5500. var isURLSearchParams = kindOfTest('URLSearchParams');
  5501. /**
  5502. * Trim excess whitespace off the beginning and end of a string
  5503. *
  5504. * @param {String} str The String to trim
  5505. * @returns {String} The String freed of excess whitespace
  5506. */
  5507. function trim(str) {
  5508. return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  5509. }
  5510. /**
  5511. * Determine if we're running in a standard browser environment
  5512. *
  5513. * This allows axios to run in a web worker, and react-native.
  5514. * Both environments support XMLHttpRequest, but not fully standard globals.
  5515. *
  5516. * web workers:
  5517. * typeof window -> undefined
  5518. * typeof document -> undefined
  5519. *
  5520. * react-native:
  5521. * navigator.product -> 'ReactNative'
  5522. * nativescript
  5523. * navigator.product -> 'NativeScript' or 'NS'
  5524. */
  5525. function isStandardBrowserEnv() {
  5526. if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
  5527. navigator.product === 'NativeScript' ||
  5528. navigator.product === 'NS')) {
  5529. return false;
  5530. }
  5531. return (
  5532. typeof window !== 'undefined' &&
  5533. typeof document !== 'undefined'
  5534. );
  5535. }
  5536. /**
  5537. * Iterate over an Array or an Object invoking a function for each item.
  5538. *
  5539. * If `obj` is an Array callback will be called passing
  5540. * the value, index, and complete array for each item.
  5541. *
  5542. * If 'obj' is an Object callback will be called passing
  5543. * the value, key, and complete object for each property.
  5544. *
  5545. * @param {Object|Array} obj The object to iterate
  5546. * @param {Function} fn The callback to invoke for each item
  5547. */
  5548. function forEach(obj, fn) {
  5549. // Don't bother if no value provided
  5550. if (obj === null || typeof obj === 'undefined') {
  5551. return;
  5552. }
  5553. // Force an array if not already something iterable
  5554. if (typeof obj !== 'object') {
  5555. /*eslint no-param-reassign:0*/
  5556. obj = [obj];
  5557. }
  5558. if (isArray(obj)) {
  5559. // Iterate over array values
  5560. for (var i = 0, l = obj.length; i < l; i++) {
  5561. fn.call(null, obj[i], i, obj);
  5562. }
  5563. } else {
  5564. // Iterate over object keys
  5565. for (var key in obj) {
  5566. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  5567. fn.call(null, obj[key], key, obj);
  5568. }
  5569. }
  5570. }
  5571. }
  5572. /**
  5573. * Accepts varargs expecting each argument to be an object, then
  5574. * immutably merges the properties of each object and returns result.
  5575. *
  5576. * When multiple objects contain the same key the later object in
  5577. * the arguments list will take precedence.
  5578. *
  5579. * Example:
  5580. *
  5581. * ```js
  5582. * var result = merge({foo: 123}, {foo: 456});
  5583. * console.log(result.foo); // outputs 456
  5584. * ```
  5585. *
  5586. * @param {Object} obj1 Object to merge
  5587. * @returns {Object} Result of all merge properties
  5588. */
  5589. function merge(/* obj1, obj2, obj3, ... */) {
  5590. var result = {};
  5591. function assignValue(val, key) {
  5592. if (isPlainObject(result[key]) && isPlainObject(val)) {
  5593. result[key] = merge(result[key], val);
  5594. } else if (isPlainObject(val)) {
  5595. result[key] = merge({}, val);
  5596. } else if (isArray(val)) {
  5597. result[key] = val.slice();
  5598. } else {
  5599. result[key] = val;
  5600. }
  5601. }
  5602. for (var i = 0, l = arguments.length; i < l; i++) {
  5603. forEach(arguments[i], assignValue);
  5604. }
  5605. return result;
  5606. }
  5607. /**
  5608. * Extends object a by mutably adding to it the properties of object b.
  5609. *
  5610. * @param {Object} a The object to be extended
  5611. * @param {Object} b The object to copy properties from
  5612. * @param {Object} thisArg The object to bind function to
  5613. * @return {Object} The resulting value of object a
  5614. */
  5615. function extend(a, b, thisArg) {
  5616. forEach(b, function assignValue(val, key) {
  5617. if (thisArg && typeof val === 'function') {
  5618. a[key] = bind(val, thisArg);
  5619. } else {
  5620. a[key] = val;
  5621. }
  5622. });
  5623. return a;
  5624. }
  5625. /**
  5626. * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
  5627. *
  5628. * @param {string} content with BOM
  5629. * @return {string} content value without BOM
  5630. */
  5631. function stripBOM(content) {
  5632. if (content.charCodeAt(0) === 0xFEFF) {
  5633. content = content.slice(1);
  5634. }
  5635. return content;
  5636. }
  5637. /**
  5638. * Inherit the prototype methods from one constructor into another
  5639. * @param {function} constructor
  5640. * @param {function} superConstructor
  5641. * @param {object} [props]
  5642. * @param {object} [descriptors]
  5643. */
  5644. function inherits(constructor, superConstructor, props, descriptors) {
  5645. constructor.prototype = Object.create(superConstructor.prototype, descriptors);
  5646. constructor.prototype.constructor = constructor;
  5647. props && Object.assign(constructor.prototype, props);
  5648. }
  5649. /**
  5650. * Resolve object with deep prototype chain to a flat object
  5651. * @param {Object} sourceObj source object
  5652. * @param {Object} [destObj]
  5653. * @param {Function} [filter]
  5654. * @returns {Object}
  5655. */
  5656. function toFlatObject(sourceObj, destObj, filter) {
  5657. var props;
  5658. var i;
  5659. var prop;
  5660. var merged = {};
  5661. destObj = destObj || {};
  5662. do {
  5663. props = Object.getOwnPropertyNames(sourceObj);
  5664. i = props.length;
  5665. while (i-- > 0) {
  5666. prop = props[i];
  5667. if (!merged[prop]) {
  5668. destObj[prop] = sourceObj[prop];
  5669. merged[prop] = true;
  5670. }
  5671. }
  5672. sourceObj = Object.getPrototypeOf(sourceObj);
  5673. } while (sourceObj && (!filter || filter(sourceObj, destObj)) && sourceObj !== Object.prototype);
  5674. return destObj;
  5675. }
  5676. /*
  5677. * determines whether a string ends with the characters of a specified string
  5678. * @param {String} str
  5679. * @param {String} searchString
  5680. * @param {Number} [position= 0]
  5681. * @returns {boolean}
  5682. */
  5683. function endsWith(str, searchString, position) {
  5684. str = String(str);
  5685. if (position === undefined || position > str.length) {
  5686. position = str.length;
  5687. }
  5688. position -= searchString.length;
  5689. var lastIndex = str.indexOf(searchString, position);
  5690. return lastIndex !== -1 && lastIndex === position;
  5691. }
  5692. /**
  5693. * Returns new array from array like object
  5694. * @param {*} [thing]
  5695. * @returns {Array}
  5696. */
  5697. function toArray(thing) {
  5698. if (!thing) return null;
  5699. var i = thing.length;
  5700. if (isUndefined(i)) return null;
  5701. var arr = new Array(i);
  5702. while (i-- > 0) {
  5703. arr[i] = thing[i];
  5704. }
  5705. return arr;
  5706. }
  5707. // eslint-disable-next-line func-names
  5708. var isTypedArray = (function(TypedArray) {
  5709. // eslint-disable-next-line func-names
  5710. return function(thing) {
  5711. return TypedArray && thing instanceof TypedArray;
  5712. };
  5713. })(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array));
  5714. var utils = {
  5715. isArray: isArray,
  5716. isArrayBuffer: isArrayBuffer,
  5717. isBuffer: isBuffer,
  5718. isFormData: isFormData,
  5719. isArrayBufferView: isArrayBufferView,
  5720. isString: isString,
  5721. isNumber: isNumber,
  5722. isObject: isObject,
  5723. isPlainObject: isPlainObject,
  5724. isUndefined: isUndefined,
  5725. isDate: isDate,
  5726. isFile: isFile,
  5727. isBlob: isBlob,
  5728. isFunction: isFunction,
  5729. isStream: isStream,
  5730. isURLSearchParams: isURLSearchParams,
  5731. isStandardBrowserEnv: isStandardBrowserEnv,
  5732. forEach: forEach,
  5733. merge: merge,
  5734. extend: extend,
  5735. trim: trim,
  5736. stripBOM: stripBOM,
  5737. inherits: inherits,
  5738. toFlatObject: toFlatObject,
  5739. kindOf: kindOf,
  5740. kindOfTest: kindOfTest,
  5741. endsWith: endsWith,
  5742. toArray: toArray,
  5743. isTypedArray: isTypedArray,
  5744. isFileList: isFileList
  5745. };
  5746. function encode(val) {
  5747. return encodeURIComponent(val).
  5748. replace(/%3A/gi, ':').
  5749. replace(/%24/g, '$').
  5750. replace(/%2C/gi, ',').
  5751. replace(/%20/g, '+').
  5752. replace(/%5B/gi, '[').
  5753. replace(/%5D/gi, ']');
  5754. }
  5755. /**
  5756. * Build a URL by appending params to the end
  5757. *
  5758. * @param {string} url The base of the url (e.g., http://www.google.com)
  5759. * @param {object} [params] The params to be appended
  5760. * @returns {string} The formatted url
  5761. */
  5762. var buildURL = function buildURL(url, params, paramsSerializer) {
  5763. /*eslint no-param-reassign:0*/
  5764. if (!params) {
  5765. return url;
  5766. }
  5767. var serializedParams;
  5768. if (paramsSerializer) {
  5769. serializedParams = paramsSerializer(params);
  5770. } else if (utils.isURLSearchParams(params)) {
  5771. serializedParams = params.toString();
  5772. } else {
  5773. var parts = [];
  5774. utils.forEach(params, function serialize(val, key) {
  5775. if (val === null || typeof val === 'undefined') {
  5776. return;
  5777. }
  5778. if (utils.isArray(val)) {
  5779. key = key + '[]';
  5780. } else {
  5781. val = [val];
  5782. }
  5783. utils.forEach(val, function parseValue(v) {
  5784. if (utils.isDate(v)) {
  5785. v = v.toISOString();
  5786. } else if (utils.isObject(v)) {
  5787. v = JSON.stringify(v);
  5788. }
  5789. parts.push(encode(key) + '=' + encode(v));
  5790. });
  5791. });
  5792. serializedParams = parts.join('&');
  5793. }
  5794. if (serializedParams) {
  5795. var hashmarkIndex = url.indexOf('#');
  5796. if (hashmarkIndex !== -1) {
  5797. url = url.slice(0, hashmarkIndex);
  5798. }
  5799. url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
  5800. }
  5801. return url;
  5802. };
  5803. function InterceptorManager() {
  5804. this.handlers = [];
  5805. }
  5806. /**
  5807. * Add a new interceptor to the stack
  5808. *
  5809. * @param {Function} fulfilled The function to handle `then` for a `Promise`
  5810. * @param {Function} rejected The function to handle `reject` for a `Promise`
  5811. *
  5812. * @return {Number} An ID used to remove interceptor later
  5813. */
  5814. InterceptorManager.prototype.use = function use(fulfilled, rejected, options) {
  5815. this.handlers.push({
  5816. fulfilled: fulfilled,
  5817. rejected: rejected,
  5818. synchronous: options ? options.synchronous : false,
  5819. runWhen: options ? options.runWhen : null
  5820. });
  5821. return this.handlers.length - 1;
  5822. };
  5823. /**
  5824. * Remove an interceptor from the stack
  5825. *
  5826. * @param {Number} id The ID that was returned by `use`
  5827. */
  5828. InterceptorManager.prototype.eject = function eject(id) {
  5829. if (this.handlers[id]) {
  5830. this.handlers[id] = null;
  5831. }
  5832. };
  5833. /**
  5834. * Iterate over all the registered interceptors
  5835. *
  5836. * This method is particularly useful for skipping over any
  5837. * interceptors that may have become `null` calling `eject`.
  5838. *
  5839. * @param {Function} fn The function to call for each interceptor
  5840. */
  5841. InterceptorManager.prototype.forEach = function forEach(fn) {
  5842. utils.forEach(this.handlers, function forEachHandler(h) {
  5843. if (h !== null) {
  5844. fn(h);
  5845. }
  5846. });
  5847. };
  5848. var InterceptorManager_1 = InterceptorManager;
  5849. var normalizeHeaderName = function normalizeHeaderName(headers, normalizedName) {
  5850. utils.forEach(headers, function processHeader(value, name) {
  5851. if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
  5852. headers[normalizedName] = value;
  5853. delete headers[name];
  5854. }
  5855. });
  5856. };
  5857. /**
  5858. * Create an Error with the specified message, config, error code, request and response.
  5859. *
  5860. * @param {string} message The error message.
  5861. * @param {string} [code] The error code (for example, 'ECONNABORTED').
  5862. * @param {Object} [config] The config.
  5863. * @param {Object} [request] The request.
  5864. * @param {Object} [response] The response.
  5865. * @returns {Error} The created error.
  5866. */
  5867. function AxiosError(message, code, config, request, response) {
  5868. Error.call(this);
  5869. this.message = message;
  5870. this.name = 'AxiosError';
  5871. code && (this.code = code);
  5872. config && (this.config = config);
  5873. request && (this.request = request);
  5874. response && (this.response = response);
  5875. }
  5876. utils.inherits(AxiosError, Error, {
  5877. toJSON: function toJSON() {
  5878. return {
  5879. // Standard
  5880. message: this.message,
  5881. name: this.name,
  5882. // Microsoft
  5883. description: this.description,
  5884. number: this.number,
  5885. // Mozilla
  5886. fileName: this.fileName,
  5887. lineNumber: this.lineNumber,
  5888. columnNumber: this.columnNumber,
  5889. stack: this.stack,
  5890. // Axios
  5891. config: this.config,
  5892. code: this.code,
  5893. status: this.response && this.response.status ? this.response.status : null
  5894. };
  5895. }
  5896. });
  5897. var prototype = AxiosError.prototype;
  5898. var descriptors = {};
  5899. [
  5900. 'ERR_BAD_OPTION_VALUE',
  5901. 'ERR_BAD_OPTION',
  5902. 'ECONNABORTED',
  5903. 'ETIMEDOUT',
  5904. 'ERR_NETWORK',
  5905. 'ERR_FR_TOO_MANY_REDIRECTS',
  5906. 'ERR_DEPRECATED',
  5907. 'ERR_BAD_RESPONSE',
  5908. 'ERR_BAD_REQUEST',
  5909. 'ERR_CANCELED'
  5910. // eslint-disable-next-line func-names
  5911. ].forEach(function(code) {
  5912. descriptors[code] = {value: code};
  5913. });
  5914. Object.defineProperties(AxiosError, descriptors);
  5915. Object.defineProperty(prototype, 'isAxiosError', {value: true});
  5916. // eslint-disable-next-line func-names
  5917. AxiosError.from = function(error, code, config, request, response, customProps) {
  5918. var axiosError = Object.create(prototype);
  5919. utils.toFlatObject(error, axiosError, function filter(obj) {
  5920. return obj !== Error.prototype;
  5921. });
  5922. AxiosError.call(axiosError, error.message, code, config, request, response);
  5923. axiosError.name = error.name;
  5924. customProps && Object.assign(axiosError, customProps);
  5925. return axiosError;
  5926. };
  5927. var AxiosError_1 = AxiosError;
  5928. var transitional = {
  5929. silentJSONParsing: true,
  5930. forcedJSONParsing: true,
  5931. clarifyTimeoutError: false
  5932. };
  5933. /**
  5934. * Convert a data object to FormData
  5935. * @param {Object} obj
  5936. * @param {?Object} [formData]
  5937. * @returns {Object}
  5938. **/
  5939. function toFormData(obj, formData) {
  5940. // eslint-disable-next-line no-param-reassign
  5941. formData = formData || new FormData();
  5942. var stack = [];
  5943. function convertValue(value) {
  5944. if (value === null) return '';
  5945. if (utils.isDate(value)) {
  5946. return value.toISOString();
  5947. }
  5948. if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
  5949. return typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
  5950. }
  5951. return value;
  5952. }
  5953. function build(data, parentKey) {
  5954. if (utils.isPlainObject(data) || utils.isArray(data)) {
  5955. if (stack.indexOf(data) !== -1) {
  5956. throw Error('Circular reference detected in ' + parentKey);
  5957. }
  5958. stack.push(data);
  5959. utils.forEach(data, function each(value, key) {
  5960. if (utils.isUndefined(value)) return;
  5961. var fullKey = parentKey ? parentKey + '.' + key : key;
  5962. var arr;
  5963. if (value && !parentKey && typeof value === 'object') {
  5964. if (utils.endsWith(key, '{}')) {
  5965. // eslint-disable-next-line no-param-reassign
  5966. value = JSON.stringify(value);
  5967. } else if (utils.endsWith(key, '[]') && (arr = utils.toArray(value))) {
  5968. // eslint-disable-next-line func-names
  5969. arr.forEach(function(el) {
  5970. !utils.isUndefined(el) && formData.append(fullKey, convertValue(el));
  5971. });
  5972. return;
  5973. }
  5974. }
  5975. build(value, fullKey);
  5976. });
  5977. stack.pop();
  5978. } else {
  5979. formData.append(parentKey, convertValue(data));
  5980. }
  5981. }
  5982. build(obj);
  5983. return formData;
  5984. }
  5985. var toFormData_1 = toFormData;
  5986. /**
  5987. * Resolve or reject a Promise based on response status.
  5988. *
  5989. * @param {Function} resolve A function that resolves the promise.
  5990. * @param {Function} reject A function that rejects the promise.
  5991. * @param {object} response The response.
  5992. */
  5993. var settle = function settle(resolve, reject, response) {
  5994. var validateStatus = response.config.validateStatus;
  5995. if (!response.status || !validateStatus || validateStatus(response.status)) {
  5996. resolve(response);
  5997. } else {
  5998. reject(new AxiosError_1(
  5999. 'Request failed with status code ' + response.status,
  6000. [AxiosError_1.ERR_BAD_REQUEST, AxiosError_1.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4],
  6001. response.config,
  6002. response.request,
  6003. response
  6004. ));
  6005. }
  6006. };
  6007. var cookies = (
  6008. utils.isStandardBrowserEnv() ?
  6009. // Standard browser envs support document.cookie
  6010. (function standardBrowserEnv() {
  6011. return {
  6012. write: function write(name, value, expires, path, domain, secure) {
  6013. var cookie = [];
  6014. cookie.push(name + '=' + encodeURIComponent(value));
  6015. if (utils.isNumber(expires)) {
  6016. cookie.push('expires=' + new Date(expires).toGMTString());
  6017. }
  6018. if (utils.isString(path)) {
  6019. cookie.push('path=' + path);
  6020. }
  6021. if (utils.isString(domain)) {
  6022. cookie.push('domain=' + domain);
  6023. }
  6024. if (secure === true) {
  6025. cookie.push('secure');
  6026. }
  6027. document.cookie = cookie.join('; ');
  6028. },
  6029. read: function read(name) {
  6030. var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)'));
  6031. return (match ? decodeURIComponent(match[3]) : null);
  6032. },
  6033. remove: function remove(name) {
  6034. this.write(name, '', Date.now() - 86400000);
  6035. }
  6036. };
  6037. })() :
  6038. // Non standard browser env (web workers, react-native) lack needed support.
  6039. (function nonStandardBrowserEnv() {
  6040. return {
  6041. write: function write() {},
  6042. read: function read() { return null; },
  6043. remove: function remove() {}
  6044. };
  6045. })()
  6046. );
  6047. /**
  6048. * Determines whether the specified URL is absolute
  6049. *
  6050. * @param {string} url The URL to test
  6051. * @returns {boolean} True if the specified URL is absolute, otherwise false
  6052. */
  6053. var isAbsoluteURL = function isAbsoluteURL(url) {
  6054. // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
  6055. // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
  6056. // by any combination of letters, digits, plus, period, or hyphen.
  6057. return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url);
  6058. };
  6059. /**
  6060. * Creates a new URL by combining the specified URLs
  6061. *
  6062. * @param {string} baseURL The base URL
  6063. * @param {string} relativeURL The relative URL
  6064. * @returns {string} The combined URL
  6065. */
  6066. var combineURLs = function combineURLs(baseURL, relativeURL) {
  6067. return relativeURL
  6068. ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
  6069. : baseURL;
  6070. };
  6071. /**
  6072. * Creates a new URL by combining the baseURL with the requestedURL,
  6073. * only when the requestedURL is not already an absolute URL.
  6074. * If the requestURL is absolute, this function returns the requestedURL untouched.
  6075. *
  6076. * @param {string} baseURL The base URL
  6077. * @param {string} requestedURL Absolute or relative URL to combine
  6078. * @returns {string} The combined full path
  6079. */
  6080. var buildFullPath = function buildFullPath(baseURL, requestedURL) {
  6081. if (baseURL && !isAbsoluteURL(requestedURL)) {
  6082. return combineURLs(baseURL, requestedURL);
  6083. }
  6084. return requestedURL;
  6085. };
  6086. // Headers whose duplicates are ignored by node
  6087. // c.f. https://nodejs.org/api/http.html#http_message_headers
  6088. var ignoreDuplicateOf = [
  6089. 'age', 'authorization', 'content-length', 'content-type', 'etag',
  6090. 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',
  6091. 'last-modified', 'location', 'max-forwards', 'proxy-authorization',
  6092. 'referer', 'retry-after', 'user-agent'
  6093. ];
  6094. /**
  6095. * Parse headers into an object
  6096. *
  6097. * ```
  6098. * Date: Wed, 27 Aug 2014 08:58:49 GMT
  6099. * Content-Type: application/json
  6100. * Connection: keep-alive
  6101. * Transfer-Encoding: chunked
  6102. * ```
  6103. *
  6104. * @param {String} headers Headers needing to be parsed
  6105. * @returns {Object} Headers parsed into an object
  6106. */
  6107. var parseHeaders = function parseHeaders(headers) {
  6108. var parsed = {};
  6109. var key;
  6110. var val;
  6111. var i;
  6112. if (!headers) { return parsed; }
  6113. utils.forEach(headers.split('\n'), function parser(line) {
  6114. i = line.indexOf(':');
  6115. key = utils.trim(line.substr(0, i)).toLowerCase();
  6116. val = utils.trim(line.substr(i + 1));
  6117. if (key) {
  6118. if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {
  6119. return;
  6120. }
  6121. if (key === 'set-cookie') {
  6122. parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);
  6123. } else {
  6124. parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
  6125. }
  6126. }
  6127. });
  6128. return parsed;
  6129. };
  6130. var isURLSameOrigin = (
  6131. utils.isStandardBrowserEnv() ?
  6132. // Standard browser envs have full support of the APIs needed to test
  6133. // whether the request URL is of the same origin as current location.
  6134. (function standardBrowserEnv() {
  6135. var msie = /(msie|trident)/i.test(navigator.userAgent);
  6136. var urlParsingNode = document.createElement('a');
  6137. var originURL;
  6138. /**
  6139. * Parse a URL to discover it's components
  6140. *
  6141. * @param {String} url The URL to be parsed
  6142. * @returns {Object}
  6143. */
  6144. function resolveURL(url) {
  6145. var href = url;
  6146. if (msie) {
  6147. // IE needs attribute set twice to normalize properties
  6148. urlParsingNode.setAttribute('href', href);
  6149. href = urlParsingNode.href;
  6150. }
  6151. urlParsingNode.setAttribute('href', href);
  6152. // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
  6153. return {
  6154. href: urlParsingNode.href,
  6155. protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
  6156. host: urlParsingNode.host,
  6157. search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
  6158. hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
  6159. hostname: urlParsingNode.hostname,
  6160. port: urlParsingNode.port,
  6161. pathname: (urlParsingNode.pathname.charAt(0) === '/') ?
  6162. urlParsingNode.pathname :
  6163. '/' + urlParsingNode.pathname
  6164. };
  6165. }
  6166. originURL = resolveURL(window.location.href);
  6167. /**
  6168. * Determine if a URL shares the same origin as the current location
  6169. *
  6170. * @param {String} requestURL The URL to test
  6171. * @returns {boolean} True if URL shares the same origin, otherwise false
  6172. */
  6173. return function isURLSameOrigin(requestURL) {
  6174. var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;
  6175. return (parsed.protocol === originURL.protocol &&
  6176. parsed.host === originURL.host);
  6177. };
  6178. })() :
  6179. // Non standard browser envs (web workers, react-native) lack needed support.
  6180. (function nonStandardBrowserEnv() {
  6181. return function isURLSameOrigin() {
  6182. return true;
  6183. };
  6184. })()
  6185. );
  6186. /**
  6187. * A `CanceledError` is an object that is thrown when an operation is canceled.
  6188. *
  6189. * @class
  6190. * @param {string=} message The message.
  6191. */
  6192. function CanceledError(message) {
  6193. // eslint-disable-next-line no-eq-null,eqeqeq
  6194. AxiosError_1.call(this, message == null ? 'canceled' : message, AxiosError_1.ERR_CANCELED);
  6195. this.name = 'CanceledError';
  6196. }
  6197. utils.inherits(CanceledError, AxiosError_1, {
  6198. __CANCEL__: true
  6199. });
  6200. var CanceledError_1 = CanceledError;
  6201. var parseProtocol = function parseProtocol(url) {
  6202. var match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url);
  6203. return match && match[1] || '';
  6204. };
  6205. var xhr = function xhrAdapter(config) {
  6206. return new Promise(function dispatchXhrRequest(resolve, reject) {
  6207. var requestData = config.data;
  6208. var requestHeaders = config.headers;
  6209. var responseType = config.responseType;
  6210. var onCanceled;
  6211. function done() {
  6212. if (config.cancelToken) {
  6213. config.cancelToken.unsubscribe(onCanceled);
  6214. }
  6215. if (config.signal) {
  6216. config.signal.removeEventListener('abort', onCanceled);
  6217. }
  6218. }
  6219. if (utils.isFormData(requestData) && utils.isStandardBrowserEnv()) {
  6220. delete requestHeaders['Content-Type']; // Let the browser set it
  6221. }
  6222. var request = new XMLHttpRequest();
  6223. // HTTP basic authentication
  6224. if (config.auth) {
  6225. var username = config.auth.username || '';
  6226. var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
  6227. requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
  6228. }
  6229. var fullPath = buildFullPath(config.baseURL, config.url);
  6230. request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
  6231. // Set the request timeout in MS
  6232. request.timeout = config.timeout;
  6233. function onloadend() {
  6234. if (!request) {
  6235. return;
  6236. }
  6237. // Prepare the response
  6238. var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
  6239. var responseData = !responseType || responseType === 'text' || responseType === 'json' ?
  6240. request.responseText : request.response;
  6241. var response = {
  6242. data: responseData,
  6243. status: request.status,
  6244. statusText: request.statusText,
  6245. headers: responseHeaders,
  6246. config: config,
  6247. request: request
  6248. };
  6249. settle(function _resolve(value) {
  6250. resolve(value);
  6251. done();
  6252. }, function _reject(err) {
  6253. reject(err);
  6254. done();
  6255. }, response);
  6256. // Clean up request
  6257. request = null;
  6258. }
  6259. if ('onloadend' in request) {
  6260. // Use onloadend if available
  6261. request.onloadend = onloadend;
  6262. } else {
  6263. // Listen for ready state to emulate onloadend
  6264. request.onreadystatechange = function handleLoad() {
  6265. if (!request || request.readyState !== 4) {
  6266. return;
  6267. }
  6268. // The request errored out and we didn't get a response, this will be
  6269. // handled by onerror instead
  6270. // With one exception: request that using file: protocol, most browsers
  6271. // will return status as 0 even though it's a successful request
  6272. if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
  6273. return;
  6274. }
  6275. // readystate handler is calling before onerror or ontimeout handlers,
  6276. // so we should call onloadend on the next 'tick'
  6277. setTimeout(onloadend);
  6278. };
  6279. }
  6280. // Handle browser request cancellation (as opposed to a manual cancellation)
  6281. request.onabort = function handleAbort() {
  6282. if (!request) {
  6283. return;
  6284. }
  6285. reject(new AxiosError_1('Request aborted', AxiosError_1.ECONNABORTED, config, request));
  6286. // Clean up request
  6287. request = null;
  6288. };
  6289. // Handle low level network errors
  6290. request.onerror = function handleError() {
  6291. // Real errors are hidden from us by the browser
  6292. // onerror should only fire if it's a network error
  6293. reject(new AxiosError_1('Network Error', AxiosError_1.ERR_NETWORK, config, request, request));
  6294. // Clean up request
  6295. request = null;
  6296. };
  6297. // Handle timeout
  6298. request.ontimeout = function handleTimeout() {
  6299. var timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
  6300. var transitional$1 = config.transitional || transitional;
  6301. if (config.timeoutErrorMessage) {
  6302. timeoutErrorMessage = config.timeoutErrorMessage;
  6303. }
  6304. reject(new AxiosError_1(
  6305. timeoutErrorMessage,
  6306. transitional$1.clarifyTimeoutError ? AxiosError_1.ETIMEDOUT : AxiosError_1.ECONNABORTED,
  6307. config,
  6308. request));
  6309. // Clean up request
  6310. request = null;
  6311. };
  6312. // Add xsrf header
  6313. // This is only done if running in a standard browser environment.
  6314. // Specifically not if we're in a web worker, or react-native.
  6315. if (utils.isStandardBrowserEnv()) {
  6316. // Add xsrf header
  6317. var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
  6318. cookies.read(config.xsrfCookieName) :
  6319. undefined;
  6320. if (xsrfValue) {
  6321. requestHeaders[config.xsrfHeaderName] = xsrfValue;
  6322. }
  6323. }
  6324. // Add headers to the request
  6325. if ('setRequestHeader' in request) {
  6326. utils.forEach(requestHeaders, function setRequestHeader(val, key) {
  6327. if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
  6328. // Remove Content-Type if data is undefined
  6329. delete requestHeaders[key];
  6330. } else {
  6331. // Otherwise add header to the request
  6332. request.setRequestHeader(key, val);
  6333. }
  6334. });
  6335. }
  6336. // Add withCredentials to request if needed
  6337. if (!utils.isUndefined(config.withCredentials)) {
  6338. request.withCredentials = !!config.withCredentials;
  6339. }
  6340. // Add responseType to request if needed
  6341. if (responseType && responseType !== 'json') {
  6342. request.responseType = config.responseType;
  6343. }
  6344. // Handle progress if needed
  6345. if (typeof config.onDownloadProgress === 'function') {
  6346. request.addEventListener('progress', config.onDownloadProgress);
  6347. }
  6348. // Not all browsers support upload events
  6349. if (typeof config.onUploadProgress === 'function' && request.upload) {
  6350. request.upload.addEventListener('progress', config.onUploadProgress);
  6351. }
  6352. if (config.cancelToken || config.signal) {
  6353. // Handle cancellation
  6354. // eslint-disable-next-line func-names
  6355. onCanceled = function(cancel) {
  6356. if (!request) {
  6357. return;
  6358. }
  6359. reject(!cancel || (cancel && cancel.type) ? new CanceledError_1() : cancel);
  6360. request.abort();
  6361. request = null;
  6362. };
  6363. config.cancelToken && config.cancelToken.subscribe(onCanceled);
  6364. if (config.signal) {
  6365. config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
  6366. }
  6367. }
  6368. if (!requestData) {
  6369. requestData = null;
  6370. }
  6371. var protocol = parseProtocol(fullPath);
  6372. if (protocol && [ 'http', 'https', 'file' ].indexOf(protocol) === -1) {
  6373. reject(new AxiosError_1('Unsupported protocol ' + protocol + ':', AxiosError_1.ERR_BAD_REQUEST, config));
  6374. return;
  6375. }
  6376. // Send the request
  6377. request.send(requestData);
  6378. });
  6379. };
  6380. // eslint-disable-next-line strict
  6381. var _null = null;
  6382. var DEFAULT_CONTENT_TYPE = {
  6383. 'Content-Type': 'application/x-www-form-urlencoded'
  6384. };
  6385. function setContentTypeIfUnset(headers, value) {
  6386. if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
  6387. headers['Content-Type'] = value;
  6388. }
  6389. }
  6390. function getDefaultAdapter() {
  6391. var adapter;
  6392. if (typeof XMLHttpRequest !== 'undefined') {
  6393. // For browsers use XHR adapter
  6394. adapter = xhr;
  6395. } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
  6396. // For node use HTTP adapter
  6397. adapter = xhr;
  6398. }
  6399. return adapter;
  6400. }
  6401. function stringifySafely(rawValue, parser, encoder) {
  6402. if (utils.isString(rawValue)) {
  6403. try {
  6404. (parser || JSON.parse)(rawValue);
  6405. return utils.trim(rawValue);
  6406. } catch (e) {
  6407. if (e.name !== 'SyntaxError') {
  6408. throw e;
  6409. }
  6410. }
  6411. }
  6412. return (encoder || JSON.stringify)(rawValue);
  6413. }
  6414. var defaults = {
  6415. transitional: transitional,
  6416. adapter: getDefaultAdapter(),
  6417. transformRequest: [function transformRequest(data, headers) {
  6418. normalizeHeaderName(headers, 'Accept');
  6419. normalizeHeaderName(headers, 'Content-Type');
  6420. if (utils.isFormData(data) ||
  6421. utils.isArrayBuffer(data) ||
  6422. utils.isBuffer(data) ||
  6423. utils.isStream(data) ||
  6424. utils.isFile(data) ||
  6425. utils.isBlob(data)
  6426. ) {
  6427. return data;
  6428. }
  6429. if (utils.isArrayBufferView(data)) {
  6430. return data.buffer;
  6431. }
  6432. if (utils.isURLSearchParams(data)) {
  6433. setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
  6434. return data.toString();
  6435. }
  6436. var isObjectPayload = utils.isObject(data);
  6437. var contentType = headers && headers['Content-Type'];
  6438. var isFileList;
  6439. if ((isFileList = utils.isFileList(data)) || (isObjectPayload && contentType === 'multipart/form-data')) {
  6440. var _FormData = this.env && this.env.FormData;
  6441. return toFormData_1(isFileList ? {'files[]': data} : data, _FormData && new _FormData());
  6442. } else if (isObjectPayload || contentType === 'application/json') {
  6443. setContentTypeIfUnset(headers, 'application/json');
  6444. return stringifySafely(data);
  6445. }
  6446. return data;
  6447. }],
  6448. transformResponse: [function transformResponse(data) {
  6449. var transitional = this.transitional || defaults.transitional;
  6450. var silentJSONParsing = transitional && transitional.silentJSONParsing;
  6451. var forcedJSONParsing = transitional && transitional.forcedJSONParsing;
  6452. var strictJSONParsing = !silentJSONParsing && this.responseType === 'json';
  6453. if (strictJSONParsing || (forcedJSONParsing && utils.isString(data) && data.length)) {
  6454. try {
  6455. return JSON.parse(data);
  6456. } catch (e) {
  6457. if (strictJSONParsing) {
  6458. if (e.name === 'SyntaxError') {
  6459. throw AxiosError_1.from(e, AxiosError_1.ERR_BAD_RESPONSE, this, null, this.response);
  6460. }
  6461. throw e;
  6462. }
  6463. }
  6464. }
  6465. return data;
  6466. }],
  6467. /**
  6468. * A timeout in milliseconds to abort a request. If set to 0 (default) a
  6469. * timeout is not created.
  6470. */
  6471. timeout: 0,
  6472. xsrfCookieName: 'XSRF-TOKEN',
  6473. xsrfHeaderName: 'X-XSRF-TOKEN',
  6474. maxContentLength: -1,
  6475. maxBodyLength: -1,
  6476. env: {
  6477. FormData: _null
  6478. },
  6479. validateStatus: function validateStatus(status) {
  6480. return status >= 200 && status < 300;
  6481. },
  6482. headers: {
  6483. common: {
  6484. 'Accept': 'application/json, text/plain, */*'
  6485. }
  6486. }
  6487. };
  6488. utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
  6489. defaults.headers[method] = {};
  6490. });
  6491. utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  6492. defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
  6493. });
  6494. var defaults_1 = defaults;
  6495. /**
  6496. * Transform the data for a request or a response
  6497. *
  6498. * @param {Object|String} data The data to be transformed
  6499. * @param {Array} headers The headers for the request or response
  6500. * @param {Array|Function} fns A single function or Array of functions
  6501. * @returns {*} The resulting transformed data
  6502. */
  6503. var transformData = function transformData(data, headers, fns) {
  6504. var context = this || defaults_1;
  6505. /*eslint no-param-reassign:0*/
  6506. utils.forEach(fns, function transform(fn) {
  6507. data = fn.call(context, data, headers);
  6508. });
  6509. return data;
  6510. };
  6511. var isCancel = function isCancel(value) {
  6512. return !!(value && value.__CANCEL__);
  6513. };
  6514. /**
  6515. * Throws a `CanceledError` if cancellation has been requested.
  6516. */
  6517. function throwIfCancellationRequested(config) {
  6518. if (config.cancelToken) {
  6519. config.cancelToken.throwIfRequested();
  6520. }
  6521. if (config.signal && config.signal.aborted) {
  6522. throw new CanceledError_1();
  6523. }
  6524. }
  6525. /**
  6526. * Dispatch a request to the server using the configured adapter.
  6527. *
  6528. * @param {object} config The config that is to be used for the request
  6529. * @returns {Promise} The Promise to be fulfilled
  6530. */
  6531. var dispatchRequest = function dispatchRequest(config) {
  6532. throwIfCancellationRequested(config);
  6533. // Ensure headers exist
  6534. config.headers = config.headers || {};
  6535. // Transform request data
  6536. config.data = transformData.call(
  6537. config,
  6538. config.data,
  6539. config.headers,
  6540. config.transformRequest
  6541. );
  6542. // Flatten headers
  6543. config.headers = utils.merge(
  6544. config.headers.common || {},
  6545. config.headers[config.method] || {},
  6546. config.headers
  6547. );
  6548. utils.forEach(
  6549. ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
  6550. function cleanHeaderConfig(method) {
  6551. delete config.headers[method];
  6552. }
  6553. );
  6554. var adapter = config.adapter || defaults_1.adapter;
  6555. return adapter(config).then(function onAdapterResolution(response) {
  6556. throwIfCancellationRequested(config);
  6557. // Transform response data
  6558. response.data = transformData.call(
  6559. config,
  6560. response.data,
  6561. response.headers,
  6562. config.transformResponse
  6563. );
  6564. return response;
  6565. }, function onAdapterRejection(reason) {
  6566. if (!isCancel(reason)) {
  6567. throwIfCancellationRequested(config);
  6568. // Transform response data
  6569. if (reason && reason.response) {
  6570. reason.response.data = transformData.call(
  6571. config,
  6572. reason.response.data,
  6573. reason.response.headers,
  6574. config.transformResponse
  6575. );
  6576. }
  6577. }
  6578. return Promise.reject(reason);
  6579. });
  6580. };
  6581. /**
  6582. * Config-specific merge-function which creates a new config-object
  6583. * by merging two configuration objects together.
  6584. *
  6585. * @param {Object} config1
  6586. * @param {Object} config2
  6587. * @returns {Object} New object resulting from merging config2 to config1
  6588. */
  6589. var mergeConfig = function mergeConfig(config1, config2) {
  6590. // eslint-disable-next-line no-param-reassign
  6591. config2 = config2 || {};
  6592. var config = {};
  6593. function getMergedValue(target, source) {
  6594. if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
  6595. return utils.merge(target, source);
  6596. } else if (utils.isPlainObject(source)) {
  6597. return utils.merge({}, source);
  6598. } else if (utils.isArray(source)) {
  6599. return source.slice();
  6600. }
  6601. return source;
  6602. }
  6603. // eslint-disable-next-line consistent-return
  6604. function mergeDeepProperties(prop) {
  6605. if (!utils.isUndefined(config2[prop])) {
  6606. return getMergedValue(config1[prop], config2[prop]);
  6607. } else if (!utils.isUndefined(config1[prop])) {
  6608. return getMergedValue(undefined, config1[prop]);
  6609. }
  6610. }
  6611. // eslint-disable-next-line consistent-return
  6612. function valueFromConfig2(prop) {
  6613. if (!utils.isUndefined(config2[prop])) {
  6614. return getMergedValue(undefined, config2[prop]);
  6615. }
  6616. }
  6617. // eslint-disable-next-line consistent-return
  6618. function defaultToConfig2(prop) {
  6619. if (!utils.isUndefined(config2[prop])) {
  6620. return getMergedValue(undefined, config2[prop]);
  6621. } else if (!utils.isUndefined(config1[prop])) {
  6622. return getMergedValue(undefined, config1[prop]);
  6623. }
  6624. }
  6625. // eslint-disable-next-line consistent-return
  6626. function mergeDirectKeys(prop) {
  6627. if (prop in config2) {
  6628. return getMergedValue(config1[prop], config2[prop]);
  6629. } else if (prop in config1) {
  6630. return getMergedValue(undefined, config1[prop]);
  6631. }
  6632. }
  6633. var mergeMap = {
  6634. 'url': valueFromConfig2,
  6635. 'method': valueFromConfig2,
  6636. 'data': valueFromConfig2,
  6637. 'baseURL': defaultToConfig2,
  6638. 'transformRequest': defaultToConfig2,
  6639. 'transformResponse': defaultToConfig2,
  6640. 'paramsSerializer': defaultToConfig2,
  6641. 'timeout': defaultToConfig2,
  6642. 'timeoutMessage': defaultToConfig2,
  6643. 'withCredentials': defaultToConfig2,
  6644. 'adapter': defaultToConfig2,
  6645. 'responseType': defaultToConfig2,
  6646. 'xsrfCookieName': defaultToConfig2,
  6647. 'xsrfHeaderName': defaultToConfig2,
  6648. 'onUploadProgress': defaultToConfig2,
  6649. 'onDownloadProgress': defaultToConfig2,
  6650. 'decompress': defaultToConfig2,
  6651. 'maxContentLength': defaultToConfig2,
  6652. 'maxBodyLength': defaultToConfig2,
  6653. 'beforeRedirect': defaultToConfig2,
  6654. 'transport': defaultToConfig2,
  6655. 'httpAgent': defaultToConfig2,
  6656. 'httpsAgent': defaultToConfig2,
  6657. 'cancelToken': defaultToConfig2,
  6658. 'socketPath': defaultToConfig2,
  6659. 'responseEncoding': defaultToConfig2,
  6660. 'validateStatus': mergeDirectKeys
  6661. };
  6662. utils.forEach(Object.keys(config1).concat(Object.keys(config2)), function computeConfigValue(prop) {
  6663. var merge = mergeMap[prop] || mergeDeepProperties;
  6664. var configValue = merge(prop);
  6665. (utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue);
  6666. });
  6667. return config;
  6668. };
  6669. var data = {
  6670. "version": "0.27.2"
  6671. };
  6672. var VERSION = data.version;
  6673. var validators$1 = {};
  6674. // eslint-disable-next-line func-names
  6675. ['object', 'boolean', 'number', 'function', 'string', 'symbol'].forEach(function(type, i) {
  6676. validators$1[type] = function validator(thing) {
  6677. return typeof thing === type || 'a' + (i < 1 ? 'n ' : ' ') + type;
  6678. };
  6679. });
  6680. var deprecatedWarnings = {};
  6681. /**
  6682. * Transitional option validator
  6683. * @param {function|boolean?} validator - set to false if the transitional option has been removed
  6684. * @param {string?} version - deprecated version / removed since version
  6685. * @param {string?} message - some message with additional info
  6686. * @returns {function}
  6687. */
  6688. validators$1.transitional = function transitional(validator, version, message) {
  6689. function formatMessage(opt, desc) {
  6690. return '[Axios v' + VERSION + '] Transitional option \'' + opt + '\'' + desc + (message ? '. ' + message : '');
  6691. }
  6692. // eslint-disable-next-line func-names
  6693. return function(value, opt, opts) {
  6694. if (validator === false) {
  6695. throw new AxiosError_1(
  6696. formatMessage(opt, ' has been removed' + (version ? ' in ' + version : '')),
  6697. AxiosError_1.ERR_DEPRECATED
  6698. );
  6699. }
  6700. if (version && !deprecatedWarnings[opt]) {
  6701. deprecatedWarnings[opt] = true;
  6702. // eslint-disable-next-line no-console
  6703. console.warn(
  6704. formatMessage(
  6705. opt,
  6706. ' has been deprecated since v' + version + ' and will be removed in the near future'
  6707. )
  6708. );
  6709. }
  6710. return validator ? validator(value, opt, opts) : true;
  6711. };
  6712. };
  6713. /**
  6714. * Assert object's properties type
  6715. * @param {object} options
  6716. * @param {object} schema
  6717. * @param {boolean?} allowUnknown
  6718. */
  6719. function assertOptions(options, schema, allowUnknown) {
  6720. if (typeof options !== 'object') {
  6721. throw new AxiosError_1('options must be an object', AxiosError_1.ERR_BAD_OPTION_VALUE);
  6722. }
  6723. var keys = Object.keys(options);
  6724. var i = keys.length;
  6725. while (i-- > 0) {
  6726. var opt = keys[i];
  6727. var validator = schema[opt];
  6728. if (validator) {
  6729. var value = options[opt];
  6730. var result = value === undefined || validator(value, opt, options);
  6731. if (result !== true) {
  6732. throw new AxiosError_1('option ' + opt + ' must be ' + result, AxiosError_1.ERR_BAD_OPTION_VALUE);
  6733. }
  6734. continue;
  6735. }
  6736. if (allowUnknown !== true) {
  6737. throw new AxiosError_1('Unknown option ' + opt, AxiosError_1.ERR_BAD_OPTION);
  6738. }
  6739. }
  6740. }
  6741. var validator = {
  6742. assertOptions: assertOptions,
  6743. validators: validators$1
  6744. };
  6745. var validators = validator.validators;
  6746. /**
  6747. * Create a new instance of Axios
  6748. *
  6749. * @param {Object} instanceConfig The default config for the instance
  6750. */
  6751. function Axios(instanceConfig) {
  6752. this.defaults = instanceConfig;
  6753. this.interceptors = {
  6754. request: new InterceptorManager_1(),
  6755. response: new InterceptorManager_1()
  6756. };
  6757. }
  6758. /**
  6759. * Dispatch a request
  6760. *
  6761. * @param {Object} config The config specific for this request (merged with this.defaults)
  6762. */
  6763. Axios.prototype.request = function request(configOrUrl, config) {
  6764. /*eslint no-param-reassign:0*/
  6765. // Allow for axios('example/url'[, config]) a la fetch API
  6766. if (typeof configOrUrl === 'string') {
  6767. config = config || {};
  6768. config.url = configOrUrl;
  6769. } else {
  6770. config = configOrUrl || {};
  6771. }
  6772. config = mergeConfig(this.defaults, config);
  6773. // Set config.method
  6774. if (config.method) {
  6775. config.method = config.method.toLowerCase();
  6776. } else if (this.defaults.method) {
  6777. config.method = this.defaults.method.toLowerCase();
  6778. } else {
  6779. config.method = 'get';
  6780. }
  6781. var transitional = config.transitional;
  6782. if (transitional !== undefined) {
  6783. validator.assertOptions(transitional, {
  6784. silentJSONParsing: validators.transitional(validators.boolean),
  6785. forcedJSONParsing: validators.transitional(validators.boolean),
  6786. clarifyTimeoutError: validators.transitional(validators.boolean)
  6787. }, false);
  6788. }
  6789. // filter out skipped interceptors
  6790. var requestInterceptorChain = [];
  6791. var synchronousRequestInterceptors = true;
  6792. this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
  6793. if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
  6794. return;
  6795. }
  6796. synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;
  6797. requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  6798. });
  6799. var responseInterceptorChain = [];
  6800. this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  6801. responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  6802. });
  6803. var promise;
  6804. if (!synchronousRequestInterceptors) {
  6805. var chain = [dispatchRequest, undefined];
  6806. Array.prototype.unshift.apply(chain, requestInterceptorChain);
  6807. chain = chain.concat(responseInterceptorChain);
  6808. promise = Promise.resolve(config);
  6809. while (chain.length) {
  6810. promise = promise.then(chain.shift(), chain.shift());
  6811. }
  6812. return promise;
  6813. }
  6814. var newConfig = config;
  6815. while (requestInterceptorChain.length) {
  6816. var onFulfilled = requestInterceptorChain.shift();
  6817. var onRejected = requestInterceptorChain.shift();
  6818. try {
  6819. newConfig = onFulfilled(newConfig);
  6820. } catch (error) {
  6821. onRejected(error);
  6822. break;
  6823. }
  6824. }
  6825. try {
  6826. promise = dispatchRequest(newConfig);
  6827. } catch (error) {
  6828. return Promise.reject(error);
  6829. }
  6830. while (responseInterceptorChain.length) {
  6831. promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());
  6832. }
  6833. return promise;
  6834. };
  6835. Axios.prototype.getUri = function getUri(config) {
  6836. config = mergeConfig(this.defaults, config);
  6837. var fullPath = buildFullPath(config.baseURL, config.url);
  6838. return buildURL(fullPath, config.params, config.paramsSerializer);
  6839. };
  6840. // Provide aliases for supported request methods
  6841. utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  6842. /*eslint func-names:0*/
  6843. Axios.prototype[method] = function(url, config) {
  6844. return this.request(mergeConfig(config || {}, {
  6845. method: method,
  6846. url: url,
  6847. data: (config || {}).data
  6848. }));
  6849. };
  6850. });
  6851. utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  6852. /*eslint func-names:0*/
  6853. function generateHTTPMethod(isForm) {
  6854. return function httpMethod(url, data, config) {
  6855. return this.request(mergeConfig(config || {}, {
  6856. method: method,
  6857. headers: isForm ? {
  6858. 'Content-Type': 'multipart/form-data'
  6859. } : {},
  6860. url: url,
  6861. data: data
  6862. }));
  6863. };
  6864. }
  6865. Axios.prototype[method] = generateHTTPMethod();
  6866. Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
  6867. });
  6868. var Axios_1 = Axios;
  6869. /**
  6870. * A `CancelToken` is an object that can be used to request cancellation of an operation.
  6871. *
  6872. * @class
  6873. * @param {Function} executor The executor function.
  6874. */
  6875. function CancelToken(executor) {
  6876. if (typeof executor !== 'function') {
  6877. throw new TypeError('executor must be a function.');
  6878. }
  6879. var resolvePromise;
  6880. this.promise = new Promise(function promiseExecutor(resolve) {
  6881. resolvePromise = resolve;
  6882. });
  6883. var token = this;
  6884. // eslint-disable-next-line func-names
  6885. this.promise.then(function(cancel) {
  6886. if (!token._listeners) return;
  6887. var i;
  6888. var l = token._listeners.length;
  6889. for (i = 0; i < l; i++) {
  6890. token._listeners[i](cancel);
  6891. }
  6892. token._listeners = null;
  6893. });
  6894. // eslint-disable-next-line func-names
  6895. this.promise.then = function(onfulfilled) {
  6896. var _resolve;
  6897. // eslint-disable-next-line func-names
  6898. var promise = new Promise(function(resolve) {
  6899. token.subscribe(resolve);
  6900. _resolve = resolve;
  6901. }).then(onfulfilled);
  6902. promise.cancel = function reject() {
  6903. token.unsubscribe(_resolve);
  6904. };
  6905. return promise;
  6906. };
  6907. executor(function cancel(message) {
  6908. if (token.reason) {
  6909. // Cancellation has already been requested
  6910. return;
  6911. }
  6912. token.reason = new CanceledError_1(message);
  6913. resolvePromise(token.reason);
  6914. });
  6915. }
  6916. /**
  6917. * Throws a `CanceledError` if cancellation has been requested.
  6918. */
  6919. CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  6920. if (this.reason) {
  6921. throw this.reason;
  6922. }
  6923. };
  6924. /**
  6925. * Subscribe to the cancel signal
  6926. */
  6927. CancelToken.prototype.subscribe = function subscribe(listener) {
  6928. if (this.reason) {
  6929. listener(this.reason);
  6930. return;
  6931. }
  6932. if (this._listeners) {
  6933. this._listeners.push(listener);
  6934. } else {
  6935. this._listeners = [listener];
  6936. }
  6937. };
  6938. /**
  6939. * Unsubscribe from the cancel signal
  6940. */
  6941. CancelToken.prototype.unsubscribe = function unsubscribe(listener) {
  6942. if (!this._listeners) {
  6943. return;
  6944. }
  6945. var index = this._listeners.indexOf(listener);
  6946. if (index !== -1) {
  6947. this._listeners.splice(index, 1);
  6948. }
  6949. };
  6950. /**
  6951. * Returns an object that contains a new `CancelToken` and a function that, when called,
  6952. * cancels the `CancelToken`.
  6953. */
  6954. CancelToken.source = function source() {
  6955. var cancel;
  6956. var token = new CancelToken(function executor(c) {
  6957. cancel = c;
  6958. });
  6959. return {
  6960. token: token,
  6961. cancel: cancel
  6962. };
  6963. };
  6964. var CancelToken_1 = CancelToken;
  6965. /**
  6966. * Syntactic sugar for invoking a function and expanding an array for arguments.
  6967. *
  6968. * Common use case would be to use `Function.prototype.apply`.
  6969. *
  6970. * ```js
  6971. * function f(x, y, z) {}
  6972. * var args = [1, 2, 3];
  6973. * f.apply(null, args);
  6974. * ```
  6975. *
  6976. * With `spread` this example can be re-written.
  6977. *
  6978. * ```js
  6979. * spread(function(x, y, z) {})([1, 2, 3]);
  6980. * ```
  6981. *
  6982. * @param {Function} callback
  6983. * @returns {Function}
  6984. */
  6985. var spread = function spread(callback) {
  6986. return function wrap(arr) {
  6987. return callback.apply(null, arr);
  6988. };
  6989. };
  6990. /**
  6991. * Determines whether the payload is an error thrown by Axios
  6992. *
  6993. * @param {*} payload The value to test
  6994. * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
  6995. */
  6996. var isAxiosError = function isAxiosError(payload) {
  6997. return utils.isObject(payload) && (payload.isAxiosError === true);
  6998. };
  6999. /**
  7000. * Create an instance of Axios
  7001. *
  7002. * @param {Object} defaultConfig The default config for the instance
  7003. * @return {Axios} A new instance of Axios
  7004. */
  7005. function createInstance(defaultConfig) {
  7006. var context = new Axios_1(defaultConfig);
  7007. var instance = bind(Axios_1.prototype.request, context);
  7008. // Copy axios.prototype to instance
  7009. utils.extend(instance, Axios_1.prototype, context);
  7010. // Copy context to instance
  7011. utils.extend(instance, context);
  7012. // Factory for creating new instances
  7013. instance.create = function create(instanceConfig) {
  7014. return createInstance(mergeConfig(defaultConfig, instanceConfig));
  7015. };
  7016. return instance;
  7017. }
  7018. // Create the default instance to be exported
  7019. var axios$1 = createInstance(defaults_1);
  7020. // Expose Axios class to allow class inheritance
  7021. axios$1.Axios = Axios_1;
  7022. // Expose Cancel & CancelToken
  7023. axios$1.CanceledError = CanceledError_1;
  7024. axios$1.CancelToken = CancelToken_1;
  7025. axios$1.isCancel = isCancel;
  7026. axios$1.VERSION = data.version;
  7027. axios$1.toFormData = toFormData_1;
  7028. // Expose AxiosError class
  7029. axios$1.AxiosError = AxiosError_1;
  7030. // alias for CanceledError for backward compatibility
  7031. axios$1.Cancel = axios$1.CanceledError;
  7032. // Expose all/spread
  7033. axios$1.all = function all(promises) {
  7034. return Promise.all(promises);
  7035. };
  7036. axios$1.spread = spread;
  7037. // Expose isAxiosError
  7038. axios$1.isAxiosError = isAxiosError;
  7039. var axios_1 = axios$1;
  7040. // Allow use of default import syntax in TypeScript
  7041. var _default = axios$1;
  7042. axios_1.default = _default;
  7043. var axios = axios_1;
  7044. class RTCEndpoint extends Event$1 {
  7045. constructor(options) {
  7046. super('RTCPusherPlayer');
  7047. this.TAG = '[RTCPusherPlayer]';
  7048. let defaults = {
  7049. element: '',
  7050. // html video element
  7051. debug: false,
  7052. // if output debug log
  7053. zlmsdpUrl: '',
  7054. simulcast: false,
  7055. useCamera: true,
  7056. audioEnable: true,
  7057. videoEnable: true,
  7058. recvOnly: false,
  7059. resolution: {
  7060. w: 0,
  7061. h: 0
  7062. },
  7063. usedatachannel: false
  7064. };
  7065. this.options = Object.assign({}, defaults, options);
  7066. if (this.options.debug) {
  7067. setLogger();
  7068. }
  7069. this.e = {
  7070. onicecandidate: this._onIceCandidate.bind(this),
  7071. ontrack: this._onTrack.bind(this),
  7072. onicecandidateerror: this._onIceCandidateError.bind(this),
  7073. onconnectionstatechange: this._onconnectionstatechange.bind(this),
  7074. ondatachannelopen: this._onDataChannelOpen.bind(this),
  7075. ondatachannelmsg: this._onDataChannelMsg.bind(this),
  7076. ondatachannelerr: this._onDataChannelErr.bind(this),
  7077. ondatachannelclose: this._onDataChannelClose.bind(this)
  7078. };
  7079. this._remoteStream = null;
  7080. this._localStream = null;
  7081. this.pc = new RTCPeerConnection(null);
  7082. this.pc.onicecandidate = this.e.onicecandidate;
  7083. this.pc.onicecandidateerror = this.e.onicecandidateerror;
  7084. this.pc.ontrack = this.e.ontrack;
  7085. this.pc.onconnectionstatechange = this.e.onconnectionstatechange;
  7086. this.datachannel = null;
  7087. if (this.options.usedatachannel) {
  7088. this.datachannel = this.pc.createDataChannel('chat');
  7089. this.datachannel.onclose = this.e.ondatachannelclose;
  7090. this.datachannel.onerror = this.e.ondatachannelerr;
  7091. this.datachannel.onmessage = this.e.ondatachannelmsg;
  7092. this.datachannel.onopen = this.e.ondatachannelopen;
  7093. }
  7094. if (!this.options.recvOnly && (this.options.audioEnable || this.options.videoEnable)) this.start();else this.receive();
  7095. }
  7096. receive() {
  7097. const AudioTransceiverInit = {
  7098. direction: 'recvonly',
  7099. sendEncodings: []
  7100. };
  7101. const VideoTransceiverInit = {
  7102. direction: 'recvonly',
  7103. sendEncodings: []
  7104. };
  7105. if (this.options.videoEnable) {
  7106. this.pc.addTransceiver('video', VideoTransceiverInit);
  7107. }
  7108. if (this.options.audioEnable) {
  7109. this.pc.addTransceiver('audio', AudioTransceiverInit);
  7110. }
  7111. this.pc.createOffer().then(desc => {
  7112. log(this.TAG, 'offer:', desc.sdp);
  7113. this.pc.setLocalDescription(desc).then(() => {
  7114. axios({
  7115. method: 'post',
  7116. url: this.options.zlmsdpUrl,
  7117. responseType: 'json',
  7118. data: desc.sdp,
  7119. headers: {
  7120. 'Content-Type': 'text/plain;charset=utf-8'
  7121. }
  7122. }).then(response => {
  7123. let ret = response.data; //JSON.parse(response.data);
  7124. if (ret.code != 0) {
  7125. // mean failed for offer/anwser exchange
  7126. this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret);
  7127. return;
  7128. }
  7129. let anwser = {};
  7130. anwser.sdp = ret.sdp;
  7131. anwser.type = 'answer';
  7132. log(this.TAG, 'answer:', ret.sdp);
  7133. this.pc.setRemoteDescription(anwser).then(() => {
  7134. log(this.TAG, 'set remote sucess');
  7135. }).catch(e => {
  7136. error(this.TAG, e);
  7137. });
  7138. });
  7139. });
  7140. }).catch(e => {
  7141. error(this.TAG, e);
  7142. });
  7143. }
  7144. start() {
  7145. let videoConstraints = false;
  7146. let audioConstraints = false;
  7147. if (this.options.useCamera) {
  7148. if (this.options.videoEnable) videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA);
  7149. if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.MIC);
  7150. } else {
  7151. if (this.options.videoEnable) {
  7152. videoConstraints = new VideoTrackConstraints(VideoSourceInfo.SCREENCAST);
  7153. if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.SCREENCAST);
  7154. } else {
  7155. if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.MIC);else {
  7156. // error shared display media not only audio
  7157. error(this.TAG, 'error paramter');
  7158. }
  7159. }
  7160. }
  7161. if (this.options.resolution.w != 0 && this.options.resolution.h != 0 && typeof videoConstraints == 'object') {
  7162. videoConstraints.resolution = new Resolution(this.options.resolution.w, this.options.resolution.h);
  7163. }
  7164. MediaStreamFactory.createMediaStream(new StreamConstraints(audioConstraints, videoConstraints)).then(stream => {
  7165. this._localStream = stream;
  7166. this.dispatch(Events$1.WEBRTC_ON_LOCAL_STREAM, stream);
  7167. const AudioTransceiverInit = {
  7168. direction: 'sendrecv',
  7169. sendEncodings: []
  7170. };
  7171. const VideoTransceiverInit = {
  7172. direction: 'sendrecv',
  7173. sendEncodings: []
  7174. };
  7175. if (this.options.simulcast && stream.getVideoTracks().length > 0) {
  7176. VideoTransceiverInit.sendEncodings = [{
  7177. rid: 'h',
  7178. active: true,
  7179. maxBitrate: 1000000
  7180. }, {
  7181. rid: 'm',
  7182. active: true,
  7183. maxBitrate: 500000,
  7184. scaleResolutionDownBy: 2
  7185. }, {
  7186. rid: 'l',
  7187. active: true,
  7188. maxBitrate: 200000,
  7189. scaleResolutionDownBy: 4
  7190. }];
  7191. }
  7192. if (this.options.audioEnable) {
  7193. if (stream.getAudioTracks().length > 0) {
  7194. this.pc.addTransceiver(stream.getAudioTracks()[0], AudioTransceiverInit);
  7195. } else {
  7196. AudioTransceiverInit.direction = 'recvonly';
  7197. this.pc.addTransceiver('audio', AudioTransceiverInit);
  7198. }
  7199. }
  7200. if (this.options.videoEnable) {
  7201. if (stream.getVideoTracks().length > 0) {
  7202. this.pc.addTransceiver(stream.getVideoTracks()[0], VideoTransceiverInit);
  7203. } else {
  7204. VideoTransceiverInit.direction = 'recvonly';
  7205. this.pc.addTransceiver('video', VideoTransceiverInit);
  7206. }
  7207. }
  7208. /*
  7209. stream.getTracks().forEach((track,idx)=>{
  7210. debug.log(this.TAG,track);
  7211. this.pc.addTrack(track);
  7212. });
  7213. */
  7214. this.pc.createOffer().then(desc => {
  7215. log(this.TAG, 'offer:', desc.sdp);
  7216. this.pc.setLocalDescription(desc).then(() => {
  7217. axios({
  7218. method: 'post',
  7219. url: this.options.zlmsdpUrl,
  7220. responseType: 'json',
  7221. data: desc.sdp,
  7222. headers: {
  7223. 'Content-Type': 'text/plain;charset=utf-8'
  7224. }
  7225. }).then(response => {
  7226. let ret = response.data; //JSON.parse(response.data);
  7227. if (ret.code != 0) {
  7228. // mean failed for offer/anwser exchange
  7229. this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret);
  7230. return;
  7231. }
  7232. let anwser = {};
  7233. anwser.sdp = ret.sdp;
  7234. anwser.type = 'answer';
  7235. log(this.TAG, 'answer:', ret.sdp);
  7236. this.pc.setRemoteDescription(anwser).then(() => {
  7237. log(this.TAG, 'set remote sucess');
  7238. }).catch(e => {
  7239. error(this.TAG, e);
  7240. });
  7241. });
  7242. });
  7243. }).catch(e => {
  7244. error(this.TAG, e);
  7245. });
  7246. }).catch(e => {
  7247. this.dispatch(Events$1.CAPTURE_STREAM_FAILED); //debug.error(this.TAG,e);
  7248. }); //const offerOptions = {};
  7249. /*
  7250. if (typeof this.pc.addTransceiver === 'function') {
  7251. // |direction| seems not working on Safari.
  7252. this.pc.addTransceiver('audio', { direction: 'recvonly' });
  7253. this.pc.addTransceiver('video', { direction: 'recvonly' });
  7254. } else {
  7255. offerOptions.offerToReceiveAudio = true;
  7256. offerOptions.offerToReceiveVideo = true;
  7257. }
  7258. */
  7259. }
  7260. _onIceCandidate(event) {
  7261. if (event.candidate) {
  7262. log('Remote ICE candidate: \n ' + event.candidate.candidate); // Send the candidate to the remote peer
  7263. }
  7264. }
  7265. _onTrack(event) {
  7266. if (this.options.element && event.streams && event.streams.length > 0) {
  7267. this.options.element.srcObject = event.streams[0];
  7268. this._remoteStream = event.streams[0];
  7269. this.dispatch(Events$1.WEBRTC_ON_REMOTE_STREAMS, event);
  7270. } else {
  7271. error('element pararm is failed');
  7272. }
  7273. }
  7274. _onIceCandidateError(event) {
  7275. this.dispatch(Events$1.WEBRTC_ICE_CANDIDATE_ERROR, event);
  7276. }
  7277. _onconnectionstatechange(event) {
  7278. this.dispatch(Events$1.WEBRTC_ON_CONNECTION_STATE_CHANGE, this.pc.connectionState);
  7279. }
  7280. _onDataChannelOpen(event) {
  7281. log(this.TAG, 'ondatachannel open:', event);
  7282. this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_OPEN, event);
  7283. }
  7284. _onDataChannelMsg(event) {
  7285. log(this.TAG, 'ondatachannel msg:', event);
  7286. this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_MSG, event);
  7287. }
  7288. _onDataChannelErr(event) {
  7289. log(this.TAG, 'ondatachannel err:', event);
  7290. this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_ERR, event);
  7291. }
  7292. _onDataChannelClose(event) {
  7293. log(this.TAG, 'ondatachannel close:', event);
  7294. this.dispatch(Events$1.WEBRTC_ON_DATA_CHANNEL_CLOSE, event);
  7295. }
  7296. sendMsg(data) {
  7297. if (this.datachannel != null) {
  7298. this.datachannel.send(data);
  7299. } else {
  7300. error(this.TAG, 'data channel is null');
  7301. }
  7302. }
  7303. closeDataChannel() {
  7304. if (this.datachannel) {
  7305. this.datachannel.close();
  7306. this.datachannel = null;
  7307. }
  7308. }
  7309. close() {
  7310. this.closeDataChannel();
  7311. if (this.pc) {
  7312. this.pc.close();
  7313. this.pc = null;
  7314. }
  7315. if (this.options) {
  7316. this.options = null;
  7317. }
  7318. if (this._localStream) {
  7319. this._localStream.getTracks().forEach((track, idx) => {
  7320. track.stop();
  7321. });
  7322. }
  7323. if (this._remoteStream) {
  7324. this._remoteStream.getTracks().forEach((track, idx) => {
  7325. track.stop();
  7326. });
  7327. }
  7328. }
  7329. get remoteStream() {
  7330. return this._remoteStream;
  7331. }
  7332. get localStream() {
  7333. return this._localStream;
  7334. }
  7335. }
  7336. const quickScan = [{
  7337. 'label': '4K(UHD)',
  7338. 'width': 3840,
  7339. 'height': 2160
  7340. }, {
  7341. 'label': '1080p(FHD)',
  7342. 'width': 1920,
  7343. 'height': 1080
  7344. }, {
  7345. 'label': 'UXGA',
  7346. 'width': 1600,
  7347. 'height': 1200,
  7348. 'ratio': '4:3'
  7349. }, {
  7350. 'label': '720p(HD)',
  7351. 'width': 1280,
  7352. 'height': 720
  7353. }, {
  7354. 'label': 'SVGA',
  7355. 'width': 800,
  7356. 'height': 600
  7357. }, {
  7358. 'label': 'VGA',
  7359. 'width': 640,
  7360. 'height': 480
  7361. }, {
  7362. 'label': '360p(nHD)',
  7363. 'width': 640,
  7364. 'height': 360
  7365. }, {
  7366. 'label': 'CIF',
  7367. 'width': 352,
  7368. 'height': 288
  7369. }, {
  7370. 'label': 'QVGA',
  7371. 'width': 320,
  7372. 'height': 240
  7373. }, {
  7374. 'label': 'QCIF',
  7375. 'width': 176,
  7376. 'height': 144
  7377. }, {
  7378. 'label': 'QQVGA',
  7379. 'width': 160,
  7380. 'height': 120
  7381. }];
  7382. function GetSupportCameraResolutions$1() {
  7383. return new Promise(function (resolve, reject) {
  7384. let resolutions = [];
  7385. let ok = 0;
  7386. let err = 0;
  7387. for (let i = 0; i < quickScan.length; ++i) {
  7388. let videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA);
  7389. videoConstraints.resolution = new Resolution(quickScan[i].width, quickScan[i].height);
  7390. MediaStreamFactory.createMediaStream(new StreamConstraints(false, videoConstraints)).then(stream => {
  7391. resolutions.push(quickScan[i]);
  7392. ok++;
  7393. if (ok + err == quickScan.length) {
  7394. resolve(resolutions);
  7395. }
  7396. }).catch(e => {
  7397. err++;
  7398. if (ok + err == quickScan.length) {
  7399. resolve(resolutions);
  7400. }
  7401. });
  7402. }
  7403. });
  7404. }
  7405. function GetAllScanResolution$1() {
  7406. return quickScan;
  7407. }
  7408. function isSupportResolution$1(w, h) {
  7409. return new Promise(function (resolve, reject) {
  7410. let videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA);
  7411. videoConstraints.resolution = new Resolution(w, h);
  7412. MediaStreamFactory.createMediaStream(new StreamConstraints(false, videoConstraints)).then(stream => {
  7413. resolve();
  7414. }).catch(e => {
  7415. reject(e);
  7416. });
  7417. });
  7418. }
  7419. console.log('build date:', BUILD_DATE);
  7420. console.log('version:', VERSION$1);
  7421. const Events = Events$1;
  7422. const Media = media;
  7423. const Endpoint = RTCEndpoint;
  7424. const GetSupportCameraResolutions = GetSupportCameraResolutions$1;
  7425. const GetAllScanResolution = GetAllScanResolution$1;
  7426. const isSupportResolution = isSupportResolution$1;
  7427. exports.Endpoint = Endpoint;
  7428. exports.Events = Events;
  7429. exports.GetAllScanResolution = GetAllScanResolution;
  7430. exports.GetSupportCameraResolutions = GetSupportCameraResolutions;
  7431. exports.Media = Media;
  7432. exports.isSupportResolution = isSupportResolution;
  7433. Object.defineProperty(exports, '__esModule', { value: true });
  7434. return exports;
  7435. })({});
  7436. //# sourceMappingURL=ZLMRTCClient.js.map