PageSourceSearch

https://i-sng.ru/assets/js/lib.cdf75dc617a0141232b6.js

js i-sng.ru collected 2026-09-25 04:49:57 UTC 224,411 bytes, 5,622 lines download raw bytes

1(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{2:function(e,n,t){"use strict";var r,i=function(){return void 0===r&&(r=Boolean(window&&document&&document.all&&!window.atob)),r},o=function(){var e={};return function(n){if(void 0===e[n]){var t=document.querySelector(n);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}e[n]=t}return e[n]}}(),a=[];function c(e){for(var n=-1,t=0;t<a.length;t++)if(a[t].identifier===e){n=t;break}return n}function s(e,n){for(var t={},r=[],i=0;i<e.length;i++){var o=e[i],s=n.base?o[0]+n.base:o[0],u=t[s]||0,l="".concat(s," ").concat(u);t[s]=u+1;var d=c(l),f={css:o[1],media:o[2],sourceMap:o[3]};-1!==d?(a[d].references++,a[d].updater(f)):a.push({identifier:l,updater:m(f,n),references:1}),r.push(l)}return r}function u(e){var n=document.createElement("style"),r=e.attributes||{};if(void 0===r.nonce){var i=t.nc;i&&(r.nonce=i)}if(Object.keys(r).forEach((function(e){n.setAttribute(e,r[e])})),"function"==typeof e.insert)e.insert(n);else{var a=o(e.insert||"head");if(!a)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");a.appendChild(n)}return n}var l,d=(l=[],function(e,n){return l[e]=n,l.filter(Boolean).join("\n")});function f(e,n,t,r){var i=t?"":r.media?"@media ".concat(r.media," {").concat(r.css,"}"):r.css;if(e.styleSheet)e.styleSheet.cssText=d(n,i);else{var o=document.createTextNode(i),a=e.childNodes;a[n]&&e.removeChild(a[n]),a.length?e.insertBefore(o,a[n]):e.appendChild(o)}}function p(e,n,t){var r=t.css,i=t.media,o=t.sourceMap;if(i?e.setAttribute("media",i):e.removeAttribute("media"),o&&btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(o))))," */")),e.styleSheet)e.styleSheet.cssText=r;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(r))}}var v=null,h=0;function m(e,n){var t,r,i;if(n.singleton){var o=h++;t=v||(v=u(n)),r=f.bind(null,t,o,!1),i=f.bind(null,t,o,!0)}else t=u(n),r=p.bind(null,t,n),i=function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)};return r(e),function(n){if(n){if(n.css===e.css&&n.media===e.media&&n.sourceMap===e.sourceMap)return;r(e=n)}else i()}}e.exports=function(e,n){(n=n||{}).singleton||"boolean"==typeof n.singleton||(n.singleton=i());var t=s(e=e||[],n);return function(e){if(e=e||[],"[object Array]"===Object.prototype.toString.call(e)){for(var r=0;r<t.length;r++){var i=c(t[r]);a[i].references--}for(var o=s(e,n),u=0;u<t.length;u++){var l=c(t[u]);0===a[l].references&&(a[l].updater(),a.splice(l,1))}t=o}}}}}]);
2(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
3    /*
4     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
5     *
6     *  Use of this source code is governed by a BSD-style license
7     *  that can be found in the LICENSE file in the root of the source
8     *  tree.
9     */
10    /* eslint-env node */
11    
12    'use strict';
13    
14    var _adapter_factory = require('./adapter_factory.js');
15    
16    var adapter = (0, _adapter_factory.adapterFactory)({ window: window });
17    module.exports = adapter; // this is the difference from adapter_core.
18    
19    },{"./adapter_factory.js":2}],2:[function(require,module,exports){
20    'use strict';
21    
22    Object.defineProperty(exports, "__esModule", {
23      value: true
24    });
25    exports.adapterFactory = adapterFactory;
26    
27    var _utils = require('./utils');
28    
29    var utils = _interopRequireWildcard(_utils);
30    
31    var _chrome_shim = require('./chrome/chrome_shim');
32    
33    var chromeShim = _interopRequireWildcard(_chrome_shim);
34    
35    var _edge_shim = require('./edge/edge_shim');
36    
37    var edgeShim = _interopRequireWildcard(_edge_shim);
38    
39    var _firefox_shim = require('./firefox/firefox_shim');
40    
41    var firefoxShim = _interopRequireWildcard(_firefox_shim);
42    
43    var _safari_shim = require('./safari/safari_shim');
44    
45    var safariShim = _interopRequireWildcard(_safari_shim);
46    
47    var _common_shim = require('./common_shim');
48    
49    var commonShim = _interopRequireWildcard(_common_shim);
50    
51    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
52    
53    // Shimming starts here.
54    /*
55     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
56     *
57     *  Use of this source code is governed by a BSD-style license
58     *  that can be found in the LICENSE file in the root of the source
59     *  tree.
60     */
61    function adapterFactory() {
62      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
63          window = _ref.window;
64    
65      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
66        shimChrome: true,
67        shimFirefox: true,
68        shimEdge: true,
69        shimSafari: true
70      };
71    
72      // Utils.
73      var logging = utils.log;
74      var browserDetails = utils.detectBrowser(window);
75    
76      var adapter = {
77        browserDetails: browserDetails,
78        commonShim: commonShim,
79        extractVersion: utils.extractVersion,
80        disableLog: utils.disableLog,
81        disableWarnings: utils.disableWarnings
82      };
83    
84      // Shim browser if found.
85      switch (browserDetails.browser) {
86        case 'chrome':
87          if (!chromeShim || !chromeShim.shimPeerConnection || !options.shimChrome) {
88            logging('Chrome shim is not included in this adapter release.');
89            return adapter;
90          }
91          if (browserDetails.version === null) {
92            logging('Chrome shim can not determine version, not shimming.');
93            return adapter;
94          }
95          logging('adapter.js shimming chrome.');
96          // Export to the adapter global object visible in the browser.
97          adapter.browserShim = chromeShim;
98    
99          chromeShim.shimGetUserMedia(window);
100          chromeShim.shimMediaStream(window);
101          chromeShim.shimPeerConnection(window);
102          chromeShim.shimOnTrack(window);
103          chromeShim.shimAddTrackRemoveTrack(window);
104          chromeShim.shimGetSendersWithDtmf(window);
105          chromeShim.shimGetStats(window);
106          chromeShim.shimSenderReceiverGetStats(window);
107          chromeShim.fixNegotiationNeeded(window);
108    
109          commonShim.shimRTCIceCandidate(window);
110          commonShim.shimConnectionState(window);
111          commonShim.shimMaxMessageSize(window);
112          commonShim.shimSendThrowTypeError(window);
113          commonShim.removeAllowExtmapMixed(window);
114          break;
115        case 'firefox':
116          if (!firefoxShim || !firefoxShim.shimPeerConnection || !options.shimFirefox) {
117            logging('Firefox shim is not included in this adapter release.');
118            return adapter;
119          }
120          logging('adapter.js shimming firefox.');
121          // Export to the adapter global object visible in the browser.
122          adapter.browserShim = firefoxShim;
123    
124          firefoxShim.shimGetUserMedia(window);
125          firefoxShim.shimPeerConnection(window);
126          firefoxShim.shimOnTrack(window);
127          firefoxShim.shimRemoveStream(window);
128          firefoxShim.shimSenderGetStats(window);
129          firefoxShim.shimReceiverGetStats(window);
130          firefoxShim.shimRTCDataChannel(window);
131          firefoxShim.shimAddTransceiver(window);
132          firefoxShim.shimGetParameters(window);
133          firefoxShim.shimCreateOffer(window);
134          firefoxShim.shimCreateAnswer(window);
135    
136          commonShim.shimRTCIceCandidate(window);
137          commonShim.shimConnectionState(window);
138          commonShim.shimMaxMessageSize(window);
139          commonShim.shimSendThrowTypeError(window);
140          break;
141        case 'edge':
142          if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {
143            logging('MS edge shim is not included in this adapter release.');
144            return adapter;
145          }
146          logging('adapter.js shimming edge.');
147          // Export to the adapter global object visible in the browser.
148          adapter.browserShim = edgeShim;
149    
150          edgeShim.shimGetUserMedia(window);
151          edgeShim.shimGetDisplayMedia(window);
152          edgeShim.shimPeerConnection(window);
153          edgeShim.shimReplaceTrack(window);
154    
155          // the edge shim implements the full RTCIceCandidate object.
156    
157          commonShim.shimMaxMessageSize(window);
158          commonShim.shimSendThrowTypeError(window);
159          break;
160        case 'safari':
161          if (!safariShim || !options.shimSafari) {
162            logging('Safari shim is not included in this adapter release.');
163            return adapter;
164          }
165          logging('adapter.js shimming safari.');
166          // Export to the adapter global object visible in the browser.
167          adapter.browserShim = safariShim;
168    
169          safariShim.shimRTCIceServerUrls(window);
170          safariShim.shimCreateOfferLegacy(window);
171          safariShim.shimCallbacksAPI(window);
172          safariShim.shimLocalStreamsAPI(window);
173          safariShim.shimRemoteStreamsAPI(window);
174          safariShim.shimTrackEventTransceiver(window);
175          safariShim.shimGetUserMedia(window);
176          safariShim.shimAudioContext(window);
177    
178          commonShim.shimRTCIceCandidate(window);
179          commonShim.shimMaxMessageSize(window);
180          commonShim.shimSendThrowTypeError(window);
181          commonShim.removeAllowExtmapMixed(window);
182          break;
183        default:
184          logging('Unsupported browser!');
185          break;
186      }
187    
188      return adapter;
189    }
190    
191    // Browser shims.
192    
193    },{"./chrome/chrome_shim":3,"./common_shim":6,"./edge/edge_shim":7,"./firefox/firefox_shim":11,"./safari/safari_shim":14,"./utils":15}],3:[function(require,module,exports){
194    /*
195     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
196     *
197     *  Use of this source code is governed by a BSD-style license
198     *  that can be found in the LICENSE file in the root of the source
199     *  tree.
200     */
201    /* eslint-env node */
202    'use strict';
203    
204    Object.defineProperty(exports, "__esModule", {
205      value: true
206    });
207    exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
208    
209    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
210    
211    var _getusermedia = require('./getusermedia');
212    
213    Object.defineProperty(exports, 'shimGetUserMedia', {
214      enumerable: true,
215      get: function get() {
216        return _getusermedia.shimGetUserMedia;
217      }
218    });
219    
220    var _getdisplaymedia = require('./getdisplaymedia');
221    
222    Object.defineProperty(exports, 'shimGetDisplayMedia', {
223      enumerable: true,
224      get: function get() {
225        return _getdisplaymedia.shimGetDisplayMedia;
226      }
227    });
228    exports.shimMediaStream = shimMediaStream;
229    exports.shimOnTrack = shimOnTrack;
230    exports.shimGetSendersWithDtmf = shimGetSendersWithDtmf;
231    exports.shimGetStats = shimGetStats;
232    exports.shimSenderReceiverGetStats = shimSenderReceiverGetStats;
233    exports.shimAddTrackRemoveTrackWithNative = shimAddTrackRemoveTrackWithNative;
234    exports.shimAddTrackRemoveTrack = shimAddTrackRemoveTrack;
235    exports.shimPeerConnection = shimPeerConnection;
236    exports.fixNegotiationNeeded = fixNegotiationNeeded;
237    
238    var _utils = require('../utils.js');
239    
240    var utils = _interopRequireWildcard(_utils);
241    
242    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
243    
244    function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
245    
246    function shimMediaStream(window) {
247      window.MediaStream = window.MediaStream || window.webkitMediaStream;
248    }
249    
250    function shimOnTrack(window) {
251      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('ontrack' in window.RTCPeerConnection.prototype)) {
252        Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
253          get: function get() {
254            return this._ontrack;
255          },
256          set: function set(f) {
257            if (this._ontrack) {
258              this.removeEventListener('track', this._ontrack);
259            }
260            this.addEventListener('track', this._ontrack = f);
261          },
262    
263          enumerable: true,
264          configurable: true
265        });
266        var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
267        window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
268          var _this = this;
269    
270          if (!this._ontrackpoly) {
271            this._ontrackpoly = function (e) {
272              // onaddstream does not fire when a track is added to an existing
273              // stream. But stream.onaddtrack is implemented so we use that.
274              e.stream.addEventListener('addtrack', function (te) {
275                var receiver = void 0;
276                if (window.RTCPeerConnection.prototype.getReceivers) {
277                  receiver = _this.getReceivers().find(function (r) {
278                    return r.track && r.track.id === te.track.id;
279                  });
280                } else {
281                  receiver = { track: te.track };
282                }
283    
284                var event = new Event('track');
285                event.track = te.track;
286                event.receiver = receiver;
287                event.transceiver = { receiver: receiver };
288                event.streams = [e.stream];
289                _this.dispatchEvent(event);
290              });
vendor: 4,078 bytes, lines 291-387
291              e.stream.getTracks().forEach(function (track) {
292                var receiver = void 0;
293                if (window.RTCPeerConnection.prototype.getReceivers) {
294                  receiver = _this.getReceivers().find(function (r) {
295                    return r.track && r.track.id === track.id;
296                  });
297                } else {
298                  receiver = { track: track };
299                }
300                var event = new Event('track');
301                event.track = track;
302                event.receiver = receiver;
303                event.transceiver = { receiver: receiver };
304                event.streams = [e.stream];
305                _this.dispatchEvent(event);
306              });
307            };
308            this.addEventListener('addstream', this._ontrackpoly);
309          }
310          return origSetRemoteDescription.apply(this, arguments);
311        };
312      } else {
313        // even if RTCRtpTransceiver is in window, it is only used and
314        // emitted in unified-plan. Unfortunately this means we need
315        // to unconditionally wrap the event.
316        utils.wrapPeerConnectionEvent(window, 'track', function (e) {
317          if (!e.transceiver) {
318            Object.defineProperty(e, 'transceiver', { value: { receiver: e.receiver } });
319          }
320          return e;
321        });
322      }
323    }
324    
325    function shimGetSendersWithDtmf(window) {
326      // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
327      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && !('getSenders' in window.RTCPeerConnection.prototype) && 'createDTMFSender' in window.RTCPeerConnection.prototype) {
328        var shimSenderWithDtmf = function shimSenderWithDtmf(pc, track) {
329          return {
330            track: track,
331            get dtmf() {
332              if (this._dtmf === undefined) {
333                if (track.kind === 'audio') {
334                  this._dtmf = pc.createDTMFSender(track);
335                } else {
336                  this._dtmf = null;
337                }
338              }
339              return this._dtmf;
340            },
341            _pc: pc
342          };
343        };
344    
345        // augment addTrack when getSenders is not available.
346        if (!window.RTCPeerConnection.prototype.getSenders) {
347          window.RTCPeerConnection.prototype.getSenders = function getSenders() {
348            this._senders = this._senders || [];
349            return this._senders.slice(); // return a copy of the internal state.
350          };
351          var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
352          window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
353            var sender = origAddTrack.apply(this, arguments);
354            if (!sender) {
355              sender = shimSenderWithDtmf(this, track);
356              this._senders.push(sender);
357            }
358            return sender;
359          };
360    
361          var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
362          window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
363            origRemoveTrack.apply(this, arguments);
364            var idx = this._senders.indexOf(sender);
365            if (idx !== -1) {
366              this._senders.splice(idx, 1);
367            }
368          };
369        }
370        var origAddStream = window.RTCPeerConnection.prototype.addStream;
371        window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
372          var _this2 = this;
373    
374          this._senders = this._senders || [];
375          origAddStream.apply(this, [stream]);
376          stream.getTracks().forEach(function (track) {
377            _this2._senders.push(shimSenderWithDtmf(_this2, track));
378          });
379        };
380    
381        var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
382        window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
383          var _this3 = this;
384    
385          this._senders = this._senders || [];
386          origRemoveStream.apply(this, [stream]);
387    
388          stream.getTracks().forEach(function (track) {
389            var sender = _this3._senders.find(function (s) {
390              return s.track === track;
391            });
392            if (sender) {
393              // remove sender
394              _this3._senders.splice(_this3._senders.indexOf(sender), 1);
395            }
396          });
397        };
398      } else if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && 'getSenders' in window.RTCPeerConnection.prototype && 'createDTMFSender' in window.RTCPeerConnection.prototype && window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
399        var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
400        window.RTCPeerConnection.prototype.getSenders = function getSenders() {
401          var _this4 = this;
402    
403          var senders = origGetSenders.apply(this, []);
404          senders.forEach(function (sender) {
405            return sender._pc = _this4;
406          });
407          return senders;
408        };
409    
410        Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
411          get: function get() {
412            if (this._dtmf === undefined) {
413              if (this.track.kind === 'audio') {
414                this._dtmf = this._pc.createDTMFSender(this.track);
415              } else {
416                this._dtmf = null;
417              }
418            }
419            return this._dtmf;
420          }
421        });
422      }
423    }
424    
425    function shimGetStats(window) {
426      if (!window.RTCPeerConnection) {
427        return;
428      }
429    
430      var origGetStats = window.RTCPeerConnection.prototype.getStats;
431      window.RTCPeerConnection.prototype.getStats = function getStats() {
432        var _this5 = this;
433    
434        var _arguments = Array.prototype.slice.call(arguments),
435            selector = _arguments[0],
436            onSucc = _arguments[1],
437            onErr = _arguments[2];
438    
439        // If selector is a function then we are in the old style stats so just
440        // pass back the original getStats format to avoid breaking old users.
441    
442    
443        if (arguments.length > 0 && typeof selector === 'function') {
444          return origGetStats.apply(this, arguments);
445        }
446    
447        // When spec-style getStats is supported, return those when called with
448        // either no arguments or the selector argument is null.
449        if (origGetStats.length === 0 && (arguments.length === 0 || typeof selector !== 'function')) {
450          return origGetStats.apply(this, []);
451        }
452    
453        var fixChromeStats_ = function fixChromeStats_(response) {
454          var standardReport = {};
455          var reports = response.result();
456          reports.forEach(function (report) {
457            var standardStats = {
458              id: report.id,
459              timestamp: report.timestamp,
460              type: {
461                localcandidate: 'local-candidate',
462                remotecandidate: 'remote-candidate'
463              }[report.type] || report.type
464            };
465            report.names().forEach(function (name) {
466              standardStats[name] = report.stat(name);
467            });
468            standardReport[standardStats.id] = standardStats;
469          });
470    
471          return standardReport;
472        };
473    
474        // shim getStats with maplike support
475        var makeMapStats = function makeMapStats(stats) {
476          return new Map(Object.keys(stats).map(function (key) {
477            return [key, stats[key]];
478          }));
479        };
480    
481        if (arguments.length >= 2) {
482          var successCallbackWrapper_ = function successCallbackWrapper_(response) {
483            onSucc(makeMapStats(fixChromeStats_(response)));
484          };
485    
486          return origGetStats.apply(this, [successCallbackWrapper_, selector]);
487        }
488    
489        // promise-support
490        return new Promise(function (resolve, reject) {
491          origGetStats.apply(_this5, [function (response) {
492            resolve(makeMapStats(fixChromeStats_(response)));
493          }, reject]);
494        }).then(onSucc, onErr);
495      };
496    }
497    
498    function shimSenderReceiverGetStats(window) {
499      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender && window.RTCRtpReceiver)) {
500        return;
501      }
502    
503      // shim sender stats.
504      if (!('getStats' in window.RTCRtpSender.prototype)) {
505        var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
506        if (origGetSenders) {
507          window.RTCPeerConnection.prototype.getSenders = function getSenders() {
508            var _this6 = this;
509    
510            var senders = origGetSenders.apply(this, []);
vendor: 13,787 bytes, lines 511-846
511            senders.forEach(function (sender) {
512              return sender._pc = _this6;
513            });
514            return senders;
515          };
516        }
517    
518        var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
519        if (origAddTrack) {
520          window.RTCPeerConnection.prototype.addTrack = function addTrack() {
521            var sender = origAddTrack.apply(this, arguments);
522            sender._pc = this;
523            return sender;
524          };
525        }
526        window.RTCRtpSender.prototype.getStats = function getStats() {
527          var sender = this;
528          return this._pc.getStats().then(function (result) {
529            return (
530              /* Note: this will include stats of all senders that
531               *   send a track with the same id as sender.track as
532               *   it is not possible to identify the RTCRtpSender.
533               */
534              utils.filterStats(result, sender.track, true)
535            );
536          });
537        };
538      }
539    
540      // shim receiver stats.
541      if (!('getStats' in window.RTCRtpReceiver.prototype)) {
542        var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
543        if (origGetReceivers) {
544          window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
545            var _this7 = this;
546    
547            var receivers = origGetReceivers.apply(this, []);
548            receivers.forEach(function (receiver) {
549              return receiver._pc = _this7;
550            });
551            return receivers;
552          };
553        }
554        utils.wrapPeerConnectionEvent(window, 'track', function (e) {
555          e.receiver._pc = e.srcElement;
556          return e;
557        });
558        window.RTCRtpReceiver.prototype.getStats = function getStats() {
559          var receiver = this;
560          return this._pc.getStats().then(function (result) {
561            return utils.filterStats(result, receiver.track, false);
562          });
563        };
564      }
565    
566      if (!('getStats' in window.RTCRtpSender.prototype && 'getStats' in window.RTCRtpReceiver.prototype)) {
567        return;
568      }
569    
570      // shim RTCPeerConnection.getStats(track).
571      var origGetStats = window.RTCPeerConnection.prototype.getStats;
572      window.RTCPeerConnection.prototype.getStats = function getStats() {
573        if (arguments.length > 0 && arguments[0] instanceof window.MediaStreamTrack) {
574          var track = arguments[0];
575          var sender = void 0;
576          var receiver = void 0;
577          var err = void 0;
578          this.getSenders().forEach(function (s) {
579            if (s.track === track) {
580              if (sender) {
581                err = true;
582              } else {
583                sender = s;
584              }
585            }
586          });
587          this.getReceivers().forEach(function (r) {
588            if (r.track === track) {
589              if (receiver) {
590                err = true;
591              } else {
592                receiver = r;
593              }
594            }
595            return r.track === track;
596          });
597          if (err || sender && receiver) {
598            return Promise.reject(new DOMException('There are more than one sender or receiver for the track.', 'InvalidAccessError'));
599          } else if (sender) {
600            return sender.getStats();
601          } else if (receiver) {
602            return receiver.getStats();
603          }
604          return Promise.reject(new DOMException('There is no sender or receiver for the track.', 'InvalidAccessError'));
605        }
606        return origGetStats.apply(this, arguments);
607      };
608    }
609    
610    function shimAddTrackRemoveTrackWithNative(window) {
611      // shim addTrack/removeTrack with native variants in order to make
612      // the interactions with legacy getLocalStreams behave as in other browsers.
613      // Keeps a mapping stream.id => [stream, rtpsenders...]
614      window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
615        var _this8 = this;
616    
617        this._shimmedLocalStreams = this._shimmedLocalStreams || {};
618        return Object.keys(this._shimmedLocalStreams).map(function (streamId) {
619          return _this8._shimmedLocalStreams[streamId][0];
620        });
621      };
622    
623      var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
624      window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
625        if (!stream) {
626          return origAddTrack.apply(this, arguments);
627        }
628        this._shimmedLocalStreams = this._shimmedLocalStreams || {};
629    
630        var sender = origAddTrack.apply(this, arguments);
631        if (!this._shimmedLocalStreams[stream.id]) {
632          this._shimmedLocalStreams[stream.id] = [stream, sender];
633        } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
634          this._shimmedLocalStreams[stream.id].push(sender);
635        }
636        return sender;
637      };
638    
639      var origAddStream = window.RTCPeerConnection.prototype.addStream;
640      window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
641        var _this9 = this;
642    
643        this._shimmedLocalStreams = this._shimmedLocalStreams || {};
644    
645        stream.getTracks().forEach(function (track) {
646          var alreadyExists = _this9.getSenders().find(function (s) {
647            return s.track === track;
648          });
649          if (alreadyExists) {
650            throw new DOMException('Track already exists.', 'InvalidAccessError');
651          }
652        });
653        var existingSenders = this.getSenders();
654        origAddStream.apply(this, arguments);
655        var newSenders = this.getSenders().filter(function (newSender) {
656          return existingSenders.indexOf(newSender) === -1;
657        });
658        this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
659      };
660    
661      var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
662      window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
663        this._shimmedLocalStreams = this._shimmedLocalStreams || {};
664        delete this._shimmedLocalStreams[stream.id];
665        return origRemoveStream.apply(this, arguments);
666      };
667    
668      var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
669      window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
670        var _this10 = this;
671    
672        this._shimmedLocalStreams = this._shimmedLocalStreams || {};
673        if (sender) {
674          Object.keys(this._shimmedLocalStreams).forEach(function (streamId) {
675            var idx = _this10._shimmedLocalStreams[streamId].indexOf(sender);
676            if (idx !== -1) {
677              _this10._shimmedLocalStreams[streamId].splice(idx, 1);
678            }
679            if (_this10._shimmedLocalStreams[streamId].length === 1) {
680              delete _this10._shimmedLocalStreams[streamId];
681            }
682          });
683        }
684        return origRemoveTrack.apply(this, arguments);
685      };
686    }
687    
688    function shimAddTrackRemoveTrack(window) {
689      if (!window.RTCPeerConnection) {
690        return;
691      }
692      var browserDetails = utils.detectBrowser(window);
693      // shim addTrack and removeTrack.
694      if (window.RTCPeerConnection.prototype.addTrack && browserDetails.version >= 65) {
695        return shimAddTrackRemoveTrackWithNative(window);
696      }
697    
698      // also shim pc.getLocalStreams when addTrack is shimmed
699      // to return the original streams.
700      var origGetLocalStreams = window.RTCPeerConnection.prototype.getLocalStreams;
701      window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
702        var _this11 = this;
703    
704        var nativeStreams = origGetLocalStreams.apply(this);
705        this._reverseStreams = this._reverseStreams || {};
706        return nativeStreams.map(function (stream) {
707          return _this11._reverseStreams[stream.id];
708        });
709      };
710    
711      var origAddStream = window.RTCPeerConnection.prototype.addStream;
712      window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
713        var _this12 = this;
714    
715        this._streams = this._streams || {};
716        this._reverseStreams = this._reverseStreams || {};
717    
718        stream.getTracks().forEach(function (track) {
719          var alreadyExists = _this12.getSenders().find(function (s) {
720            return s.track === track;
721          });
722          if (alreadyExists) {
723            throw new DOMException('Track already exists.', 'InvalidAccessError');
724          }
725        });
726        // Add identity mapping for consistency with addTrack.
727        // Unless this is being used with a stream from addTrack.
728        if (!this._reverseStreams[stream.id]) {
729          var newStream = new window.MediaStream(stream.getTracks());
730          this._streams[stream.id] = newStream;
731          this._reverseStreams[newStream.id] = stream;
732          stream = newStream;
733        }
734        origAddStream.apply(this, [stream]);
735      };
736    
737      var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
738      window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
739        this._streams = this._streams || {};
740        this._reverseStreams = this._reverseStreams || {};
741    
742        origRemoveStream.apply(this, [this._streams[stream.id] || stream]);
743        delete this._reverseStreams[this._streams[stream.id] ? this._streams[stream.id].id : stream.id];
744        delete this._streams[stream.id];
745      };
746    
747      window.RTCPeerConnection.prototype.addTrack = function addTrack(track, stream) {
748        var _this13 = this;
749    
750        if (this.signalingState === 'closed') {
751          throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
752        }
753        var streams = [].slice.call(arguments, 1);
754        if (streams.length !== 1 || !streams[0].getTracks().find(function (t) {
755          return t === track;
756        })) {
757          // this is not fully correct but all we can manage without
758          // [[associated MediaStreams]] internal slot.
759          throw new DOMException('The adapter.js addTrack polyfill only supports a single ' + ' stream which is associated with the specified track.', 'NotSupportedError');
760        }
761    
762        var alreadyExists = this.getSenders().find(function (s) {
763          return s.track === track;
764        });
765        if (alreadyExists) {
766          throw new DOMException('Track already exists.', 'InvalidAccessError');
767        }
768    
769        this._streams = this._streams || {};
770        this._reverseStreams = this._reverseStreams || {};
771        var oldStream = this._streams[stream.id];
772        if (oldStream) {
773          // this is using odd Chrome behaviour, use with caution:
774          // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
775          // Note: we rely on the high-level addTrack/dtmf shim to
776          // create the sender with a dtmf sender.
777          oldStream.addTrack(track);
778    
779          // Trigger ONN async.
780          Promise.resolve().then(function () {
781            _this13.dispatchEvent(new Event('negotiationneeded'));
782          });
783        } else {
784          var newStream = new window.MediaStream([track]);
785          this._streams[stream.id] = newStream;
786          this._reverseStreams[newStream.id] = stream;
787          this.addStream(newStream);
788        }
789        return this.getSenders().find(function (s) {
790          return s.track === track;
791        });
792      };
793    
794      // replace the internal stream id with the external one and
795      // vice versa.
796      function replaceInternalStreamId(pc, description) {
797        var sdp = description.sdp;
798        Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
799          var externalStream = pc._reverseStreams[internalId];
800          var internalStream = pc._streams[externalStream.id];
801          sdp = sdp.replace(new RegExp(internalStream.id, 'g'), externalStream.id);
802        });
803        return new RTCSessionDescription({
804          type: description.type,
805          sdp: sdp
806        });
807      }
808      function replaceExternalStreamId(pc, description) {
809        var sdp = description.sdp;
810        Object.keys(pc._reverseStreams || []).forEach(function (internalId) {
811          var externalStream = pc._reverseStreams[internalId];
812          var internalStream = pc._streams[externalStream.id];
813          sdp = sdp.replace(new RegExp(externalStream.id, 'g'), internalStream.id);
814        });
815        return new RTCSessionDescription({
816          type: description.type,
817          sdp: sdp
818        });
819      }
820      ['createOffer', 'createAnswer'].forEach(function (method) {
821        var nativeMethod = window.RTCPeerConnection.prototype[method];
822        var methodObj = _defineProperty({}, method, function () {
823          var _this14 = this;
824    
825          var args = arguments;
826          var isLegacyCall = arguments.length && typeof arguments[0] === 'function';
827          if (isLegacyCall) {
828            return nativeMethod.apply(this, [function (description) {
829              var desc = replaceInternalStreamId(_this14, description);
830              args[0].apply(null, [desc]);
831            }, function (err) {
832              if (args[1]) {
833                args[1].apply(null, err);
834              }
835            }, arguments[2]]);
836          }
837          return nativeMethod.apply(this, arguments).then(function (description) {
838            return replaceInternalStreamId(_this14, description);
839          });
840        });
841        window.RTCPeerConnection.prototype[method] = methodObj[method];
842      });
843    
844      var origSetLocalDescription = window.RTCPeerConnection.prototype.setLocalDescription;
845      window.RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription() {
846        if (!arguments.length || !arguments[0].type) {
847          return origSetLocalDescription.apply(this, arguments);
848        }
849        arguments[0] = replaceExternalStreamId(this, arguments[0]);
850        return origSetLocalDescription.apply(this, arguments);
851      };
852    
853      // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
854    
855      var origLocalDescription = Object.getOwnPropertyDescriptor(window.RTCPeerConnection.prototype, 'localDescription');
856      Object.defineProperty(window.RTCPeerConnection.prototype, 'localDescription', {
857        get: function get() {
858          var description = origLocalDescription.get.apply(this);
859          if (description.type === '') {
860            return description;
861          }
862          return replaceInternalStreamId(this, description);
863        }
864      });
865    
866      window.RTCPeerConnection.prototype.removeTrack = function removeTrack(sender) {
867        var _this15 = this;
868    
869        if (this.signalingState === 'closed') {
870          throw new DOMException('The RTCPeerConnection\'s signalingState is \'closed\'.', 'InvalidStateError');
871        }
872        // We can not yet check for sender instanceof RTCRtpSender
873        // since we shim RTPSender. So we check if sender._pc is set.
874        if (!sender._pc) {
875          throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' + 'does not implement interface RTCRtpSender.', 'TypeError');
876        }
877        var isLocal = sender._pc === this;
878        if (!isLocal) {
879          throw new DOMException('Sender was not created by this connection.', 'InvalidAccessError');
880        }
881    
882        // Search for the native stream the senders track belongs to.
883        this._streams = this._streams || {};
884        var stream = void 0;
885        Object.keys(this._streams).forEach(function (streamid) {
886          var hasTrack = _this15._streams[streamid].getTracks().find(function (track) {
887            return sender.track === track;
888          });
889          if (hasTrack) {
890            stream = _this15._streams[streamid];
891          }
892        });
893    
894        if (stream) {
895          if (stream.getTracks().length === 1) {
896            // if this is the last track of the stream, remove the stream. This
897            // takes care of any shimmed _senders.
898            this.removeStream(this._reverseStreams[stream.id]);
899          } else {
900            // relying on the same odd chrome behaviour as above.
901            stream.removeTrack(sender.track);
902          }
903          this.dispatchEvent(new Event('negotiationneeded'));
904        }
905      };
906    }
907    
908    function shimPeerConnection(window) {
909      var browserDetails = utils.detectBrowser(window);
910    
911      if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
912        // very basic support for old versions.
913        window.RTCPeerConnection = window.webkitRTCPeerConnection;
914      }
915      if (!window.RTCPeerConnection) {
916        return;
917      }
918    
919      var addIceCandidateNullSupported = window.RTCPeerConnection.prototype.addIceCandidate.length === 0;
920    
921      // shim implicit creation of RTCSessionDescription/RTCIceCandidate
922      if (browserDetails.version < 53) {
923        ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
924          var nativeMethod = window.RTCPeerConnection.prototype[method];
925          var methodObj = _defineProperty({}, method, function () {
926            arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
927            return nativeMethod.apply(this, arguments);
928          });
929          window.RTCPeerConnection.prototype[method] = methodObj[method];
930        });
931      }
932    
933      // support for addIceCandidate(null or undefined)
934      var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
935      window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() {
936        if (!addIceCandidateNullSupported && !arguments[0]) {
937          if (arguments[1]) {
938            arguments[1].apply(null);
939          }
940          return Promise.resolve();
941        }
942        // Firefox 68+ emits and processes {candidate: "", ...}, ignore
943        // in older versions. Native support planned for Chrome M77.
944        if (browserDetails.version < 78 && arguments[0] && arguments[0].candidate === '') {
945          return Promise.resolve();
946        }
947        return nativeAddIceCandidate.apply(this, arguments);
948      };
949    }
950    
951    // Attempt to fix ONN in plan-b mode.
952    function fixNegotiationNeeded(window) {
953      var browserDetails = utils.detectBrowser(window);
954      utils.wrapPeerConnectionEvent(window, 'negotiationneeded', function (e) {
955        var pc = e.target;
956        if (browserDetails.version < 72 || pc.getConfiguration && pc.getConfiguration().sdpSemantics === 'plan-b') {
957          if (pc.signalingState !== 'stable') {
958            return;
959          }
960        }
961        return e;
962      });
963    }
964    
965    },{"../utils.js":15,"./getdisplaymedia":4,"./getusermedia":5}],4:[function(require,module,exports){
966    /*
967     *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
968     *
969     *  Use of this source code is governed by a BSD-style license
970     *  that can be found in the LICENSE file in the root of the source
971     *  tree.
972     */
973    /* eslint-env node */
974    'use strict';
975    
976    Object.defineProperty(exports, "__esModule", {
977      value: true
978    });
979    exports.shimGetDisplayMedia = shimGetDisplayMedia;
980    function shimGetDisplayMedia(window, getSourceId) {
981      if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
982        return;
983      }
984      if (!window.navigator.mediaDevices) {
985        return;
986      }
987      // getSourceId is a function that returns a promise resolving with
988      // the sourceId of the screen/window/tab to be shared.
989      if (typeof getSourceId !== 'function') {
990        console.error('shimGetDisplayMedia: getSourceId argument is not ' + 'a function');
991        return;
992      }
993      window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
994        return getSourceId(constraints).then(function (sourceId) {
995          var widthSpecified = constraints.video && constraints.video.width;
996          var heightSpecified = constraints.video && constraints.video.height;
997          var frameRateSpecified = constraints.video && constraints.video.frameRate;
998          constraints.video = {
999            mandatory: {
1000              chromeMediaSource: 'desktop',
1001              chromeMediaSourceId: sourceId,
1002              maxFrameRate: frameRateSpecified || 3
1003            }
1004          };
1005          if (widthSpecified) {
1006            constraints.video.mandatory.maxWidth = widthSpecified;
1007          }
1008          if (heightSpecified) {
1009            constraints.video.mandatory.maxHeight = heightSpecified;
1010          }
1011          return window.navigator.mediaDevices.getUserMedia(constraints);
1012        });
1013      };
1014    }
1015    
1016    },{}],5:[function(require,module,exports){
1017    /*
1018     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1019     *
1020     *  Use of this source code is governed by a BSD-style license
1021     *  that can be found in the LICENSE file in the root of the source
1022     *  tree.
1023     */
1024    /* eslint-env node */
1025    'use strict';
1026    
1027    Object.defineProperty(exports, "__esModule", {
1028      value: true
1029    });
1030    
1031    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
1032    
1033    exports.shimGetUserMedia = shimGetUserMedia;
1034    
1035    var _utils = require('../utils.js');
1036    
1037    var utils = _interopRequireWildcard(_utils);
1038    
1039    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
1040    
1041    var logging = utils.log;
1042    
1043    function shimGetUserMedia(window) {
1044      var navigator = window && window.navigator;
1045    
1046      if (!navigator.mediaDevices) {
1047        return;
1048      }
1049    
1050      var browserDetails = utils.detectBrowser(window);
1051    
1052      var constraintsToChrome_ = function constraintsToChrome_(c) {
1053        if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) !== 'object' || c.mandatory || c.optional) {
1054          return c;
1055        }
1056        var cc = {};
1057        Object.keys(c).forEach(function (key) {
1058          if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
1059            return;
1060          }
1061          var r = _typeof(c[key]) === 'object' ? c[key] : { ideal: c[key] };
1062          if (r.exact !== undefined && typeof r.exact === 'number') {
1063            r.min = r.max = r.exact;
1064          }
1065          var oldname_ = function oldname_(prefix, name) {
1066            if (prefix) {
1067              return prefix + name.charAt(0).toUpperCase() + name.slice(1);
1068            }
1069            return name === 'deviceId' ? 'sourceId' : name;
1070          };
1071          if (r.ideal !== undefined) {
1072            cc.optional = cc.optional || [];
1073            var oc = {};
1074            if (typeof r.ideal === 'number') {
1075              oc[oldname_('min', key)] = r.ideal;
1076              cc.optional.push(oc);
1077              oc = {};
1078              oc[oldname_('max', key)] = r.ideal;
1079              cc.optional.push(oc);
1080            } else {
1081              oc[oldname_('', key)] = r.ideal;
1082              cc.optional.push(oc);
1083            }
1084          }
1085          if (r.exact !== undefined && typeof r.exact !== 'number') {
1086            cc.mandatory = cc.mandatory || {};
1087            cc.mandatory[oldname_('', key)] = r.exact;
1088          } else {
1089            ['min', 'max'].forEach(function (mix) {
1090              if (r[mix] !== undefined) {
1091                cc.mandatory = cc.mandatory || {};
1092                cc.mandatory[oldname_(mix, key)] = r[mix];
1093              }
1094            });
1095          }
1096        });
1097        if (c.advanced) {
1098          cc.optional = (cc.optional || []).concat(c.advanced);
1099        }
1100        return cc;
1101      };
1102    
1103      var shimConstraints_ = function shimConstraints_(constraints, func) {
1104        if (browserDetails.version >= 61) {
1105          return func(constraints);
1106        }
1107        constraints = JSON.parse(JSON.stringify(constraints));
1108        if (constraints && _typeof(constraints.audio) === 'object') {
1109          var remap = function remap(obj, a, b) {
1110            if (a in obj && !(b in obj)) {
1111              obj[b] = obj[a];
1112              delete obj[a];
1113            }
1114          };
1115          constraints = JSON.parse(JSON.stringify(constraints));
1116          remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
1117          remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
1118          constraints.audio = constraintsToChrome_(constraints.audio);
1119        }
1120        if (constraints && _typeof(constraints.video) === 'object') {
1121          // Shim facingMode for mobile & surface pro.
1122          var face = constraints.video.facingMode;
1123          face = face && ((typeof face === 'undefined' ? 'undefined' : _typeof(face)) === 'object' ? face : { ideal: face });
1124          var getSupportedFacingModeLies = browserDetails.version < 66;
1125    
1126          if (face && (face.exact === 'user' || face.exact === 'environment' || face.ideal === 'user' || face.ideal === 'environment') && !(navigator.mediaDevices.getSupportedConstraints && navigator.mediaDevices.getSupportedConstraints().facingMode && !getSupportedFacingModeLies)) {
1127            delete constraints.video.facingMode;
1128            var matches = void 0;
1129            if (face.exact === 'environment' || face.ideal === 'environment') {
1130              matches = ['back', 'rear'];
1131            } else if (face.exact === 'user' || face.ideal === 'user') {
1132              matches = ['front'];
1133            }
1134            if (matches) {
1135              // Look for matches in label, or use last cam for back (typical).
vendor: 3,608 bytes, lines 1136-1224
1136              return navigator.mediaDevices.enumerateDevices().then(function (devices) {
1137                devices = devices.filter(function (d) {
1138                  return d.kind === 'videoinput';
1139                });
1140                var dev = devices.find(function (d) {
1141                  return matches.some(function (match) {
1142                    return d.label.toLowerCase().includes(match);
1143                  });
1144                });
1145                if (!dev && devices.length && matches.includes('back')) {
1146                  dev = devices[devices.length - 1]; // more likely the back cam
1147                }
1148                if (dev) {
1149                  constraints.video.deviceId = face.exact ? { exact: dev.deviceId } : { ideal: dev.deviceId };
1150                }
1151                constraints.video = constraintsToChrome_(constraints.video);
1152                logging('chrome: ' + JSON.stringify(constraints));
1153                return func(constraints);
1154              });
1155            }
1156          }
1157          constraints.video = constraintsToChrome_(constraints.video);
1158        }
1159        logging('chrome: ' + JSON.stringify(constraints));
1160        return func(constraints);
1161      };
1162    
1163      var shimError_ = function shimError_(e) {
1164        if (browserDetails.version >= 64) {
1165          return e;
1166        }
1167        return {
1168          name: {
1169            PermissionDeniedError: 'NotAllowedError',
1170            PermissionDismissedError: 'NotAllowedError',
1171            InvalidStateError: 'NotAllowedError',
1172            DevicesNotFoundError: 'NotFoundError',
1173            ConstraintNotSatisfiedError: 'OverconstrainedError',
1174            TrackStartError: 'NotReadableError',
1175            MediaDeviceFailedDueToShutdown: 'NotAllowedError',
1176            MediaDeviceKillSwitchOn: 'NotAllowedError',
1177            TabCaptureError: 'AbortError',
1178            ScreenCaptureError: 'AbortError',
1179            DeviceCaptureError: 'AbortError'
1180          }[e.name] || e.name,
1181          message: e.message,
1182          constraint: e.constraint || e.constraintName,
1183          toString: function toString() {
1184            return this.name + (this.message && ': ') + this.message;
1185          }
1186        };
1187      };
1188    
1189      var getUserMedia_ = function getUserMedia_(constraints, onSuccess, onError) {
1190        shimConstraints_(constraints, function (c) {
1191          navigator.webkitGetUserMedia(c, onSuccess, function (e) {
1192            if (onError) {
1193              onError(shimError_(e));
1194            }
1195          });
1196        });
1197      };
1198      navigator.getUserMedia = getUserMedia_.bind(navigator);
1199    
1200      // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
1201      // function which returns a Promise, it does not accept spec-style
1202      // constraints.
1203      if (navigator.mediaDevices.getUserMedia) {
1204        var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
1205        navigator.mediaDevices.getUserMedia = function (cs) {
1206          return shimConstraints_(cs, function (c) {
1207            return origGetUserMedia(c).then(function (stream) {
1208              if (c.audio && !stream.getAudioTracks().length || c.video && !stream.getVideoTracks().length) {
1209                stream.getTracks().forEach(function (track) {
1210                  track.stop();
1211                });
1212                throw new DOMException('', 'NotFoundError');
1213              }
1214              return stream;
1215            }, function (e) {
1216              return Promise.reject(shimError_(e));
1217            });
1218          });
1219        };
1220      }
1221    }
1222    
1223    },{"../utils.js":15}],6:[function(require,module,exports){
1224    /*
1225     *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
1226     *
1227     *  Use of this source code is governed by a BSD-style license
1228     *  that can be found in the LICENSE file in the root of the source
1229     *  tree.
1230     */
1231    /* eslint-env node */
1232    'use strict';
1233    
1234    Object.defineProperty(exports, "__esModule", {
1235      value: true
1236    });
1237    
1238    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
1239    
1240    exports.shimRTCIceCandidate = shimRTCIceCandidate;
1241    exports.shimMaxMessageSize = shimMaxMessageSize;
1242    exports.shimSendThrowTypeError = shimSendThrowTypeError;
1243    exports.shimConnectionState = shimConnectionState;
1244    exports.removeAllowExtmapMixed = removeAllowExtmapMixed;
1245    
1246    var _sdp = require('sdp');
1247    
1248    var _sdp2 = _interopRequireDefault(_sdp);
1249    
1250    var _utils = require('./utils');
1251    
1252    var utils = _interopRequireWildcard(_utils);
1253    
1254    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
1255    
1256    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1257    
1258    function shimRTCIceCandidate(window) {
1259      // foundation is arbitrarily chosen as an indicator for full support for
1260      // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
1261      if (!window.RTCIceCandidate || window.RTCIceCandidate && 'foundation' in window.RTCIceCandidate.prototype) {
1262        return;
1263      }
1264    
1265      var NativeRTCIceCandidate = window.RTCIceCandidate;
1266      window.RTCIceCandidate = function RTCIceCandidate(args) {
1267        // Remove the a= which shouldn't be part of the candidate string.
1268        if ((typeof args === 'undefined' ? 'undefined' : _typeof(args)) === 'object' && args.candidate && args.candidate.indexOf('a=') === 0) {
1269          args = JSON.parse(JSON.stringify(args));
1270          args.candidate = args.candidate.substr(2);
1271        }
1272    
1273        if (args.candidate && args.candidate.length) {
1274          // Augment the native candidate with the parsed fields.
1275          var nativeCandidate = new NativeRTCIceCandidate(args);
1276          var parsedCandidate = _sdp2.default.parseCandidate(args.candidate);
1277          var augmentedCandidate = Object.assign(nativeCandidate, parsedCandidate);
1278    
1279          // Add a serializer that does not serialize the extra attributes.
1280          augmentedCandidate.toJSON = function toJSON() {
1281            return {
1282              candidate: augmentedCandidate.candidate,
1283              sdpMid: augmentedCandidate.sdpMid,
1284              sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
1285              usernameFragment: augmentedCandidate.usernameFragment
1286            };
1287          };
1288          return augmentedCandidate;
1289        }
1290        return new NativeRTCIceCandidate(args);
1291      };
1292      window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
1293    
1294      // Hook up the augmented candidate in onicecandidate and
1295      // addEventListener('icecandidate', ...)
1296      utils.wrapPeerConnectionEvent(window, 'icecandidate', function (e) {
1297        if (e.candidate) {
1298          Object.defineProperty(e, 'candidate', {
1299            value: new window.RTCIceCandidate(e.candidate),
1300            writable: 'false'
1301          });
1302        }
1303        return e;
1304      });
1305    }
1306    
1307    function shimMaxMessageSize(window) {
1308      if (!window.RTCPeerConnection) {
1309        return;
1310      }
1311      var browserDetails = utils.detectBrowser(window);
1312    
1313      if (!('sctp' in window.RTCPeerConnection.prototype)) {
1314        Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
1315          get: function get() {
1316            return typeof this._sctp === 'undefined' ? null : this._sctp;
1317          }
1318        });
1319      }
1320    
1321      var sctpInDescription = function sctpInDescription(description) {
1322        if (!description || !description.sdp) {
1323          return false;
vendor: 4,663 bytes, lines 1324-1425
1324        }
1325        var sections = _sdp2.default.splitSections(description.sdp);
1326        sections.shift();
1327        return sections.some(function (mediaSection) {
1328          var mLine = _sdp2.default.parseMLine(mediaSection);
1329          return mLine && mLine.kind === 'application' && mLine.protocol.indexOf('SCTP') !== -1;
1330        });
1331      };
1332    
1333      var getRemoteFirefoxVersion = function getRemoteFirefoxVersion(description) {
1334        // TODO: Is there a better solution for detecting Firefox?
1335        var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
1336        if (match === null || match.length < 2) {
1337          return -1;
1338        }
1339        var version = parseInt(match[1], 10);
1340        // Test for NaN (yes, this is ugly)
1341        return version !== version ? -1 : version;
1342      };
1343    
1344      var getCanSendMaxMessageSize = function getCanSendMaxMessageSize(remoteIsFirefox) {
1345        // Every implementation we know can send at least 64 KiB.
1346        // Note: Although Chrome is technically able to send up to 256 KiB, the
1347        //       data does not reach the other peer reliably.
1348        //       See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
1349        var canSendMaxMessageSize = 65536;
1350        if (browserDetails.browser === 'firefox') {
1351          if (browserDetails.version < 57) {
1352            if (remoteIsFirefox === -1) {
1353              // FF < 57 will send in 16 KiB chunks using the deprecated PPID
1354              // fragmentation.
1355              canSendMaxMessageSize = 16384;
1356            } else {
1357              // However, other FF (and RAWRTC) can reassemble PPID-fragmented
1358              // messages. Thus, supporting ~2 GiB when sending.
1359              canSendMaxMessageSize = 2147483637;
1360            }
1361          } else if (browserDetails.version < 60) {
1362            // Currently, all FF >= 57 will reset the remote maximum message size
1363            // to the default value when a data channel is created at a later
1364            // stage. :(
1365            // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
1366            canSendMaxMessageSize = browserDetails.version === 57 ? 65535 : 65536;
1367          } else {
1368            // FF >= 60 supports sending ~2 GiB
1369            canSendMaxMessageSize = 2147483637;
1370          }
1371        }
1372        return canSendMaxMessageSize;
1373      };
1374    
1375      var getMaxMessageSize = function getMaxMessageSize(description, remoteIsFirefox) {
1376        // Note: 65536 bytes is the default value from the SDP spec. Also,
1377        //       every implementation we know supports receiving 65536 bytes.
1378        var maxMessageSize = 65536;
1379    
1380        // FF 57 has a slightly incorrect default remote max message size, so
1381        // we need to adjust it here to avoid a failure when sending.
1382        // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
1383        if (browserDetails.browser === 'firefox' && browserDetails.version === 57) {
1384          maxMessageSize = 65535;
1385        }
1386    
1387        var match = _sdp2.default.matchPrefix(description.sdp, 'a=max-message-size:');
1388        if (match.length > 0) {
1389          maxMessageSize = parseInt(match[0].substr(19), 10);
1390        } else if (browserDetails.browser === 'firefox' && remoteIsFirefox !== -1) {
1391          // If the maximum message size is not present in the remote SDP and
1392          // both local and remote are Firefox, the remote peer can receive
1393          // ~2 GiB.
1394          maxMessageSize = 2147483637;
1395        }
1396        return maxMessageSize;
1397      };
1398    
1399      var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
1400      window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
1401        this._sctp = null;
1402        // Chrome decided to not expose .sctp in plan-b mode.
1403        // As usual, adapter.js has to do an 'ugly worakaround'
1404        // to cover up the mess.
1405        if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
1406          var _getConfiguration = this.getConfiguration(),
1407              sdpSemantics = _getConfiguration.sdpSemantics;
1408    
1409          if (sdpSemantics === 'plan-b') {
1410            Object.defineProperty(this, 'sctp', {
1411              get: function get() {
1412                return typeof this._sctp === 'undefined' ? null : this._sctp;
1413              },
1414    
1415              enumerable: true,
1416              configurable: true
1417            });
1418          }
1419        }
1420    
1421        if (sctpInDescription(arguments[0])) {
1422          // Check if the remote is FF.
1423          var isFirefox = getRemoteFirefoxVersion(arguments[0]);
1424    
1425          // Get the maximum message size the local peer is capable of sending
1426          var canSendMMS = getCanSendMaxMessageSize(isFirefox);
1427    
1428          // Get the maximum message size of the remote peer.
1429          var remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
1430    
1431          // Determine final maximum message size
1432          var maxMessageSize = void 0;
1433          if (canSendMMS === 0 && remoteMMS === 0) {
1434            maxMessageSize = Number.POSITIVE_INFINITY;
1435          } else if (canSendMMS === 0 || remoteMMS === 0) {
1436            maxMessageSize = Math.max(canSendMMS, remoteMMS);
1437          } else {
1438            maxMessageSize = Math.min(canSendMMS, remoteMMS);
1439          }
1440    
1441          // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
1442          // attribute.
1443          var sctp = {};
1444          Object.defineProperty(sctp, 'maxMessageSize', {
1445            get: function get() {
1446              return maxMessageSize;
1447            }
1448          });
1449          this._sctp = sctp;
1450        }
1451    
1452        return origSetRemoteDescription.apply(this, arguments);
1453      };
1454    }
1455    
1456    function shimSendThrowTypeError(window) {
1457      if (!(window.RTCPeerConnection && 'createDataChannel' in window.RTCPeerConnection.prototype)) {
1458        return;
1459      }
1460    
1461      // Note: Although Firefox >= 57 has a native implementation, the maximum
1462      //       message size can be reset for all data channels at a later stage.
1463      //       See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
1464    
1465      function wrapDcSend(dc, pc) {
1466        var origDataChannelSend = dc.send;
1467        dc.send = function send() {
1468          var data = arguments[0];
1469          var length = data.length || data.size || data.byteLength;
1470          if (dc.readyState === 'open' && pc.sctp && length > pc.sctp.maxMessageSize) {
1471            throw new TypeError('Message too large (can send a maximum of ' + pc.sctp.maxMessageSize + ' bytes)');
1472          }
1473          return origDataChannelSend.apply(dc, arguments);
1474        };
1475      }
1476      var origCreateDataChannel = window.RTCPeerConnection.prototype.createDataChannel;
1477      window.RTCPeerConnection.prototype.createDataChannel = function createDataChannel() {
1478        var dataChannel = origCreateDataChannel.apply(this, arguments);
1479        wrapDcSend(dataChannel, this);
1480        return dataChannel;
1481      };
1482      utils.wrapPeerConnectionEvent(window, 'datachannel', function (e) {
1483        wrapDcSend(e.channel, e.target);
1484        return e;
1485      });
1486    }
1487    
1488    /* shims RTCConnectionState by pretending it is the same as iceConnectionState.
1489     * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
1490     * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
1491     * since DTLS failures would be hidden. See
1492     * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
1493     * for the Firefox tracking bug.
1494     */
1495    function shimConnectionState(window) {
1496      if (!window.RTCPeerConnection || 'connectionState' in window.RTCPeerConnection.prototype) {
1497        return;
1498      }
1499      var proto = window.RTCPeerConnection.prototype;
1500      Object.defineProperty(proto, 'connectionState', {
1501        get: function get() {
1502          return {
1503            completed: 'connected',
1504            checking: 'connecting'
1505          }[this.iceConnectionState] || this.iceConnectionState;
1506        },
1507    
1508        enumerable: true,
1509        configurable: true
1510      });
1511      Object.defineProperty(proto, 'onconnectionstatechange', {
1512        get: function get() {
1513          return this._onconnectionstatechange || null;
1514        },
1515        set: function set(cb) {
1516          if (this._onconnectionstatechange) {
1517            this.removeEventListener('connectionstatechange', this._onconnectionstatechange);
1518            delete this._onconnectionstatechange;
1519          }
1520          if (cb) {
1521            this.addEventListener('connectionstatechange', this._onconnectionstatechange = cb);
1522          }
1523        },
1524    
1525        enumerable: true,
1526        configurable: true
1527      });
1528    
1529      ['setLocalDescription', 'setRemoteDescription'].forEach(function (method) {
1530        var origMethod = proto[method];
1531        proto[method] = function () {
1532          if (!this._connectionstatechangepoly) {
1533            this._connectionstatechangepoly = function (e) {
1534              var pc = e.target;
1535              if (pc._lastConnectionState !== pc.connectionState) {
1536                pc._lastConnectionState = pc.connectionState;
1537                var newEvent = new Event('connectionstatechange', e);
1538                pc.dispatchEvent(newEvent);
1539              }
1540              return e;
1541            };
1542            this.addEventListener('iceconnectionstatechange', this._connectionstatechangepoly);
1543          }
1544          return origMethod.apply(this, arguments);
1545        };
1546      });
1547    }
1548    
1549    function removeAllowExtmapMixed(window) {
1550      /* remove a=extmap-allow-mixed for webrtc.org < M71 */
1551      if (!window.RTCPeerConnection) {
1552        return;
1553      }
1554      var browserDetails = utils.detectBrowser(window);
1555      if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
1556        return;
1557      }
1558      if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
1559        return;
1560      }
1561      var nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
1562      window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(desc) {
1563        if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
1564          desc.sdp = desc.sdp.split('\n').filter(function (line) {
1565            return line.trim() !== 'a=extmap-allow-mixed';
1566          }).join('\n');
1567        }
1568        return nativeSRD.apply(this, arguments);
1569      };
1570    }
1571    
1572    },{"./utils":15,"sdp":17}],7:[function(require,module,exports){
1573    /*
1574     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1575     *
1576     *  Use of this source code is governed by a BSD-style license
1577     *  that can be found in the LICENSE file in the root of the source
1578     *  tree.
1579     */
1580    /* eslint-env node */
1581    'use strict';
1582    
1583    Object.defineProperty(exports, "__esModule", {
1584      value: true
1585    });
1586    exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
1587    
1588    var _getusermedia = require('./getusermedia');
1589    
1590    Object.defineProperty(exports, 'shimGetUserMedia', {
1591      enumerable: true,
1592      get: function get() {
1593        return _getusermedia.shimGetUserMedia;
1594      }
1595    });
1596    
1597    var _getdisplaymedia = require('./getdisplaymedia');
1598    
1599    Object.defineProperty(exports, 'shimGetDisplayMedia', {
1600      enumerable: true,
1601      get: function get() {
1602        return _getdisplaymedia.shimGetDisplayMedia;
1603      }
1604    });
1605    exports.shimPeerConnection = shimPeerConnection;
1606    exports.shimReplaceTrack = shimReplaceTrack;
1607    
1608    var _utils = require('../utils');
1609    
1610    var utils = _interopRequireWildcard(_utils);
1611    
1612    var _filtericeservers = require('./filtericeservers');
1613    
1614    var _rtcpeerconnectionShim = require('rtcpeerconnection-shim');
1615    
1616    var _rtcpeerconnectionShim2 = _interopRequireDefault(_rtcpeerconnectionShim);
1617    
1618    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1619    
1620    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
1621    
1622    function shimPeerConnection(window) {
1623      var browserDetails = utils.detectBrowser(window);
1624    
1625      if (window.RTCIceGatherer) {
1626        if (!window.RTCIceCandidate) {
1627          window.RTCIceCandidate = function RTCIceCandidate(args) {
1628            return args;
1629          };
1630        }
1631        if (!window.RTCSessionDescription) {
1632          window.RTCSessionDescription = function RTCSessionDescription(args) {
1633            return args;
1634          };
1635        }
1636        // this adds an additional event listener to MediaStrackTrack that signals
1637        // when a tracks enabled property was changed. Workaround for a bug in
1638        // addStream, see below. No longer required in 15025+
1639        if (browserDetails.version < 15025) {
1640          var origMSTEnabled = Object.getOwnPropertyDescriptor(window.MediaStreamTrack.prototype, 'enabled');
1641          Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
1642            set: function set(value) {
1643              origMSTEnabled.set.call(this, value);
1644              var ev = new Event('enabled');
1645              ev.enabled = value;
1646              this.dispatchEvent(ev);
1647            }
1648          });
1649        }
1650      }
1651    
1652      // ORTC defines the DTMF sender a bit different.
1653      // https://github.com/w3c/ortc/issues/714
1654      if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
1655        Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
1656          get: function get() {
1657            if (this._dtmf === undefined) {
1658              if (this.track.kind === 'audio') {
1659                this._dtmf = new window.RTCDtmfSender(this);
1660              } else if (this.track.kind === 'video') {
1661                this._dtmf = null;
1662              }
1663            }
1664            return this._dtmf;
1665          }
1666        });
1667      }
1668      // Edge currently only implements the RTCDtmfSender, not the
1669      // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*
1670      if (window.RTCDtmfSender && !window.RTCDTMFSender) {
1671        window.RTCDTMFSender = window.RTCDtmfSender;
1672      }
1673    
1674      var RTCPeerConnectionShim = (0, _rtcpeerconnectionShim2.default)(window, browserDetails.version);
1675      window.RTCPeerConnection = function RTCPeerConnection(config) {
1676        if (config && config.iceServers) {
1677          config.iceServers = (0, _filtericeservers.filterIceServers)(config.iceServers, browserDetails.version);
1678          utils.log('ICE servers after filtering:', config.iceServers);
1679        }
1680        return new RTCPeerConnectionShim(config);
1681      };
1682      window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype;
1683    }
1684    
1685    function shimReplaceTrack(window) {
1686      // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
1687      if (window.RTCRtpSender && !('replaceTrack' in window.RTCRtpSender.prototype)) {
1688        window.RTCRtpSender.prototype.replaceTrack = window.RTCRtpSender.prototype.setTrack;
1689      }
1690    }
1691    
1692    }
1692,{"../utils":15,"./filtericeservers":8,"./getdisplaymedia":9,"./getusermedia":10,"rtcpeerconnection-shim":16}],8:[function(require,module,exports){
1693    /*
1694     *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
1695     *
1696     *  Use of this source code is governed by a BSD-style license
1697     *  that can be found in the LICENSE file in the root of the source
1698     *  tree.
1699     */
1700    /* eslint-env node */
1701    'use strict';
1702    
1703    Object.defineProperty(exports, "__esModule", {
1704      value: true
1705    });
1706    exports.filterIceServers = filterIceServers;
1707    
1708    var _utils = require('../utils');
1709    
1710    var utils = _interopRequireWildcard(_utils);
1711    
1712    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
1713    
1714    // Edge does not like
1715    // 1) stun: filtered after 14393 unless ?transport=udp is present
1716    // 2) turn: that does not have all of turn:host:port?transport=udp
1717    // 3) turn: with ipv6 addresses
1718    // 4) turn: occurring muliple times
1719    function filterIceServers(iceServers, edgeVersion) {
1720      var hasTurn = false;
1721      iceServers = JSON.parse(JSON.stringify(iceServers));
1722      return iceServers.filter(function (server) {
1723        if (server && (server.urls || server.url)) {
1724          var urls = server.urls || server.url;
1725          if (server.url && !server.urls) {
1726            utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
1727          }
1728          var isString = typeof urls === 'string';
1729          if (isString) {
1730            urls = [urls];
1731          }
1732          urls = urls.filter(function (url) {
1733            // filter STUN unconditionally.
1734            if (url.indexOf('stun:') === 0) {
1735              return false;
1736            }
1737    
1738            var validTurn = url.startsWith('turn') && !url.startsWith('turn:[') && url.includes('transport=udp');
1739            if (validTurn && !hasTurn) {
1740              hasTurn = true;
1741              return true;
1742            }
1743            return validTurn && !hasTurn;
1744          });
1745    
1746          delete server.url;
1747          server.urls = isString ? urls[0] : urls;
1748          return !!urls.length;
1749        }
1750      });
1751    }
1752    
1753    },{"../utils":15}],9:[function(require,module,exports){
1754    /*
1755     *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
1756     *
1757     *  Use of this source code is governed by a BSD-style license
1758     *  that can be found in the LICENSE file in the root of the source
1759     *  tree.
1760     */
1761    /* eslint-env node */
1762    'use strict';
1763    
1764    Object.defineProperty(exports, "__esModule", {
1765      value: true
1766    });
1767    exports.shimGetDisplayMedia = shimGetDisplayMedia;
1768    function shimGetDisplayMedia(window) {
1769      if (!('getDisplayMedia' in window.navigator)) {
1770        return;
1771      }
1772      if (!window.navigator.mediaDevices) {
1773        return;
1774      }
1775      if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
1776        return;
1777      }
1778      window.navigator.mediaDevices.getDisplayMedia = window.navigator.getDisplayMedia.bind(window.navigator);
1779    }
1780    
1781    },{}],10:[function(require,module,exports){
1782    /*
1783     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1784     *
1785     *  Use of this source code is governed by a BSD-style license
1786     *  that can be found in the LICENSE file in the root of the source
1787     *  tree.
1788     */
1789    /* eslint-env node */
1790    'use strict';
1791    
1792    Object.defineProperty(exports, "__esModule", {
1793      value: true
1794    });
1795    exports.shimGetUserMedia = shimGetUserMedia;
1796    function shimGetUserMedia(window) {
1797      var navigator = window && window.navigator;
1798    
1799      var shimError_ = function shimError_(e) {
1800        return {
1801          name: { PermissionDeniedError: 'NotAllowedError' }[e.name] || e.name,
1802          message: e.message,
1803          constraint: e.constraint,
1804          toString: function toString() {
1805            return this.name;
1806          }
1807        };
1808      };
1809    
1810      // getUserMedia error shim.
1811      var origGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
1812      navigator.mediaDevices.getUserMedia = function (c) {
1813        return origGetUserMedia(c).catch(function (e) {
1814          return Promise.reject(shimError_(e));
1815        });
1816      };
1817    }
1818    
1819    },{}],11:[function(require,module,exports){
1820    /*
1821     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
1822     *
1823     *  Use of this source code is governed by a BSD-style license
1824     *  that can be found in the LICENSE file in the root of the source
1825     *  tree.
1826     */
1827    /* eslint-env node */
1828    'use strict';
1829    
1830    Object.defineProperty(exports, "__esModule", {
1831      value: true
1832    });
1833    exports.shimGetDisplayMedia = exports.shimGetUserMedia = undefined;
1834    
1835    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
1836    
1837    var _getusermedia = require('./getusermedia');
1838    
1839    Object.defineProperty(exports, 'shimGetUserMedia', {
1840      enumerable: true,
1841      get: function get() {
1842        return _getusermedia.shimGetUserMedia;
1843      }
1844    });
1845    
1846    var _getdisplaymedia = require('./getdisplaymedia');
1847    
1848    Object.defineProperty(exports, 'shimGetDisplayMedia', {
1849      enumerable: true,
1850      get: function get() {
1851        return _getdisplaymedia.shimGetDisplayMedia;
1852      }
1853    });
1854    exports.shimOnTrack = shimOnTrack;
1855    exports.shimPeerConnection = shimPeerConnection;
1856    exports.shimSenderGetStats = shimSenderGetStats;
1857    exports.shimReceiverGetStats = shimReceiverGetStats;
1858    exports.shimRemoveStream = shimRemoveStream;
1859    exports.shimRTCDataChannel = shimRTCDataChannel;
1860    exports.shimAddTransceiver = shimAddTransceiver;
1861    exports.shimGetParameters = shimGetParameters;
1862    exports.shimCreateOffer = shimCreateOffer;
1863    exports.shimCreateAnswer = shimCreateAnswer;
1864    
1865    var _utils = require('../utils');
1866    
1867    var utils = _interopRequireWildcard(_utils);
1868    
1869    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
1870    
1871    function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
1872    
1873    function shimOnTrack(window) {
1874      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
1875        Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
1876          get: function get() {
1877            return { receiver: this.receiver };
1878          }
1879        });
1880      }
1881    }
1882    
1883    function shimPeerConnection(window) {
1884      var browserDetails = utils.detectBrowser(window);
1885    
1886      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
1887        return; // probably media.peerconnection.enabled=false in about:config
1888      }
1889      if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
1890        // very basic support for old versions.
1891        window.RTCPeerConnection = window.mozRTCPeerConnection;
1892      }
1893    
1894      if (browserDetails.version < 53) {
1895        // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
1896        ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'].forEach(function (method) {
1897          var nativeMethod = window.RTCPeerConnection.prototype[method];
1898          var methodObj = _defineProperty({}, method, function () {
1899            arguments[0] = new (method === 'addIceCandidate' ? window.RTCIceCandidate : window.RTCSessionDescription)(arguments[0]);
1900            return nativeMethod.apply(this, arguments);
1901          });
1902          window.RTCPeerConnection.prototype[method] = methodObj[method];
1903        });
1904      }
1905    
1906      // support for addIceCandidate(null or undefined)
1907      // as well as ignoring {sdpMid, candidate: ""}
1908      if (browserDetails.version < 68) {
1909        var nativeAddIceCandidate = window.RTCPeerConnection.prototype.addIceCandidate;
1910        window.RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate() {
1911          if (!arguments[0]) {
1912            if (arguments[1]) {
1913              arguments[1].apply(null);
1914            }
1915            return Promise.resolve();
1916          }
1917          // Firefox 68+ emits and processes {candidate: "", ...}, ignore
1918          // in older versions.
1919          if (arguments[0] && arguments[0].candidate === '') {
1920            return Promise.resolve();
1921          }
1922          return nativeAddIceCandidate.apply(this, arguments);
1923        };
1924      }
1925    
1926      var modernStatsTypes = {
1927        inboundrtp: 'inbound-rtp',
1928        outboundrtp: 'outbound-rtp',
1929        candidatepair: 'candidate-pair',
1930        localcandidate: 'local-candidate',
1931        remotecandidate: 'remote-candidate'
1932      };
1933    
1934      var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
1935      window.RTCPeerConnection.prototype.getStats = function getStats() {
1936        var _arguments = Array.prototype.slice.call(arguments),
1937            selector = _arguments[0],
1938            onSucc = _arguments[1],
1939            onErr = _arguments[2];
1940    
1941        return nativeGetStats.apply(this, [selector || null]).then(function (stats) {
1942          if (browserDetails.version < 53 && !onSucc) {
1943            // Shim only promise getStats with spec-hyphens in type names
1944            // Leave callback version alone; misc old uses of forEach before Map
1945            try {
1946              stats.forEach(function (stat) {
1947                stat.type = modernStatsTypes[stat.type] || stat.type;
1948              });
1949            } catch (e) {
1950              if (e.name !== 'TypeError') {
1951                throw e;
1952              }
1953              // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
vendor: 2,891 bytes, lines 1954-2034
1954              stats.forEach(function (stat, i) {
1955                stats.set(i, Object.assign({}, stat, {
1956                  type: modernStatsTypes[stat.type] || stat.type
1957                }));
1958              });
1959            }
1960          }
1961          return stats;
1962        }).then(onSucc, onErr);
1963      };
1964    }
1965    
1966    function shimSenderGetStats(window) {
1967      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
1968        return;
1969      }
1970      if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
1971        return;
1972      }
1973      var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
1974      if (origGetSenders) {
1975        window.RTCPeerConnection.prototype.getSenders = function getSenders() {
1976          var _this = this;
1977    
1978          var senders = origGetSenders.apply(this, []);
1979          senders.forEach(function (sender) {
1980            return sender._pc = _this;
1981          });
1982          return senders;
1983        };
1984      }
1985    
1986      var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
1987      if (origAddTrack) {
1988        window.RTCPeerConnection.prototype.addTrack = function addTrack() {
1989          var sender = origAddTrack.apply(this, arguments);
1990          sender._pc = this;
1991          return sender;
1992        };
1993      }
1994      window.RTCRtpSender.prototype.getStats = function getStats() {
1995        return this.track ? this._pc.getStats(this.track) : Promise.resolve(new Map());
1996      };
1997    }
1998    
1999    function shimReceiverGetStats(window) {
2000      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection && window.RTCRtpSender)) {
2001        return;
2002      }
2003      if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
2004        return;
2005      }
2006      var origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
2007      if (origGetReceivers) {
2008        window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
2009          var _this2 = this;
2010    
2011          var receivers = origGetReceivers.apply(this, []);
2012          receivers.forEach(function (receiver) {
2013            return receiver._pc = _this2;
2014          });
2015          return receivers;
2016        };
2017      }
2018      utils.wrapPeerConnectionEvent(window, 'track', function (e) {
2019        e.receiver._pc = e.srcElement;
2020        return e;
2021      });
2022      window.RTCRtpReceiver.prototype.getStats = function getStats() {
2023        return this._pc.getStats(this.track);
2024      };
2025    }
2026    
2027    function shimRemoveStream(window) {
2028      if (!window.RTCPeerConnection || 'removeStream' in window.RTCPeerConnection.prototype) {
2029        return;
2030      }
2031      window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
2032        var _this3 = this;
2033    
2034        utils.deprecated('removeStream', 'removeTrack');
2035        this.getSenders().forEach(function (sender) {
2036          if (sender.track && stream.getTracks().includes(sender.track)) {
2037            _this3.removeTrack(sender);
2038          }
2039        });
2040      };
2041    }
2042    
2043    function shimRTCDataChannel(window) {
2044      // rename DataChannel to RTCDataChannel (native fix in FF60):
2045      // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
2046      if (window.DataChannel && !window.RTCDataChannel) {
2047        window.RTCDataChannel = window.DataChannel;
2048      }
2049    }
2050    
2051    function shimAddTransceiver(window) {
2052      // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
2053      // Firefox ignores the init sendEncodings options passed to addTransceiver
2054      // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
2055      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
2056        return;
2057      }
2058      var origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
2059      if (origAddTransceiver) {
2060        window.RTCPeerConnection.prototype.addTransceiver = function addTransceiver() {
2061          this.setParametersPromises = [];
2062          var initParameters = arguments[1];
2063          var shouldPerformCheck = initParameters && 'sendEncodings' in initParameters;
2064          if (shouldPerformCheck) {
2065            // If sendEncodings params are provided, validate grammar
2066            initParameters.sendEncodings.forEach(function (encodingParam) {
2067              if ('rid' in encodingParam) {
2068                var ridRegex = /^[a-z0-9]{0,16}$/i;
2069                if (!ridRegex.test(encodingParam.rid)) {
2070                  throw new TypeError('Invalid RID value provided.');
2071                }
2072              }
2073              if ('scaleResolutionDownBy' in encodingParam) {
2074                if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
2075                  throw new RangeError('scale_resolution_down_by must be >= 1.0');
2076                }
2077              }
2078              if ('maxFramerate' in encodingParam) {
2079                if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
2080                  throw new RangeError('max_framerate must be >= 0.0');
2081                }
2082              }
2083            });
2084          }
2085          var transceiver = origAddTransceiver.apply(this, arguments);
2086          if (shouldPerformCheck) {
2087            // Check if the init options were applied. If not we do this in an
2088            // asynchronous way and save the promise reference in a global object.
2089            // This is an ugly hack, but at the same time is way more robust than
2090            // checking the sender parameters before and after the createOffer
2091            // Also note that after the createoffer we are not 100% sure that
2092            // the params were asynchronously applied so we might miss the
2093            // opportunity to recreate offer.
2094            var sender = transceiver.sender;
2095    
2096            var params = sender.getParameters();
2097            if (!('encodings' in params)) {
2098              params.encodings = initParameters.sendEncodings;
2099              sender.sendEncodings = initParameters.sendEncodings;
2100              this.setParametersPromises.push(sender.setParameters(params).then(function () {
2101                delete sender.sendEncodings;
2102              }).catch(function () {
2103                delete sender.sendEncodings;
2104              }));
2105            }
2106          }
2107          return transceiver;
2108        };
2109      }
2110    }
2111    
2112    function shimGetParameters(window) {
2113      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCRtpSender)) {
2114        return;
2115      }
2116      var origGetParameters = window.RTCRtpSender.prototype.getParameters;
2117      if (origGetParameters) {
2118        window.RTCRtpSender.prototype.getParameters = function getParameters() {
2119          var params = origGetParameters.apply(this, arguments);
2120          if (!('sendEncodings' in this)) {
2121            return params;
2122          }
2123          return Object.assign({}, { encodings: this.sendEncodings }, params);
2124        };
2125      }
2126    }
2127    
2128    function shimCreateOffer(window) {
2129      // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
2130      // Firefox ignores the init sendEncodings options passed to addTransceiver
2131      // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
2132      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
2133        return;
2134      }
2135      var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
2136      window.RTCPeerConnection.prototype.createOffer = function createOffer() {
2137        var _this4 = this,
2138            _arguments2 = arguments;
2139    
2140        if (this.setParametersPromises && this.setParametersPromises.length) {
2141          return Promise.all(this.setParametersPromises).then(function () {
2142            return origCreateOffer.apply(_this4, _arguments2);
2143          }).finally(function () {
2144            _this4.setParametersPromises = [];
2145          });
2146        }
2147        return origCreateOffer.apply(this, arguments);
2148      };
2149    }
2150    
2151    function shimCreateAnswer(window) {
2152      // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
2153      // Firefox ignores the init sendEncodings options passed to addTransceiver
2154      // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
2155      if (!((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCPeerConnection)) {
2156        return;
2157      }
2158      var origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
2159      window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
2160        var _this5 = this,
2161            _arguments3 = arguments;
2162    
2163        if (this.setParametersPromises && this.setParametersPromises.length) {
2164          return Promise.all(this.setParametersPromises).then(function () {
2165            return origCreateAnswer.apply(_this5, _arguments3);
2166          }).finally(function () {
2167            _this5.setParametersPromises = [];
2168          });
2169        }
2170        return origCreateAnswer.apply(this, arguments);
2171      };
2172    }
2173    
2174    }
2174,{"../utils":15,"./getdisplaymedia":12,"./getusermedia":13}],12:[function(require,module,exports){
2175    /*
2176     *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
2177     *
2178     *  Use of this source code is governed by a BSD-style license
2179     *  that can be found in the LICENSE file in the root of the source
2180     *  tree.
2181     */
2182    /* eslint-env node */
2183    'use strict';
2184    
2185    Object.defineProperty(exports, "__esModule", {
2186      value: true
2187    });
2188    exports.shimGetDisplayMedia = shimGetDisplayMedia;
2189    function shimGetDisplayMedia(window, preferredMediaSource) {
2190      if (window.navigator.mediaDevices && 'getDisplayMedia' in window.navigator.mediaDevices) {
2191        return;
2192      }
2193      if (!window.navigator.mediaDevices) {
2194        return;
2195      }
2196      window.navigator.mediaDevices.getDisplayMedia = function getDisplayMedia(constraints) {
2197        if (!(constraints && constraints.video)) {
2198          var err = new DOMException('getDisplayMedia without video ' + 'constraints is undefined');
2199          err.name = 'NotFoundError';
2200          // from https://heycam.github.io/webidl/#idl-DOMException-error-names
2201          err.code = 8;
2202          return Promise.reject(err);
2203        }
2204        if (constraints.video === true) {
2205          constraints.video = { mediaSource: preferredMediaSource };
2206        } else {
2207          constraints.video.mediaSource = preferredMediaSource;
2208        }
2209        return window.navigator.mediaDevices.getUserMedia(constraints);
2210      };
2211    }
2212    
2213    },{}],13:[function(require,module,exports){
2214    /*
2215     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2216     *
2217     *  Use of this source code is governed by a BSD-style license
2218     *  that can be found in the LICENSE file in the root of the source
2219     *  tree.
2220     */
2221    /* eslint-env node */
2222    'use strict';
2223    
2224    Object.defineProperty(exports, "__esModule", {
2225      value: true
2226    });
2227    
2228    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
2229    
2230    exports.shimGetUserMedia = shimGetUserMedia;
2231    
2232    var _utils = require('../utils');
2233    
2234    var utils = _interopRequireWildcard(_utils);
2235    
2236    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
2237    
2238    function shimGetUserMedia(window) {
2239      var browserDetails = utils.detectBrowser(window);
2240      var navigator = window && window.navigator;
2241      var MediaStreamTrack = window && window.MediaStreamTrack;
2242    
2243      navigator.getUserMedia = function (constraints, onSuccess, onError) {
2244        // Replace Firefox 44+'s deprecation warning with unprefixed version.
2245        utils.deprecated('navigator.getUserMedia', 'navigator.mediaDevices.getUserMedia');
2246        navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
2247      };
2248    
2249      if (!(browserDetails.version > 55 && 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
2250        var remap = function remap(obj, a, b) {
2251          if (a in obj && !(b in obj)) {
2252            obj[b] = obj[a];
2253            delete obj[a];
2254          }
2255        };
2256    
2257        var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);
2258        navigator.mediaDevices.getUserMedia = function (c) {
2259          if ((typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object' && _typeof(c.audio) === 'object') {
2260            c = JSON.parse(JSON.stringify(c));
2261            remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
2262            remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
2263          }
2264          return nativeGetUserMedia(c);
2265        };
2266    
2267        if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
2268          var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
2269          MediaStreamTrack.prototype.getSettings = function () {
2270            var obj = nativeGetSettings.apply(this, arguments);
2271            remap(obj, 'mozAutoGainControl', 'autoGainControl');
2272            remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
2273            return obj;
2274          };
2275        }
2276    
2277        if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
2278          var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
2279          MediaStreamTrack.prototype.applyConstraints = function (c) {
2280            if (this.kind === 'audio' && (typeof c === 'undefined' ? 'undefined' : _typeof(c)) === 'object') {
2281              c = JSON.parse(JSON.stringify(c));
2282              remap(c, 'autoGainControl', 'mozAutoGainControl');
2283              remap(c, 'noiseSuppression', 'mozNoiseSuppression');
2284            }
2285            return nativeApplyConstraints.apply(this, [c]);
2286          };
2287        }
2288      }
2289    }
2290    
2291    },{"../utils":15}],14:[function(require,module,exports){
2292    /*
2293     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2294     *
2295     *  Use of this source code is governed by a BSD-style license
2296     *  that can be found in the LICENSE file in the root of the source
2297     *  tree.
2298     */
2299    'use strict';
2300    
2301    Object.defineProperty(exports, "__esModule", {
2302      value: true
2303    });
2304    
2305    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
2306    
2307    exports.shimLocalStreamsAPI = shimLocalStreamsAPI;
2308    exports.shimRemoteStreamsAPI = shimRemoteStreamsAPI;
2309    exports.shimCallbacksAPI = shimCallbacksAPI;
2310    exports.shimGetUserMedia = shimGetUserMedia;
2311    exports.shimConstraints = shimConstraints;
2312    exports.shimRTCIceServerUrls = shimRTCIceServerUrls;
2313    exports.shimTrackEventTransceiver = shimTrackEventTransceiver;
2314    exports.shimCreateOfferLegacy = shimCreateOfferLegacy;
2315    exports.shimAudioContext = shimAudioContext;
2316    
2317    var _utils = require('../utils');
2318    
2319    var utils = _interopRequireWildcard(_utils);
2320    
2321    function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
2322    
2323    function shimLocalStreamsAPI(window) {
2324      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
2325        return;
2326      }
2327      if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
2328        window.RTCPeerConnection.prototype.getLocalStreams = function getLocalStreams() {
2329          if (!this._localStreams) {
2330            this._localStreams = [];
2331          }
2332          return this._localStreams;
2333        };
2334      }
2335      if (!('addStream' in window.RTCPeerConnection.prototype)) {
2336        var _addTrack = window.RTCPeerConnection.prototype.addTrack;
2337        window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
2338          var _this = this;
2339    
2340          if (!this._localStreams) {
2341            this._localStreams = [];
2342          }
2343          if (!this._localStreams.includes(stream)) {
2344            this._localStreams.push(stream);
2345          }
2346          // Try to emulate Chrome's behaviour of adding in audio-video order.
2347          // Safari orders by track id.
2348          stream.getAudioTracks().forEach(function (track) {
2349            return _addTrack.call(_this, track, stream);
2350          });
2351          stream.getVideoTracks().forEach(function (track) {
2352            return _addTrack.call(_this, track, stream);
2353          });
2354        };
2355    
2356        window.RTCPeerConnection.prototype.addTrack = function addTrack(track) {
2357          var _this2 = this;
2358    
2359          for (var _len = arguments.length, streams = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2360            streams[_key - 1] = arguments[_key];
2361          }
2362    
2363          if (streams) {
2364            streams.forEach(function (stream) {
2365              if (!_this2._localStreams) {
2366                _this2._localStreams = [stream];
2367              } else if (!_this2._localStreams.includes(stream)) {
2368                _this2._localStreams.push(stream);
2369              }
2370            });
2371          }
2372          return _addTrack.apply(this, arguments);
2373        };
2374      }
2375      if (!('removeStream' in window.RTCPeerConnection.prototype)) {
2376        window.RTCPeerConnection.prototype.removeStream = function removeStream(stream) {
2377          var _this3 = this;
2378    
2379          if (!this._localStreams) {
2380            this._localStreams = [];
2381          }
2382          var index = this._localStreams.indexOf(stream);
2383          if (index === -1) {
2384            return;
2385          }
2386          this._localStreams.splice(index, 1);
2387          var tracks = stream.getTracks();
vendor: 4,925 bytes, lines 2388-2510
2388          this.getSenders().forEach(function (sender) {
2389            if (tracks.includes(sender.track)) {
2390              _this3.removeTrack(sender);
2391            }
2392          });
2393        };
2394      }
2395    }
2396    
2397    function shimRemoteStreamsAPI(window) {
2398      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
2399        return;
2400      }
2401      if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
2402        window.RTCPeerConnection.prototype.getRemoteStreams = function getRemoteStreams() {
2403          return this._remoteStreams ? this._remoteStreams : [];
2404        };
2405      }
2406      if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
2407        Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
2408          get: function get() {
2409            return this._onaddstream;
2410          },
2411          set: function set(f) {
2412            var _this4 = this;
2413    
2414            if (this._onaddstream) {
2415              this.removeEventListener('addstream', this._onaddstream);
2416              this.removeEventListener('track', this._onaddstreampoly);
2417            }
2418            this.addEventListener('addstream', this._onaddstream = f);
2419            this.addEventListener('track', this._onaddstreampoly = function (e) {
2420              e.streams.forEach(function (stream) {
2421                if (!_this4._remoteStreams) {
2422                  _this4._remoteStreams = [];
2423                }
2424                if (_this4._remoteStreams.includes(stream)) {
2425                  return;
2426                }
2427                _this4._remoteStreams.push(stream);
2428                var event = new Event('addstream');
2429                event.stream = stream;
2430                _this4.dispatchEvent(event);
2431              });
2432            });
2433          }
2434        });
2435        var origSetRemoteDescription = window.RTCPeerConnection.prototype.setRemoteDescription;
2436        window.RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription() {
2437          var pc = this;
2438          if (!this._onaddstreampoly) {
2439            this.addEventListener('track', this._onaddstreampoly = function (e) {
2440              e.streams.forEach(function (stream) {
2441                if (!pc._remoteStreams) {
2442                  pc._remoteStreams = [];
2443                }
2444                if (pc._remoteStreams.indexOf(stream) >= 0) {
2445                  return;
2446                }
2447                pc._remoteStreams.push(stream);
2448                var event = new Event('addstream');
2449                event.stream = stream;
2450                pc.dispatchEvent(event);
2451              });
2452            });
2453          }
2454          return origSetRemoteDescription.apply(pc, arguments);
2455        };
2456      }
2457    }
2458    
2459    function shimCallbacksAPI(window) {
2460      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || !window.RTCPeerConnection) {
2461        return;
2462      }
2463      var prototype = window.RTCPeerConnection.prototype;
2464      var origCreateOffer = prototype.createOffer;
2465      var origCreateAnswer = prototype.createAnswer;
2466      var setLocalDescription = prototype.setLocalDescription;
2467      var setRemoteDescription = prototype.setRemoteDescription;
2468      var addIceCandidate = prototype.addIceCandidate;
2469    
2470      prototype.createOffer = function createOffer(successCallback, failureCallback) {
2471        var options = arguments.length >= 2 ? arguments[2] : arguments[0];
2472        var promise = origCreateOffer.apply(this, [options]);
2473        if (!failureCallback) {
2474          return promise;
2475        }
2476        promise.then(successCallback, failureCallback);
2477        return Promise.resolve();
2478      };
2479    
2480      prototype.createAnswer = function createAnswer(successCallback, failureCallback) {
2481        var options = arguments.length >= 2 ? arguments[2] : arguments[0];
2482        var promise = origCreateAnswer.apply(this, [options]);
2483        if (!failureCallback) {
2484          return promise;
2485        }
2486        promise.then(successCallback, failureCallback);
2487        return Promise.resolve();
2488      };
2489    
2490      var withCallback = function withCallback(description, successCallback, failureCallback) {
2491        var promise = setLocalDescription.apply(this, [description]);
2492        if (!failureCallback) {
2493          return promise;
2494        }
2495        promise.then(successCallback, failureCallback);
2496        return Promise.resolve();
2497      };
2498      prototype.setLocalDescription = withCallback;
2499    
2500      withCallback = function withCallback(description, successCallback, failureCallback) {
2501        var promise = setRemoteDescription.apply(this, [description]);
2502        if (!failureCallback) {
2503          return promise;
2504        }
2505        promise.then(successCallback, failureCallback);
2506        return Promise.resolve();
2507      };
2508      prototype.setRemoteDescription = withCallback;
2509    
2510      withCallback = function withCallback(candidate, successCallback, failureCallback) {
2511        var promise = addIceCandidate.apply(this, [candidate]);
2512        if (!failureCallback) {
2513          return promise;
2514        }
2515        promise.then(successCallback, failureCallback);
2516        return Promise.resolve();
2517      };
2518      prototype.addIceCandidate = withCallback;
2519    }
2520    
2521    function shimGetUserMedia(window) {
2522      var navigator = window && window.navigator;
2523    
2524      if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
2525        // shim not needed in Safari 12.1
2526        var mediaDevices = navigator.mediaDevices;
2527        var _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
2528        navigator.mediaDevices.getUserMedia = function (constraints) {
2529          return _getUserMedia(shimConstraints(constraints));
2530        };
2531      }
2532    
2533      if (!navigator.getUserMedia && navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
2534        navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
2535          navigator.mediaDevices.getUserMedia(constraints).then(cb, errcb);
2536        }.bind(navigator);
2537      }
2538    }
2539    
2540    function shimConstraints(constraints) {
2541      if (constraints && constraints.video !== undefined) {
2542        return Object.assign({}, constraints, { video: utils.compactObject(constraints.video) });
2543      }
2544    
2545      return constraints;
2546    }
2547    
2548    function shimRTCIceServerUrls(window) {
2549      if (!window.RTCPeerConnection) {
2550        return;
2551      }
2552      // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
2553      var OrigPeerConnection = window.RTCPeerConnection;
2554      window.RTCPeerConnection = function RTCPeerConnection(pcConfig, pcConstraints) {
2555        if (pcConfig && pcConfig.iceServers) {
2556          var newIceServers = [];
2557          for (var i = 0; i < pcConfig.iceServers.length; i++) {
2558            var server = pcConfig.iceServers[i];
2559            if (!server.hasOwnProperty('urls') && server.hasOwnProperty('url')) {
2560              utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
2561              server = JSON.parse(JSON.stringify(server));
2562              server.urls = server.url;
2563              delete server.url;
2564              newIceServers.push(server);
2565            } else {
2566              newIceServers.push(pcConfig.iceServers[i]);
2567            }
2568          }
2569          pcConfig.iceServers = newIceServers;
2570        }
2571        return new OrigPeerConnection(pcConfig, pcConstraints);
2572      };
2573      window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
2574      // wrap static methods. Currently just generateCertificate.
2575      if ('generateCertificate' in OrigPeerConnection) {
2576        Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
2577          get: function get() {
2578            return OrigPeerConnection.generateCertificate;
2579          }
2580        });
2581      }
2582    }
2583    
2584    function shimTrackEventTransceiver(window) {
2585      // Add event.transceiver member over deprecated event.receiver
2586      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && window.RTCTrackEvent && 'receiver' in window.RTCTrackEvent.prototype && !('transceiver' in window.RTCTrackEvent.prototype)) {
2587        Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
2588          get: function get() {
2589            return { receiver: this.receiver };
2590          }
2591        });
2592      }
2593    }
2594    
2595    function shimCreateOfferLegacy(window) {
2596      var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
2597      window.RTCPeerConnection.prototype.createOffer = function createOffer(offerOptions) {
2598        if (offerOptions) {
2599          if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
2600            // support bit values
2601            offerOptions.offerToReceiveAudio = !!offerOptions.offerToReceiveAudio;
2602          }
2603          var audioTransceiver = this.getTransceivers().find(function (transceiver) {
2604            return transceiver.receiver.track.kind === 'audio';
2605          });
2606          if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
2607            if (audioTransceiver.direction === 'sendrecv') {
2608              if (audioTransceiver.setDirection) {
2609                audioTransceiver.setDirection('sendonly');
2610              } else {
2611                audioTransceiver.direction = 'sendonly';
2612              }
2613            } else if (audioTransceiver.direction === 'recvonly') {
2614              if (audioTransceiver.setDirection) {
2615                audioTransceiver.setDirection('inactive');
2616              } else {
2617                audioTransceiver.direction = 'inactive';
2618              }
2619            }
2620          } else if (offerOptions.offerToReceiveAudio === true && !audioTransceiver) {
2621            this.addTransceiver('audio');
2622          }
2623    
2624          if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
2625            // support bit values
2626            offerOptions.offerToReceiveVideo = !!offerOptions.offerToReceiveVideo;
2627          }
2628          var videoTransceiver = this.getTransceivers().find(function (transceiver) {
2629            return transceiver.receiver.track.kind === 'video';
2630          });
2631          if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
2632            if (videoTransceiver.direction === 'sendrecv') {
2633              if (videoTransceiver.setDirection) {
2634                videoTransceiver.setDirection('sendonly');
2635              } else {
2636                videoTransceiver.direction = 'sendonly';
2637              }
2638            } else if (videoTransceiver.direction === 'recvonly') {
2639              if (videoTransceiver.setDirection) {
2640                videoTransceiver.setDirection('inactive');
2641              } else {
2642                videoTransceiver.direction = 'inactive';
2643              }
2644            }
2645          } else if (offerOptions.offerToReceiveVideo === true && !videoTransceiver) {
2646            this.addTransceiver('video');
2647          }
2648        }
2649        return origCreateOffer.apply(this, arguments);
2650      };
2651    }
2652    
2653    function shimAudioContext(window) {
2654      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) !== 'object' || window.AudioContext) {
2655        return;
2656      }
2657      window.AudioContext = window.webkitAudioContext;
2658    }
2659    
2660    },{"../utils":15}],15:[function(require,module,exports){
2661    /*
2662     *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
2663     *
2664     *  Use of this source code is governed by a BSD-style license
2665     *  that can be found in the LICENSE file in the root of the source
2666     *  tree.
2667     */
2668    /* eslint-env node */
2669    'use strict';
2670    
2671    Object.defineProperty(exports, "__esModule", {
2672      value: true
2673    });
2674    
2675    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
2676    
2677    exports.extractVersion = extractVersion;
2678    exports.wrapPeerConnectionEvent = wrapPeerConnectionEvent;
2679    exports.disableLog = disableLog;
2680    exports.disableWarnings = disableWarnings;
2681    exports.log = log;
2682    exports.deprecated = deprecated;
2683    exports.detectBrowser = detectBrowser;
2684    exports.compactObject = compactObject;
2685    exports.walkStats = walkStats;
2686    exports.filterStats = filterStats;
2687    
2688    function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2689    
2690    var logDisabled_ = true;
2691    var deprecationWarnings_ = true;
2692    
2693    /**
2694     * Extract browser version out of the provided user agent string.
2695     *
2696     * @param {!string} uastring userAgent string.
2697     * @param {!string} expr Regular expression used as match criteria.
2698     * @param {!number} pos position in the version string to be returned.
2699     * @return {!number} browser version.
2700     */
2701    function extractVersion(uastring, expr, pos) {
2702      var match = uastring.match(expr);
2703      return match && match.length >= pos && parseInt(match[pos], 10);
2704    }
2705    
2706    // Wraps the peerconnection event eventNameToWrap in a function
2707    // which returns the modified event object (or false to prevent
2708    // the event).
2709    function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
2710      if (!window.RTCPeerConnection) {
2711        return;
2712      }
2713      var proto = window.RTCPeerConnection.prototype;
2714      var nativeAddEventListener = proto.addEventListener;
2715      proto.addEventListener = function (nativeEventName, cb) {
2716        if (nativeEventName !== eventNameToWrap) {
2717          return nativeAddEventListener.apply(this, arguments);
2718        }
2719        var wrappedCallback = function wrappedCallback(e) {
2720          var modifiedEvent = wrapper(e);
2721          if (modifiedEvent) {
2722            if (cb.handleEvent) {
2723              cb.handleEvent(modifiedEvent);
2724            } else {
2725              cb(modifiedEvent);
2726            }
2727          }
2728        };
2729        this._eventMap = this._eventMap || {};
2730        if (!this._eventMap[eventNameToWrap]) {
2731          this._eventMap[eventNameToWrap] = new Map();
2732        }
2733        this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
2734        return nativeAddEventListener.apply(this, [nativeEventName, wrappedCallback]);
2735      };
2736    
2737      var nativeRemoveEventListener = proto.removeEventListener;
2738      proto.removeEventListener = function (nativeEventName, cb) {
2739        if (nativeEventName !== eventNameToWrap || !this._eventMap || !this._eventMap[eventNameToWrap]) {
2740          return nativeRemoveEventListener.apply(this, arguments);
2741        }
2742        if (!this._eventMap[eventNameToWrap].has(cb)) {
2743          return nativeRemoveEventListener.apply(this, arguments);
2744        }
2745        var unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
2746        this._eventMap[eventNameToWrap].delete(cb);
2747        if (this._eventMap[eventNameToWrap].size === 0) {
2748          delete this._eventMap[eventNameToWrap];
2749        }
2750        if (Object.keys(this._eventMap).length === 0) {
2751          delete this._eventMap;
2752        }
2753        return nativeRemoveEventListener.apply(this, [nativeEventName, unwrappedCb]);
2754      };
2755    
2756      Object.defineProperty(proto, 'on' + eventNameToWrap, {
2757        get: function get() {
2758          return this['_on' + eventNameToWrap];
2759        },
2760        set: function set(cb) {
2761          if (this['_on' + eventNameToWrap]) {
2762            this.removeEventListener(eventNameToWrap, this['_on' + eventNameToWrap]);
2763            delete this['_on' + eventNameToWrap];
2764          }
2765          if (cb) {
2766            this.addEventListener(eventNameToWrap, this['_on' + eventNameToWrap] = cb);
2767          }
2768        },
2769    
2770        enumerable: true,
2771        configurable: true
2772      });
2773    }
2774    
2775    function disableLog(bool) {
2776      if (typeof bool !== 'boolean') {
2777        return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
2778      }
2779      logDisabled_ = bool;
2780      return bool ? 'adapter.js logging disabled' : 'adapter.js logging enabled';
2781    }
2782    
2783    /**
2784     * Disable or enable deprecation warnings
2785     * @param {!boolean} bool set to true to disable warnings.
2786     */
2787    function disableWarnings(bool) {
2788      if (typeof bool !== 'boolean') {
2789        return new Error('Argument type: ' + (typeof bool === 'undefined' ? 'undefined' : _typeof(bool)) + '. Please use a boolean.');
2790      }
2791      deprecationWarnings_ = !bool;
2792      return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
2793    }
2794    
2795    function log() {
2796      if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object') {
2797        if (logDisabled_) {
2798          return;
2799        }
2800        if (typeof console !== 'undefined' && typeof console.log === 'function') {
2801          console.log.apply(console, arguments);
2802        }
2803      }
2804    }
2805    
2806    /**
2807     * Shows a deprecation warning suggesting the modern and spec-compatible API.
2808     */
2809    function deprecated(oldMethod, newMethod) {
2810      if (!deprecationWarnings_) {
2811        return;
2812      }
2813      console.warn(oldMethod + ' is deprecated, please use ' + newMethod + ' instead.');
2814    }
2815    
2816    /**
2817     * Browser detector.
2818     *
2819     * @return {object} result containing browser and version
2820     *     properties.
2821     */
2822    function detectBrowser(window) {
2823      var navigator = window.navigator;
2824    
2825      // Returned result object.
2826    
2827      var result = { browser: null, version: null };
2828    
2829      // Fail early if it's not a browser
2830      if (typeof window === 'undefined' || !window.navigator) {
2831        result.browser = 'Not a browser.';
2832        return result;
2833      }
2834    
2835      if (navigator.mozGetUserMedia) {
2836        // Firefox.
2837        result.browser = 'firefox';
2838        result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
2839      } else if (navigator.webkitGetUserMedia || window.isSecureContext === false && window.webkitRTCPeerConnection && !window.RTCIceGatherer) {
2840        // Chrome, Chromium, Webview, Opera.
2841        // Version matches Chrome/WebRTC version.
2842        // Chrome 74 removed webkitGetUserMedia on http as well so we need the
2843        // more complicated fallback to webkitRTCPeerConnection.
2844        result.browser = 'chrome';
2845        result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
2846      } else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
2847        // Edge.
2848        result.browser = 'edge';
2849        result.version = extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2);
2850      } else if (window.RTCPeerConnection && navigator.userAgent.match(/AppleWeb
2850Kit\/(\d+)\./)) {
2851        // Safari.
2852        result.browser = 'safari';
2853        result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
2854        result.supportsUnifiedPlan = window.RTCRtpTransceiver && 'currentDirection' in window.RTCRtpTransceiver.prototype;
2855      } else {
2856        // Default fallthrough: not supported.
2857        result.browser = 'Not a supported browser.';
2858        return result;
2859      }
2860    
2861      return result;
2862    }
2863    
2864    /**
2865     * Checks if something is an object.
2866     *
2867     * @param {*} val The something you want to check.
2868     * @return true if val is an object, false otherwise.
2869     */
2870    function isObject(val) {
2871      return Object.prototype.toString.call(val) === '[object Object]';
2872    }
2873    
2874    /**
2875     * Remove all empty objects and undefined values
2876     * from a nested object -- an enhanced and vanilla version
2877     * of Lodash's `compact`.
2878     */
2879    function compactObject(data) {
2880      if (!isObject(data)) {
2881        return data;
2882      }
2883    
2884      return Object.keys(data).reduce(function (accumulator, key) {
2885        var isObj = isObject(data[key]);
2886        var value = isObj ? compactObject(data[key]) : data[key];
2887        var isEmptyObject = isObj && !Object.keys(value).length;
2888        if (value === undefined || isEmptyObject) {
2889          return accumulator;
2890        }
2891        return Object.assign(accumulator, _defineProperty({}, key, value));
2892      }, {});
2893    }
2894    
2895    /* iterates the stats graph recursively. */
2896    function walkStats(stats, base, resultSet) {
2897      if (!base || resultSet.has(base.id)) {
2898        return;
2899      }
2900      resultSet.set(base.id, base);
2901      Object.keys(base).forEach(function (name) {
2902        if (name.endsWith('Id')) {
2903          walkStats(stats, stats.get(base[name]), resultSet);
2904        } else if (name.endsWith('Ids')) {
2905          base[name].forEach(function (id) {
2906            walkStats(stats, stats.get(id), resultSet);
2907          });
2908        }
2909      });
2910    }
2911    
2912    /* filter getStats for a sender/receiver track. */
2913    function filterStats(result, track, outbound) {
2914      var streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
2915      var filteredResult = new Map();
2916      if (track === null) {
2917        return filteredResult;
2918      }
2919      var trackStats = [];
2920      result.forEach(function (value) {
2921        if (value.type === 'track' && value.trackIdentifier === track.id) {
2922          trackStats.push(value);
2923        }
2924      });
2925      trackStats.forEach(function (trackStat) {
2926        result.forEach(function (stats) {
2927          if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
2928            walkStats(result, stats, filteredResult);
2929          }
2930        });
2931      });
2932      return filteredResult;
2933    }
2934    
2935    },{}],16:[function(require,module,exports){
2936    /*
2937     *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
2938     *
2939     *  Use of this source code is governed by a BSD-style license
2940     *  that can be found in the LICENSE file in the root of the source
2941     *  tree.
2942     */
2943     /* eslint-env node */
2944    'use strict';
2945    
2946    var SDPUtils = require('sdp');
2947    
2948    function fixStatsType(stat) {
2949      return {
2950        inboundrtp: 'inbound-rtp',
2951        outboundrtp: 'outbound-rtp',
2952        candidatepair: 'candidate-pair',
2953        localcandidate: 'local-candidate',
2954        remotecandidate: 'remote-candidate'
2955      }[stat.type] || stat.type;
2956    }
2957    
2958    function writeMediaSection(transceiver, caps, type, stream, dtlsRole) {
2959      var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
2960    
2961      // Map ICE parameters (ufrag, pwd) to SDP.
2962      sdp += SDPUtils.writeIceParameters(
2963          transceiver.iceGatherer.getLocalParameters());
2964    
2965      // Map DTLS parameters to SDP.
2966      sdp += SDPUtils.writeDtlsParameters(
2967          transceiver.dtlsTransport.getLocalParameters(),
2968          type === 'offer' ? 'actpass' : dtlsRole || 'active');
2969    
2970      sdp += 'a=mid:' + transceiver.mid + '\r\n';
2971    
2972      if (transceiver.rtpSender && transceiver.rtpReceiver) {
2973        sdp += 'a=sendrecv\r\n';
2974      } else if (transceiver.rtpSender) {
2975        sdp += 'a=sendonly\r\n';
2976      } else if (transceiver.rtpReceiver) {
2977        sdp += 'a=recvonly\r\n';
2978      } else {
2979        sdp += 'a=inactive\r\n';
2980      }
2981    
2982      if (transceiver.rtpSender) {
2983        var trackId = transceiver.rtpSender._initialTrackId ||
2984            transceiver.rtpSender.track.id;
2985        transceiver.rtpSender._initialTrackId = trackId;
2986        // spec.
2987        var msid = 'msid:' + (stream ? stream.id : '-') + ' ' +
2988            trackId + '\r\n';
2989        sdp += 'a=' + msid;
2990        // for Chrome. Legacy should no longer be required.
2991        sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
2992            ' ' + msid;
2993    
2994        // RTX
2995        if (transceiver.sendEncodingParameters[0].rtx) {
2996          sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
2997              ' ' + msid;
2998          sdp += 'a=ssrc-group:FID ' +
2999              transceiver.sendEncodingParameters[0].ssrc + ' ' +
3000              transceiver.sendEncodingParameters[0].rtx.ssrc +
3001              '\r\n';
3002        }
3003      }
3004      // FIXME: this should be written by writeRtpDescription.
3005      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
3006          ' cname:' + SDPUtils.localCName + '\r\n';
3007      if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
3008        sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
3009            ' cname:' + SDPUtils.localCName + '\r\n';
3010      }
3011      return sdp;
3012    }
3013    
3014    // Edge does not like
3015    // 1) stun: filtered after 14393 unless ?transport=udp is present
3016    // 2) turn: that does not have all of turn:host:port?transport=udp
3017    // 3) turn: with ipv6 addresses
3018    // 4) turn: occurring muliple times
3019    function filterIceServers(iceServers, edgeVersion) {
3020      var hasTurn = false;
3021      iceServers = JSON.parse(JSON.stringify(iceServers));
3022      return iceServers.filter(function(server) {
3023        if (server && (server.urls || server.url)) {
3024          var urls = server.urls || server.url;
3025          if (server.url && !server.urls) {
3026            console.warn('RTCIceServer.url is deprecated! Use urls instead.');
3027          }
3028          var isString = typeof urls === 'string';
3029          if (isString) {
3030            urls = [urls];
3031          }
3032          urls = urls.filter(function(url) {
3033            var validTurn = url.indexOf('turn:') === 0 &&
3034                url.indexOf('transport=udp') !== -1 &&
3035                url.indexOf('turn:[') === -1 &&
3036                !hasTurn;
3037    
3038            if (validTurn) {
3039              hasTurn = true;
3040              return true;
3041            }
3042            return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&
3043                url.indexOf('?transport=udp') === -1;
3044          });
3045    
3046          delete server.url;
3047          server.urls = isString ? urls[0] : urls;
3048          return !!urls.length;
3049        }
3050      });
3051    }
3052    
3053    // Determines the intersection of local and remote capabilities.
3054    function getCommonCapabilities(localCapabilities, remoteCapabilities) {
3055      var commonCapabilities = {
3056        codecs: [],
3057        headerExtensions: [],
3058        fecMechanisms: []
3059      };
3060    
3061      var findCodecByPayloadType = function(pt, codecs) {
3062        pt = parseInt(pt, 10);
3063        for (var i = 0; i < codecs.length; i++) {
3064          if (codecs[i].payloadType === pt ||
3065              codecs[i].preferredPayloadType === pt) {
3066            return codecs[i];
3067          }
3068        }
3069      };
3070    
3071      var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
3072        var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
3073        var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
3074        return lCodec && rCodec &&
3075            lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
3076      };
3077    
3078      localCapabilities.codecs.forEach(function(lCodec) {
3079        for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
3080          var rCodec = remoteCapabilities.codecs[i];
3081          if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
3082              lCodec.clockRate === rCodec.clockRate) {
3083            if (lCodec.name.toLowerCase() === 'rtx' &&
3084                lCodec.parameters && rCodec.parameters.apt) {
3085              // for RTX we need to find the local rtx that has a apt
3086              // which points to the same local codec as the remote one.
3087              if (!rtxCapabilityMatches(lCodec, rCodec,
3088                  localCapabilities.codecs, remoteCapabilities.codecs)) {
3089                continue;
3090              }
3091            }
3092            rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
3093            // number of channels is the highest common number of channels
3094            rCodec.numChannels = Math.min(lCodec.numChannels,
3095                rCodec.numChannels);
3096            // push rCodec so we reply with offerer payload type
3097            commonCapabilities.codecs.push(rCodec);
3098    
3099            // determine common feedback mechanisms
3100            rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
3101              for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
3102                if (lCodec.rtcpFeedback[j].type === fb.type &&
3103                    lCodec.rtcpFeedback[j].parameter === fb.parameter) {
3104                  return true;
3105                }
3106              }
3107              return false;
3108            });
3109            // FIXME: also need to determine .parameters
3110            //  see https://github.com/openpeer/ortc/issues/569
3111            break;
3112          }
3113        }
3114      });
3115    
3116      localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
3117        for (var i = 0; i < remoteCapabilities.headerExtensions.length;
3118             i++) {
3119          var rHeaderExtension = remoteCapabilities.headerExtensions[i];
3120          if (lHeaderExtension.uri === rHeaderExtension.uri) {
3121            commonCapabilities.headerExtensions.push(rHeaderExtension);
3122            break;
3123          }
3124        }
3125      });
3126    
3127      // FIXME: fecMechanisms
3128      return commonCapabilities;
3129    }
3130    
3131    // is action=setLocalDescription with type allowed in signalingState
3132    function isActionAllowedInSignalingState(action, type, signalingState) {
3133      return {
3134        offer: {
3135          setLocalDescription: ['stable', 'have-local-offer'],
3136          setRemoteDescription: ['stable', 'have-remote-offer']
3137        },
3138        answer: {
3139          setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
3140          setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
3141        }
3142      }[type][action].indexOf(signalingState) !== -1;
3143    }
3144    
3145    function maybeAddCandidate(iceTransport, candidate) {
3146      // Edge's internal representation adds some fields therefore
3147      // not all fieldѕ are taken into account.
3148      var alreadyAdded = iceTransport.getRemoteCandidates()
3149          .find(function(remoteCandidate) {
3150            return candidate.foundation === remoteCandidate.foundation &&
3151                candidate.ip === remoteCandidate.ip &&
3152                candidate.port === remoteCandidate.port &&
3153                candidate.priority === remoteCandidate.priority &&
3154                candidate.protocol === remoteCandidate.protocol &&
3155                candidate.type === remoteCandidate.type;
3156          });
3157      if (!alreadyAdded) {
3158        iceTransport.addRemoteCandidate(candidate);
3159      }
3160      return !alreadyAdded;
3161    }
3162    
3163    
3164    function makeError(name, description) {
3165      var e = new Error(description);
3166      e.name = name;
3167      // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names
3168      e.code = {
3169        NotSupportedError: 9,
3170        InvalidStateError: 11,
3171        InvalidAccessError: 15,
3172        TypeError: undefined,
3173        OperationError: undefined
3174      }[name];
3175      return e;
3176    }
3177    
3178    module.exports = function(window, edgeVersion) {
3179      // https://w3c.github.io/mediacapture-main/#mediastream
3180      // Helper function to add the track to the stream and
3181      // dispatch the event ourselves.
3182      function addTrackToStreamAndFireEvent(track, stream) {
3183        stream.addTrack(track);
3184        stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack',
3185            {track: track}));
3186      }
3187    
3188      function removeTrackFromStreamAndFireEvent(track, stream) {
3189        stream.removeTrack(track);
3190        stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack',
3191            {track: track}));
3192      }
3193    
3194      function fireAddTrack(pc, track, receiver, streams) {
3195        var trackEvent = new Event('track');
3196        trackEvent.track = track;
3197        trackEvent.receiver = receiver;
3198        trackEvent.transceiver = {receiver: receiver};
3199        trackEvent.streams = streams;
3200        window.setTimeout(function() {
3201          pc._dispatchEvent('track', trackEvent);
3202        });
3203      }
3204    
3205      var RTCPeerConnection = function(config) {
3206        var pc = this;
3207    
3208        var _eventTarget = document.createDocumentFragment();
3209        ['addEventListener', 'removeEventListener', 'dispatchEvent']
3210            .forEach(function(method) {
3211              pc[method] = _eventTarget[method].bind(_eventTarget);
3212            });
3213    
3214        this.canTrickleIceCandidates = null;
3215    
3216        this.needNegotiation = false;
3217    
3218        this.localStreams = [];
3219        this.remoteStreams = [];
3220    
3221        this._localDescription = null;
3222        this._remoteDescription = null;
3223    
3224        this.signalingState = 'stable';
3225        this.iceConnectionState = 'new';
3226        this.connectionState = 'new';
3227        this.iceGatheringState = 'new';
3228    
3229        config = JSON.parse(JSON.stringify(config || {}));
3230    
3231        this.usingBundle = config.bundlePolicy === 'max-bundle';
3232        if (config.rtcpMuxPolicy === 'negotiate') {
3233          throw(makeError('NotSupportedError',
3234              'rtcpMuxPolicy \'negotiate\' is not supported'));
3235        } else if (!config.rtcpMuxPolicy) {
3236          config.rtcpMuxPolicy = 'require';
3237        }
3238    
3239        switch (config.iceTransportPolicy) {
3240          case 'all':
3241          case 'relay':
3242            break;
3243          default:
3244            config.iceTransportPolicy = 'all';
3245            break;
3246        }
3247    
3248        switch (config.bundlePolicy) {
3249          case 'balanced':
3250          case 'max-compat':
3251          case 'max-bundle':
3252            break;
3253          default:
3254            config.bundlePolicy = 'balanced';
3255            break;
3256        }
3257    
3258        config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);
3259    
3260        this._iceGatherers = [];
3261        if (config.iceCandidatePoolSize) {
3262          for (var i = config.iceCandidatePoolSize; i > 0; i--) {
3263            this._iceGatherers.push(new window.RTCIceGatherer({
3264              iceServers: config.iceServers,
3265              gatherPolicy: config.iceTransportPolicy
3266            }));
3267          }
3268        } else {
3269          config.iceCandidatePoolSize = 0;
3270        }
3271    
3272        this._config = config;
3273    
3274        // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
3275        // everything that is needed to describe a SDP m-line.
3276        this.transceivers = [];
3277    
3278        this._sdpSessionId = SDPUtils.generateSessionId();
3279        this._sdpSessionVersion = 0;
3280    
3281        this._dtlsRole = undefined; // role for a=setup to use in answers.
3282    
3283        this._isClosed = false;
3284      };
3285    
3286      Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {
3287        configurable: true,
3288        get: function() {
3289          return this._localDescription;
3290        }
3291      });
3292      Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {
3293        configurable: true,
3294        get: function() {
3295          return this._remoteDescription;
3296        }
3297      });
3298    
3299      // set up event handlers on prototype
3300      RTCPeerConnection.prototype.onicecandidate = null;
3301      RTCPeerConnection.prototype.onaddstream = null;
3302      RTCPeerConnection.prototype.ontrack = null;
3303      RTCPeerConnection.prototype.onremovestream = null;
3304      RTCPeerConnection.prototype.onsignalingstatechange = null;
3305      RTCPeerConnection.prototype.oniceconnectionstatechange = null;
3306      RTCPeerConnection.prototype.onconnectionstatechange = null;
3307      RTCPeerConnection.prototype.onicegatheringstatechange = null;
3308      RTCPeerConnection.prototype.onnegotiationneeded = null;
3309      RTCPeerConnection.prototype.ondatachannel = null;
3310    
3311      RTCPeerConnection.prototype._dispatchEvent = function(name, event) {
3312        if (this._isClosed) {
3313          return;
3314        }
3315        this.dispatchEvent(event);
3316        if (typeof this['on' + name] === 'function') {
3317          this['on' + name](event);
3318        }
3319      };
3320    
3321      RTCPeerConnection.prototype._emitGatheringStateChange = function() {
3322        var event = new Event('icegatheringstatechange');
3323        this._dispatchEvent('icegatheringstatechange', event);
3324      };
3325    
3326      RTCPeerConnection.prototype.getConfiguration = function() {
3327        return this._config;
3328      };
3329    
3330      RTCPeerConnection.prototype.getLocalStreams = function() {
3331        return this.localStreams;
3332      };
3333    
3334      RTCPeerConnection.prototype.getRemoteStreams = function() {
3335        return this.remoteStreams;
3336      };
3337    
3338      // internal helper to create a transceiver object.
3339      // (which is not yet the same as the WebRTC 1.0 transceiver)
3340      RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) {
3341        var hasBundleTransport = this.transceivers.length > 0;
3342        var transceiver = {
3343          track: null,
3344          iceGatherer: null,
3345          iceTransport: null,
3346          dtlsTransport: null,
3347          localCapabilities: null,
3348          remoteCapabilities: null,
3349          rtpSender: null,
3350          rtpReceiver: null,
3351          kind: kind,
3352          mid: null,
3353          sendEncodingParameters: null,
3354          recvEncodingParameters: null,
3355          stream: null,
3356          associatedRemoteMediaStreams: [],
3357          wantReceive: true
3358        };
3359        if (this.usingBundle && hasBundleTransport) {
3360          transceiver.iceTransport = this.transceivers[0].iceTransport;
3361          transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
3362        } else {
3363          var transports = this._createIceAndDtlsTransports();
3364          transceiver.iceTransport = transports.iceTransport;
3365          transceiver.dtlsTransport = transports.dtlsTransport;
3366        }
3367        if (!doNotAdd) {
3368          this.transceivers.push(transceiver);
3369        }
3370        return transceiver;
3371      };
3372    
3373      RTCPeerConnection.prototype.addTrack = function(track, stream) {
3374        if (this._isClosed) {
3375          throw makeError('InvalidStateError',
3376              'Attempted to call addTrack on a closed peerconnection.');
3377        }
3378    
3379        var alreadyExists = this.transceivers.find(function(s) {
3380          return s.track === track;
3381        });
3382    
3383        if (alreadyExists) {
3384          throw makeError('InvalidAccessError', 'Track already exists.');
3385        }
3386    
3387        var transceiver;
3388        for (var i = 0; i < this.transceivers.length; i++) {
3389          if (!this.transceivers[i].track &&
3390              this.transceivers[i].kind === track.kind) {
3391            transceiver = this.transceivers[i];
3392          }
3393        }
3394        if (!transceiver) {
3395          transceiver = this._createTransceiver(track.kind);
3396        }
3397    
3398        this._maybeFireNegotiationNeeded();
3399    
3400        if (this.localStreams.indexOf(stream) === -1) {
3401          this.localStreams.push(stream);
3402        }
3403    
3404        transceiver.track = track;
3405        transceiver.stream = stream;
3406        transceiver.rtpSender = new window.RTCRtpSender(track,
3407            transceiver.dtlsTransport);
3408        return transceiver.rtpSender;
3409      };
3410    
3411      RTCPeerConnection.prototype.addStream = function(stream) {
3412        var pc = this;
3413        if (edgeVersion >= 15025) {
3414          stream.getTracks().forEach(function(track) {
3415            pc.addTrack(track, stream);
3416          });
3417        } else {
3418          // Clone is necessary for local demos mostly, attaching directly
3419          // to two different senders does not work (build 10547).
3420          // Fixed in 15025 (or earlier)
3421          var clonedStream = stream.clone();
3422          stream.getTracks().forEach(function(track, idx) {
3423            var clonedTrack = clonedStream.getTracks()[idx];
3424            track.addEventListener('enabled', function(event) {
3425              clonedTrack.enabled = event.enabled;
3426            });
3427          });
3428          clonedStream.getTracks().forEach(function(track) {
3429            pc.addTrack(track, clonedStream);
3430          });
3431        }
3432      };
3433    
3434      RTCPeerConnection.prototype.removeTrack = function(sender) {
3435        if (this._isClosed) {
3436          throw makeError('InvalidStateError',
3437              'Attempted to call removeTrack on a closed peerconnection.');
3438        }
3439    
3440        if (!(sender instanceof window.RTCRtpSender)) {
3441          throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +
3442              'does not implement interface RTCRtpSender.');
3443        }
3444    
3445        var transceiver = this.transceivers.find(function(t) {
3446          return t.rtpSender === sender;
3447        });
3448    
3449        if (!transceiver) {
3450          throw makeError('InvalidAccessError',
3451              'Sender was not created by this connection.');
3452        }
3453        var stream = transceiver.stream;
3454    
3455        transceiver.rtpSender.stop();
3456        transceiver.rtpSender = null;
3457        transceiver.track = null;
3458        transceiver.stream = null;
3459    
3460        // remove the stream from the set of local streams
3461        var localStreams = this.transceivers.map(function(t) {
3462          return t.stream;
3463        });
3464        if (localStreams.indexOf(stream) === -1 &&
3465            this.localStreams.indexOf(stream) > -1) {
3466          this.localStreams.splice(this.localStreams.indexOf(stream), 1);
3467        }
3468    
3469        this._maybeFireNegotiationNeeded();
3470      };
3471    
3472      RTCPeerConnection.prototype.removeStream = function(stream) {
3473        var pc = this;
3474        stream.getTracks().forEach(function(track) {
3475          var sender = pc.getSenders().find(function(s) {
3476            return s.track === track;
3477          });
3478          if (sender) {
3479            pc.removeTrack(sender);
3480          }
3481        });
3482      };
3483    
3484      RTCPeerConnection.prototype.getSenders = function() {
3485        return this.transceivers.filter(function(transceiver) {
3486          return !!transceiver.rtpSender;
3487        })
3488        .map(function(transceiver) {
3489          return transceiver.rtpSender;
3490        });
3491      };
3492    
3493      RTCPeerConnection.prototype.getReceivers = function() {
3494        return this.transceivers.filter(function(transceiver) {
3495          return !!transceiver.rtpReceiver;
3496        })
3497        .map(function(transceiver) {
3498          return transceiver.rtpReceiver;
3499        });
3500      };
3501    
3502    
3503      RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,
3504          usingBundle) {
3505        var pc = this;
3506        if (usingBundle && sdpMLineIndex > 0) {
3507          return this.transceivers[0].iceGatherer;
3508        } else if (this._iceGatherers.length) {
3509          return this._iceGatherers.shift();
3510        }
3511        var iceGatherer = new window.RTCIceGatherer({
3512          iceServers: this._config.iceServers,
3513          gatherPolicy: this._config.iceTransportPolicy
3514        });
3515        Object.defineProperty(iceGatherer, 'state',
3516            {value: 'new', writable: true}
3517        );
3518    
3519        this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];
3520        this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {
3521          var end = !event.candidate || Object.keys(event.candidate).length === 0;
3522          // polyfill since RTCIceGatherer.state is not implemented in
3523          // Edge 10547 yet.
3524          iceGatherer.state = end ? 'completed' : 'gathering';
3525          if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {
3526            pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);
3527          }
3528        };
3529        iceGatherer.addEventListener('localcandidate',
3530          this.transceivers[sdpMLineIndex].bufferCandidates);
3531        return iceGatherer;
3532      };
3533    
3534      // start gathering from an RTCIceGatherer.
3535      RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {
3536        var pc = this;
3537        var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
3538        if (iceGatherer.onlocalcandidate) {
3539          return;
3540        }
3541        var bufferedCandidateEvents =
3542          this.transceivers[sdpMLineIndex].bufferedCandidateEvents;
3543        this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;
3544        iceGatherer.removeEventListener('localcandidate',
3545          this.transceivers[sdpMLineIndex].bufferCandidates);
3546        iceGatherer.onlocalcandidate = function(evt) {
3547          if (pc.usingBundle && sdpMLineIndex > 0) {
3548            // if we know that we use bundle we can drop candidates with
3549            // ѕdpMLineIndex > 0. If we don't do this then our state gets
3550            // confused since we dispose the extra ice gatherer.
3551            return;
3552          }
3553          var event = new Event('icecandidate');
3554          event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
3555    
3556          var cand = evt.candidate;
3557          // Edge emits an empty object for RTCIceCandidateComplete‥
3558          var end = !cand || Object.keys(cand).length === 0;
3559          if (end) {
3560            // polyfill since RTCIceGatherer.state is not implemented in
3561            // Edge 10547 yet.
3562            if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {
3563              iceGatherer.state = 'completed';
3564            }
3565          } else {
3566            if (iceGatherer.state === 'new') {
3567              iceGatherer.state = 'gathering';
3568            }
3569            // RTCIceCandidate doesn't have a component, needs to be added
3570            cand.component = 1;
3571            // also the usernameFragment. TODO: update SDP to take both variants.
3572            cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;
3573    
3574            var serializedCandidate = SDPUtils.writeCandidate(cand);
3575            event.candidate = Object.assign(event.candidate,
3576                SDPUtils.parseCandidate(serializedCandidate));
3577    
3578            event.candidate.candidate = serializedCandidate;
3579            event.candidate.toJSON = function() {
3580              return {
3581                candidate: event.candidate.candidate,
3582                sdpMid: event.candidate.sdpMid,
3583                sdpMLineIndex: event.candidate.sdpMLineIndex,
3584                usernameFragment: event.candidate.usernameFragment
3585              };
3586            };
3587          }
3588    
3589          // update local description.
3590          var sections = SDPUtils.getMediaSections(pc._localDescription.sdp);
3591          if (!end) {
3592            sections[event.candidate.sdpMLineIndex] +=
3593                'a=' + event.candidate.candidate + '\r\n';
3594          } else {
3595            sections[event.candidate.sdpMLineIndex] +=
3596                'a=end-of-candidates\r\n';
3597          }
3598          pc._localDescription.sdp =
3599              SDPUtils.getDescription(pc._localDescription.sdp) +
3600              sections.join('');
3601          var complete = pc.transceivers.every(function(transceiver) {
3602            return transceiver.iceGatherer &&
3603                transceiver.iceGatherer.state === 'completed';
3604          });
3605    
3606          if (pc.iceGatheringState !== 'gathering') {
3607            pc.iceGatheringState = 'gathering';
3608            pc._emitGatheringStateChange();
3609          }
3610    
3611          // Emit candidate. Also emit null candidate when all gatherers are
3612          // complete.
3613          if (!end) {
3614            pc._dispatchEvent('icecandidate', event);
3615          }
3616          if (complete) {
3617            pc._dispatchEvent('icecandidate', new Event('icecandidate'));
3618            pc.iceGatheringState = 'complete';
3619            pc._emitGatheringStateChange();
3620          }
3621        };
3622    
3623        // emit already gathered candidates.
3624        window.setTimeout(function() {
3625          bufferedCandidateEvents.forEach(function(e) {
3626            iceGatherer.onlocalcandidate(e);
3627          });
3628        }, 0);
3629      };
3630    
3631      // Create ICE transport and DTLS transport.
3632      RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
3633        var pc = this;
3634        var iceTransport = new window.RTCIceTransport(null);
3635        iceTransport.onicestatechange = function() {
3636          pc._updateIceConnectionState();
3637          pc._updateConnectionState();
3638        };
3639    
3640        var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
3641        dtlsTransport.ondtlsstatechange = function() {
3642          pc._updateConnectionState();
3643        };
3644        dtlsTransport.onerror = function() {
3645          // onerror does not set state to failed by itself.
3646          Object.defineProperty(dtlsTransport, 'state',
3647              {value: 'failed', writable: true});
3648          pc._updateConnectionState();
3649        };
3650    
3651        return {
3652          iceTransport: iceTransport,
3653          dtlsTransport: dtlsTransport
3654        };
3655      };
3656    
3657      // Destroy ICE gatherer, ICE transport and DTLS transport.
3658      // Without triggering the callbacks.
3659      RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
3660          sdpMLineIndex) {
3661        var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
3662        if (iceGatherer) {
3663          delete iceGatherer.onlocalcandidate;
3664          delete this.transceivers[sdpMLineIndex].iceGatherer;
3665        }
3666        var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
3667        if (iceTransport) {
3668          delete iceTransport.onicestatechange;
3669          delete this.transceivers[sdpMLineIndex].iceTransport;
3670        }
3671        var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
3672        if (dtlsTransport) {
3673          delete dtlsTransport.ondtlsstatechange;
3674          delete dtlsTransport.onerror;
3675          delete this.transceivers[sdpMLineIndex].dtlsTransport;
3676        }
3677      };
3678    
3679      // Start the RTP Sender and Receiver for a transceiver.
3680      RTCPeerConnection.prototype._transceive = function(transceiver,
3681          send, recv) {
3682        var params = getCommonCapabilities(transceiver.localCapabilities,
3683            transceiver.remoteCapabilities);
3684        if (send && transceiver.rtpSender) {
3685          params.encodings = transceiver.sendEncodingParameters;
3686          params.rtcp = {
3687            cname: SDPUtils.localCName,
3688            compound: transceiver.rtcpParameters.compound
3689          };
3690          if (transceiver.recvEncodingParameters.length) {
3691            params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
3692          }
3693          transceiver.rtpSender.send(params);
3694        }
3695        if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {
3696          // remove RTX field in Edge 14942
3697          if (transceiver.kind === 'video'
3698              && transceiver.recvEncodingParameters
3699              && edgeVersion < 15019) {
3700            transceiver.recvEncodingParameters.forEach(function(p) {
3701              delete p.rtx;
3702            });
3703          }
3704          if (transceiver.recvEncodingParameters.length) {
3705            params.encodings = transceiver.recvEncodingParameters;
3706          } else {
3707            params.encodings = [{}];
3708          }
3709          params.rtcp = {
3710            compound: transceiver.rtcpParameters.compound
3711          };
3712          if (transceiver.rtcpParameters.cname) {
3713            params.rtcp.cname = transceiver.rtcpParameters.cname;
3714          }
3715          if (transceiver.sendEncodingParameters.length) {
3716            params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
3717          }
3718          transceiver.rtpReceiver.receive(params);
3719        }
3720      };
3721    
3722      RTCPeerConnection.prototype.setLocalDescription = function(description) {
3723        var pc = this;
3724    
3725        // Note: pranswer is not supported.
3726        if (['offer', 'answer'].indexOf(description.type) === -1) {
3727          return Promise.reject(makeError('TypeError',
3728              'Unsupported type "' + description.type + '"'));
3729        }
3730    
3731        if (!isActionAllowedInSignalingState('setLocalDescription',
3732            description.type, pc.signalingState) || pc._isClosed) {
3733          return Promise.reject(makeError('InvalidStateError',
3734              'Can not set local ' + description.type +
3735              ' in state ' + pc.signalingState));
3736        }
3737    
3738        var sections;
3739        var sessionpart;
3740        if (description.type === 'offer') {
3741          // VERY limited support for SDP munging. Limited to:
3742          // * changing the order of codecs
3743          sections = SDPUtils.splitSections(description.sdp);
3744          sessionpart = sections.shift();
3745          sections.forEach(function(mediaSection, sdpMLineIndex) {
3746            var caps = SDPUtils.parseRtpParameters(mediaSection);
3747            pc.transceivers[sdpMLineIndex].localCapabilities = caps;
3748          });
3749    
3750          pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
3751            pc._gather(transceiver.mid, sdpMLineIndex);
3752          });
3753        } else if (description.type === 'answer') {
3754          sections = SDPUtils.splitSections(pc._remoteDescription.sdp);
3755          sessionpart = sections.shift();
3756          var isIceLite = SDPUtils.matchPrefix(sessionpart,
3757              'a=ice-lite').length > 0;
3758          sections.forEach(function(mediaSection, sdpMLineIndex) {
3759            var transceiver = pc.transceivers[sdpMLineIndex];
3760            var iceGatherer = transceiver.iceGatherer;
3761            var iceTransport = transceiver.iceTransport;
3762            var dtlsTransport = transceiver.dtlsTransport;
3763            var localCapabilities = transceiver.localCapabilities;
3764            var remoteCapabilities = transceiver.remoteCapabilities;
3765    
3766            // treat bundle-only as not-rejected.
3767            var rejected = SDPUtils.isRejected(mediaSection) &&
3768                SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
3769    
3770            if (!rejected && !transceiver.rejected) {
3771              var remoteIceParameters = SDPUtils.getIceParameters(
3772                  mediaSection, sessionpart);
3773              var remoteDtlsParameters = SDPUtils.getDtlsParameters(
3774                  mediaSection, sessionpart);
3775              if (isIceLite) {
3776                remoteDtlsParameters.role = 'server';
3777              }
3778    
3779              if (!pc.usingBundle || sdpMLineIndex === 0) {
3780                pc._gather(transceiver.mid, sdpMLineIndex);
3781                if (iceTransport.state === 'new') {
3782                  iceTransport.start(iceGatherer, remoteIceParameters,
3783                      isIceLite ? 'controlling' : 'controlled');
3784                }
3785                if (dtlsTransport.state === 'new') {
3786                  dtlsTransport.start(remoteDtlsParameters);
3787                }
3788              }
3789    
3790              // Calculate intersection of capabilities.
3791              var params = getCommonCapabilities(localCapabilities,
3792                  remoteCapabilities);
3793    
3794              // Start the RTCRtpSender. The RTCRtpReceiver for this
3795              // transceiver has already been started in setRemoteDescription.
3796              pc._transceive(transceiver,
3797                  params.codecs.length > 0,
3798                  false);
3799            }
3800          });
3801        }
3802    
3803        pc._localDescription = {
3804          type: description.type,
3805          sdp: description.sdp
3806        };
3807        if (description.type === 'offer') {
3808          pc._updateSignalingState('have-local-offer');
3809        } else {
3810          pc._updateSignalingState('stable');
3811        }
3812    
3813        return Promise.resolve();
3814      };
3815    
3816      RTCPeerConnection.prototype.setRemoteDescription = function(description) {
3817        var pc = this;
3818    
3819        // Note: pranswer is not supported.
3820        if (['offer', 'answer'].indexOf(description.type) === -1) {
3821          return Promise.reject(makeError('TypeError',
3822              'Unsupported type "' + description.type + '"'));
3823        }
3824    
3825        if (!isActionAllowedInSignalingState('setRemoteDescription',
3826            description.type, pc.signalingState) || pc._isClosed) {
3827          return Promise.reject(makeError('InvalidStateError',
3828              'Can not set remote ' + description.type +
3829              ' in state ' + pc.signalingState));
3830        }
3831    
3832        var streams = {};
3833        pc.remoteStreams.forEach(function(stream) {
3834          streams[stream.id] = stream;
3835        });
3836        var receiverList = [];
3837        var sections = SDPUtils.splitSections(description.sdp);
3838        var sessionpart = sections.shift();
3839        var isIceLite = SDPUtils.matchPrefix(sessionpart,
3840            'a=ice-lite').length > 0;
3841        var usingBundle = SDPUtils.matchPrefix(sessionpart,
3842            'a=group:BUNDLE ').length > 0;
3843        pc.usingBundle = usingBundle;
3844        var iceOptions = SDPUtils.matchPrefix(sessionpart,
3845            'a=ice-options:')[0];
3846        if (iceOptions) {
3847          pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
3848              .indexOf('trickle') >= 0;
3849        } else {
3850          pc.canTrickleIceCandidates = false;
3851        }
3852    
3853        sections.forEach(function(mediaSection, sdpMLineIndex) {
3854          var lines = SDPUtils.splitLines(mediaSection);
3855          var kind = SDPUtils.getKind(mediaSection);
3856          // treat bundle-only as not-rejected.
3857          var rejected = SDPUtils.isRejected(mediaSection) &&
3858              SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
3859          var protocol = lines[0].substr(2).split(' ')[2];
3860    
3861          var direction = SDPUtils.getDirection(mediaSection, sessionpart);
3862          var remoteMsid = SDPUtils.parseMsid(mediaSection);
3863    
3864          var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();
3865    
3866          // Reject datachannels which are not implemented yet.
3867          if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' ||
3868              protocol === 'UDP/DTLS/SCTP'))) {
3869            // TODO: this is dangerous in the case where a non-rejected m-line
3870            //     becomes rejected.
3871            pc.transceivers[sdpMLineIndex] = {
3872              mid: mid,
3873              kind: kind,
3874              protocol: protocol,
3875              rejected: true
3876            };
3877            return;
3878          }
3879    
3880          if (!rejected && pc.transceivers[sdpMLineIndex] &&
3881              pc.transceivers[sdpMLineIndex].rejected) {
3882            // recycle a rejected transceiver.
3883            pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);
3884          }
3885    
3886          var transceiver;
3887          var iceGatherer;
3888          var iceTransport;
3889          var dtlsTransport;
3890          var rtpReceiver;
3891          var sendEncodingParameters;
3892          var recvEncodingParameters;
3893          var localCapabilities;
3894    
3895          var track;
3896          // FIXME: ensure the mediaSection has rtcp-mux set.
3897          var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
3898          var remoteIceParameters;
3899          var remoteDtlsParameters;
3900          if (!rejected) {
3901            remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
3902                sessionpart);
3903            remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
3904                sessionpart);
3905            remoteDtlsParameters.role = 'client';
3906          }
3907          recvEncodingParameters =
3908              SDPUtils.parseRtpEncodingParameters(mediaSection);
3909    
3910          var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
3911    
3912          var isComplete = SDPUtils.matchPrefix(mediaSection,
3913              'a=end-of-candidates', sessionpart).length > 0;
3914          var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
3915              .map(function(cand) {
3916                return SDPUtils.parseCandidate(cand);
3917              })
3918              .filter(function(cand) {
3919                return cand.component === 1;
3920              });
3921    
3922          // Check if we can use BUNDLE and dispose transports.
3923          if ((description.type === 'offer' || description.type === 'answer') &&
3924              !rejected && usingBundle && sdpMLineIndex > 0 &&
3925              pc.transceivers[sdpMLineIndex]) {
3926            pc._disposeIceAndDtlsTransports(sdpMLineIndex);
3927            pc.transceivers[sdpMLineIndex].iceGatherer =
3928                pc.transceivers[0].iceGatherer;
3929            pc.transceivers[sdpMLineIndex].iceTransport =
3930                pc.transceivers[0].iceTransport;
3931            pc.transceivers[sdpMLineIndex].dtlsTransport =
3932                pc.transceivers[0].dtlsTransport;
3933            if (pc.transceivers[sdpMLineIndex].rtpSender) {
3934              pc.transceivers[sdpMLineIndex].rtpSender.setTransport(
3935                  pc.transceivers[0].dtlsTransport);
3936            }
3937            if (pc.transceivers[sdpMLineIndex].rtpReceiver) {
3938              pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
3939                  pc.transceivers[0].dtlsTransport);
3940            }
3941          }
3942          if (description.type === 'offer' && !rejected) {
3943            transceiver = pc.transceivers[sdpMLineIndex] ||
3944                pc._createTransceiver(kind);
3945            transceiver.mid = mid;
3946    
3947            if (!transceiver.iceGatherer) {
3948              transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
3949                  usingBundle);
3950            }
3951    
3952            if (cands.length && transceiver.iceTransport.state === 'new') {
3953              if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
3954                transceiver.iceTransport.setRemoteCandidates(cands);
3955              } else {
3956                cands.forEach(function(candidate) {
3957                  maybeAddCandidate(transceiver.iceTransport, candidate);
3958                });
3959              }
3960            }
3961    
3962            localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
3963    
3964            // filter RTX until additional stuff needed for RTX is implemented
3965            // in adapter.js
3966            if (edgeVersion < 15019) {
3967              localCapabilities.codecs = localCapabilities.codecs.filter(
3968                  function(codec) {
3969                    return codec.name !== 'rtx';
3970                  });
3971            }
3972    
3973            sendEncodingParameters = transceiver.sendEncodingParameters || [{
3974              ssrc: (2 * sdpMLineIndex + 2) * 1001
3975            }];
3976    
3977            // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
3978            var isNewTrack = false;
3979            if (direction === 'sendrecv' || direction === 'sendonly') {
3980              isNewTrack = !transceiver.rtpReceiver;
3981              rtpReceiver = transceiver.rtpReceiver ||
3982                  new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);
3983    
3984              if (isNewTrack) {
3985                var stream;
3986                track = rtpReceiver.track;
3987                // FIXME: does not work with Plan B.
3988                if (remoteMsid && remoteMsid.stream === '-') {
3989                  // no-op. a stream id of '-' means: no associated stream.
3990                } else if (remoteMsid) {
3991                  if (!streams[remoteMsid.stream]) {
3992                    streams[remoteMsid.stream] = new window.MediaStream();
3993                    Object.defineProperty(streams[remoteMsid.stream], 'id', {
3994                      get: function() {
3995                        return remoteMsid.stream;
3996                      }
3997                    });
3998                  }
3999                  Object.defineProperty(track, 'id', {
4000                    get: function() {
4001                      return remoteMsid.track;
4002                    }
4003                  });
4004                  stream = streams[remoteMsid.stream];
4005                } else {
4006                  if (!streams.default) {
4007                    streams.default = new window.MediaStream();
4008                  }
4009                  stream = streams.default;
4010                }
4011                if (stream) {
4012                  addTrackToStreamAndFireEvent(track, stream);
4013                  transceiver.associatedRemoteMediaStreams.push(stream);
4014                }
4015                receiverList.push([track, rtpReceiver, stream]);
4016              }
4017            } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {
4018              transceiver.associatedRemoteMediaStreams.forEach(function(s) {
4019                var nativeTrack = s.getTracks().find(function(t) {
4020                  return t.id === transceiver.rtpReceiver.track.id;
4021                });
4022                if (nativeTrack) {
4023                  removeTrackFromStreamAndFireEvent(nativeTrack, s);
4024                }
4025              });
4026              transceiver.associatedRemoteMediaStreams = [];
4027            }
4028    
4029            transceiver.localCapabilities = localCapabilities;
4030            transceiver.remoteCapabilities = remoteCapabilities;
4031            transceiver.rtpReceiver = rtpReceiver;
4032            transceiver.rtcpParameters = rtcpParameters;
4033            transceiver.sendEncodingParameters = sendEncodingParameters;
4034            transceiver.recvEncodingParameters = recvEncodingParameters;
4035    
4036            // Start the RTCRtpReceiver now. The RTPSender is started in
4037            // setLocalDescription.
4038            pc._transceive(pc.transceivers[sdpMLineIndex],
4039                false,
4040                isNewTrack);
4041          } else if (description.type === 'answer' && !rejected) {
4042            transceiver = pc.transceivers[sdpMLineIndex];
4043            iceGatherer = transceiver.iceGatherer;
4044            iceTransport = transceiver.iceTransport;
4045            dtlsTransport = transceiver.dtlsTransport;
4046            rtpReceiver = transceiver.rtpReceiver;
4047            sendEncodingParameters = transceiver.sendEncodingParameters;
4048            localCapabilities = transceiver.localCapabilities;
4049    
4050            pc.transceivers[sdpMLineIndex].recvEncodingParameters =
4051                recvEncodingParameters;
4052            pc.transceivers[sdpMLineIndex].remoteCapabilities =
4053                remoteCapabilities;
4054            pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
4055    
4056            if (cands.length && iceTransport.state === 'new') {
4057              if ((isIceLite || isComplete) &&
4058                  (!usingBundle || sdpMLineIndex === 0)) {
4059                iceTransport.setRemoteCandidates(cands);
4060              } else {
4061                cands.forEach(function(candidate) {
4062                  maybeAddCandidate(transceiver.iceTransport, candidate);
4063                });
4064              }
4065            }
4066    
4067            if (!usingBundle || sdpMLineIndex === 0) {
4068              if (iceTransport.state === 'new') {
4069                iceTransport.start(iceGatherer, remoteIceParameters,
4070                    'controlling');
4071              }
4072              if (dtlsTransport.state === 'new') {
4073                dtlsTransport.start(remoteDtlsParameters);
4074              }
4075            }
4076    
4077            // If the offer contained RTX but the answer did not,
4078            // remove RTX from sendEncodingParameters.
4079            var commonCapabilities = getCommonCapabilities(
4080              transceiver.localCapabilities,
4081              transceiver.remoteCapabilities);
4082    
4083            var hasRtx = commonCapabilities.codecs.filter(function(c) {
4084              return c.name.toLowerCase() === 'rtx';
4085            }).length;
4086            if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
4087              delete transceiver.sendEncodingParameters[0].rtx;
4088            }
4089    
4090            pc._transceive(transceiver,
4091                direction === 'sendrecv' || direction === 'recvonly',
4092                direction === 'sendrecv' || direction === 'sendonly');
4093    
4094            // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
4095            if (rtpReceiver &&
4096                (direction === 'sendrecv' || direction === 'sendonly')) {
4097              track = rtpReceiver.track;
4098              if (remoteMsid) {
4099                if (!streams[remoteMsid.stream]) {
4100                  streams[remoteMsid.stream] = new window.MediaStream();
4101                }
4102                addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);
4103                receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
4104              } else {
4105                if (!streams.default) {
4106                  streams.default = new window.MediaStream();
4107                }
4108                addTrackToStreamAndFireEvent(track, streams.default);
4109                receiverList.push([track, rtpReceiver, streams.default]);
4110              }
4111            } else {
4112              // FIXME: actually the receiver should be created later.
4113              delete transceiver.rtpReceiver;
4114            }
4115          }
4116        });
4117    
4118        if (pc._dtlsRole === undefined) {
4119          pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';
4120        }
4121    
4122        pc._remoteDescription = {
4123          type: description.type,
4124          sdp: description.sdp
4125        };
4126        if (description.type === 'offer') {
4127          pc._updateSignalingState('have-remote-offer');
4128        } else {
4129          pc._updateSignalingState('stable');
4130        }
4131        Object.keys(streams).forEach(function(sid) {
4132          var stream = streams[sid];
4133          if (stream.getTracks().length) {
4134            if (pc.remoteStreams.indexOf(stream) === -1) {
4135              pc.remoteStreams.push(stream);
4136              var event = new Event('addstream');
4137              event.stream = stream;
4138              window.setTimeout(function() {
4139                pc._dispatchEvent('addstream', event);
4140              });
4141            }
4142    
4143            receiverList.forEach(function(item) {
4144              var track = item[0];
4145              var receiver = item[1];
4146              if (stream.id !== item[2].id) {
4147                return;
4148              }
4149              fireAddTrack(pc, track, receiver, [stream]);
4150            });
4151          }
4152        });
4153        receiverList.forEach(function(item) {
4154          if (item[2]) {
4155            return;
4156          }
4157          fireAddTrack(pc, item[0], item[1], []);
4158        });
4159    
4160        // check whether addIceCandidate({}) was called within four seconds after
4161        // setRemoteDescription.
4162        window.setTimeout(function() {
4163          if (!(pc && pc.transceivers)) {
4164            return;
4165          }
4166          pc.transceivers.forEach(function(transceiver) {
4167            if (transceiver.iceTransport &&
4168                transceiver.iceTransport.state === 'new' &&
4169                transceiver.iceTransport.getRemoteCandidates().length > 0) {
4170              console.warn('Timeout for addRemoteCandidate. Consider sending ' +
4171                  'an end-of-candidates notification');
4172              transceiver.iceTransport.addRemoteCandidate({});
4173            }
4174          });
4175        }, 4000);
4176    
4177        return Promise.resolve();
4178      };
4179    
4180      RTCPeerConnection.prototype.close = function() {
4181        this.transceivers.forEach(function(transceiver) {
4182          /* not yet
4183          if (transceiver.iceGatherer) {
4184            transceiver.iceGatherer.close();
4185          }
4186          */
4187          if (transceiver.iceTransport) {
4188            transceiver.iceTransport.stop();
4189          }
4190          if (transceiver.dtlsTransport) {
4191            transceiver.dtlsTransport.stop();
4192          }
4193          if (transceiver.rtpSender) {
4194            transceiver.rtpSender.stop();
4195          }
4196          if (transceiver.rtpReceiver) {
4197            transceiver.rtpReceiver.stop();
4198          }
4199        });
4200        // FIXME: clean up tracks, local streams, remote streams, etc
4201        this._isClosed = true;
4202        this._updateSignalingState('closed');
4203      };
4204    
4205      // Update the signaling state.
4206      RTCPeerConnection.prototype._updateSignalingState = function(newState) {
4207        this.signalingState = newState;
4208        var event = new Event('signalingstatechange');
4209        this._dispatchEvent('signalingstatechange', event);
4210      };
4211    
4212      // Determine whether to fire the negotiationneeded event.
4213      RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
4214        var pc = this;
4215        if (this.signalingState !== 'stable' || this.needNegotiation === true) {
4216          return;
4217        }
4218        this.needNegotiation = true;
4219        window.setTimeout(function() {
4220          if (pc.needNegotiation) {
4221            pc.needNegotiation = false;
4222            var event = new Event('negotiationneeded');
4223            pc._dispatchEvent('negotiationneeded', event);
4224          }
4225        }, 0);
4226      };
4227    
4228      // Update the ice connection state.
4229      RTCPeerConnection.prototype._updateIceConnectionState = function() {
4230        var newState;
4231        var states = {
4232          'new': 0,
4233          closed: 0,
4234          checking: 0,
4235          connected: 0,
4236          completed: 0,
4237          disconnected: 0,
4238          failed: 0
4239        };
4240        this.transceivers.forEach(function(transceiver) {
4241          if (transceiver.iceTransport && !transceiver.rejected) {
4242            states[transceiver.iceTransport.state]++;
4243          }
4244        });
4245    
4246        newState = 'new';
4247        if (states.failed > 0) {
4248          newState = 'failed';
4249        } else if (states.checking > 0) {
4250          newState = 'checking';
4251        } else if (states.disconnected > 0) {
4252          newState = 'disconnected';
4253        } else if (states.new > 0) {
4254          newState = 'new';
4255        } else if (states.connected > 0) {
4256          newState = 'connected';
4257        } else if (states.completed > 0) {
4258          newState = 'completed';
4259        }
4260    
4261        if (newState !== this.iceConnectionState) {
4262          this.iceConnectionState = newState;
4263          var event = new Event('iceconnectionstatechange');
4264          this._dispatchEvent('iceconnectionstatechange', event);
4265        }
4266      };
4267    
4268      // Update the connection state.
4269      RTCPeerConnection.prototype._updateConnectionState = function() {
4270        var newState;
4271        var states = {
4272          'new': 0,
4273          closed: 0,
4274          connecting: 0,
4275          connected: 0,
4276          completed: 0,
4277          disconnected: 0,
4278          failed: 0
4279        };
4280        this.transceivers.forEach(function(transceiver) {
4281          if (transceiver.iceTransport && transceiver.dtlsTransport &&
4282              !transceiver.rejected) {
4283            states[transceiver.iceTransport.state]++;
4284            states[transceiver.dtlsTransport.state]++;
4285          }
4286        });
4287        // ICETransport.completed and connected are the same for this purpose.
4288        states.connected += states.completed;
4289    
4290        newState = 'new';
4291        if (states.failed > 0) {
4292          newState = 'failed';
4293        } else if (states.connecting > 0) {
4294          newState = 'connecting';
4295        } else if (states.disconnected > 0) {
4296          newState = 'disconnected';
4297        } else if (states.new > 0) {
4298          newState = 'new';
4299        } else if (states.connected > 0) {
4300          newState = 'connected';
4301        }
4302    
4303        if (newState !== this.connectionState) {
4304          this.connectionState = newState;
4305          var event = new Event('connectionstatechange');
4306          this._dispatchEvent('connectionstatechange', event);
4307        }
4308      };
4309    
4310      RTCPeerConnection.prototype.createOffer = function() {
4311        var pc = this;
4312    
4313        if (pc._isClosed) {
4314          return Promise.reject(makeError('InvalidStateError',
4315              'Can not call createOffer after close'));
4316        }
4317    
4318        var numAudioTracks = pc.transceivers.filter(function(t) {
4319          return t.kind === 'audio';
4320        }).length;
4321        var numVideoTracks = pc.transceivers.filter(function(t) {
4322          return t.kind === 'video';
4323        }).length;
4324    
4325        // Determine number of audio and video tracks we need to send/recv.
4326        var offerOptions = arguments[0];
4327        if (offerOptions) {
4328          // Reject Chrome legacy constraints.
4329          if (offerOptions.mandatory || offerOptions.optional) {
4330            throw new TypeError(
4331                'Legacy mandatory/optional constraints not supported.');
4332          }
4333          if (offerOptions.offerToReceiveAudio !== undefined) {
4334            if (offerOptions.offerToReceiveAudio === true) {
4335              numAudioTracks = 1;
4336            } else if (offerOptions.offerToReceiveAudio === false) {
4337              numAudioTracks = 0;
4338            } else {
4339              numAudioTracks = offerOptions.offerToReceiveAudio;
4340            }
4341          }
4342          if (offerOptions.offerToReceiveVideo !== undefined) {
4343            if (offerOptions.offerToReceiveVideo === true) {
4344              numVideoTracks = 1;
4345            } else if (offerOptions.offerToReceiveVideo === false) {
4346              numVideoTracks = 0;
4347            } else {
4348              numVideoTracks = offerOptions.offerToReceiveVideo;
4349            }
4350          }
4351        }
4352    
4353        pc.transceivers.forEach(function(transceiver) {
4354          if (transceiver.kind === 'audio') {
4355            numAudioTracks--;
4356            if (numAudioTracks < 0) {
4357              transceiver.wantReceive = false;
4358            }
4359          } else if (transceiver.kind === 'video') {
4360            numVideoTracks--;
4361            if (numVideoTracks < 0) {
4362              transceiver.wantReceive = false;
4363            }
4364          }
4365        });
4366    
4367        // Create M-lines for recvonly streams.
4368        while (numAudioTracks > 0 || numVideoTracks > 0) {
4369          if (numAudioTracks > 0) {
4370            pc._createTransceiver('audio');
4371            numAudioTracks--;
4372          }
4373          if (numVideoTracks > 0) {
4374            pc._createTransceiver('video');
4375            numVideoTracks--;
4376          }
4377        }
4378    
4379        var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,
4380            pc._sdpSessionVersion++);
4381        pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
4382          // For each track, create an ice gatherer, ice transport,
4383          // dtls transport, potentially rtpsender and rtpreceiver.
4384          var track = transceiver.track;
4385          var kind = transceiver.kind;
4386          var mid = transceiver.mid || SDPUtils.generateIdentifier();
4387          transceiver.mid = mid;
4388    
4389          if (!transceiver.iceGatherer) {
4390            transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
4391                pc.usingBundle);
4392          }
4393    
4394          var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
4395          // filter RTX until additional stuff needed for RTX is implemented
4396          // in adapter.js
4397          if (edgeVersion < 15019) {
4398            localCapabilities.codecs = localCapabilities.codecs.filter(
4399                function(codec) {
4400                  return codec.name !== 'rtx';
4401                });
4402          }
4403          localCapabilities.codecs.forEach(function(codec) {
4404            // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
4405            // by adding level-asymmetry-allowed=1
4406            if (codec.name === 'H264' &&
4407                codec.parameters['level-asymmetry-allowed'] === undefined) {
4408              codec.parameters['level-asymmetry-allowed'] = '1';
4409            }
4410    
4411            // for subsequent offers, we might have to re-use the payload
4412            // type of the last offer.
4413            if (transceiver.remoteCapabilities &&
4414                transceiver.remoteCapabilities.codecs) {
4415              transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) {
4416                if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() &&
4417                    codec.clockRate === remoteCodec.clockRate) {
4418                  codec.preferredPayloadType = remoteCodec.payloadType;
4419                }
4420              });
4421            }
4422          });
4423          localCapabilities.headerExtensions.forEach(function(hdrExt) {
4424            var remoteExtensions = transceiver.remoteCapabilities &&
4425                transceiver.remoteCapabilities.headerExtensions || [];
4426            remoteExtensions.forEach(function(rHdrExt) {
4427              if (hdrExt.uri === rHdrExt.uri) {
4428                hdrExt.id = rHdrExt.id;
4429              }
4430            });
4431          });
4432    
4433          // generate an ssrc now, to be used later in rtpSender.send
4434          var sendEncodingParameters = transceiver.sendEncodingParameters || [{
4435            ssrc: (2 * sdpMLineIndex + 1) * 1001
4436          }];
4437          if (track) {
4438            // add RTX
4439            if (edgeVersion >= 15019 && kind === 'video' &&
4440                !sendEncodingParameters[0].rtx) {
4441              sendEncodingParameters[0].rtx = {
4442                ssrc: sendEncodingParameters[0].ssrc + 1
4443              };
4444            }
4445          }
4446    
4447          if (transceiver.wantReceive) {
4448            transceiver.rtpReceiver = new window.RTCRtpReceiver(
4449                transceiver.dtlsTransport, kind);
4450          }
4451    
4452          transceiver.localCapabilities = localCapabilities;
4453          transceiver.sendEncodingParameters = sendEncodingParameters;
4454        });
4455    
4456        // always offer BUNDLE and dispose on return if not supported.
4457        if (pc._config.bundlePolicy !== 'max-compat') {
4458          sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {
4459            return t.mid;
4460          }).join(' ') + '\r\n';
4461        }
4462        sdp += 'a=ice-options:trickle\r\n';
4463    
4464        pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
4465          sdp += writeMediaSection(transceiver, transceiver.localCapabilities,
4466              'offer', transceiver.stream, pc._dtlsRole);
4467          sdp += 'a=rtcp-rsize\r\n';
4468    
4469          if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&
4470              (sdpMLineIndex === 0 || !pc.usingBundle)) {
4471            transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {
4472              cand.component = 1;
4473              sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\r\n';
4474            });
4475    
4476            if (transceiver.iceGatherer.state === 'completed') {
4477              sdp += 'a=end-of-candidates\r\n';
4478            }
4479          }
4480        });
4481    
4482        var desc = new window.RTCSessionDescription({
4483          type: 'offer',
4484          sdp: sdp
4485        });
4486        return Promise.resolve(desc);
4487      };
4488    
4489      RTCPeerConnection.prototype.createAnswer = function() {
4490        var pc = this;
4491    
4492        if (pc._isClosed) {
4493          return Promise.reject(makeError('InvalidStateError',
4494              'Can not call createAnswer after close'));
4495        }
4496    
4497        if (!(pc.signalingState === 'have-remote-offer' ||
4498            pc.signalingState === 'have-local-pranswer')) {
4499          return Promise.reject(makeError('InvalidStateError',
4500              'Can not call createAnswer in signalingState ' + pc.signalingState));
4501        }
4502    
4503        var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,
4504            pc._sdpSessionVersion++);
4505        if (pc.usingBundle) {
4506          sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {
4507            return t.mid;
4508          }).join(' ') + '\r\n';
4509        }
4510        sdp += 'a=ice-options:trickle\r\n';
4511    
4512        var mediaSectionsInOffer = SDPUtils.getMediaSections(
4513            pc._remoteDescription.sdp).length;
4514        pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
4515          if (sdpMLineIndex + 1 > mediaSectionsInOffer) {
4516            return;
4517          }
4518          if (transceiver.rejected) {
4519            if (transceiver.kind === 'application') {
4520              if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt
4521                sdp += 'm=application 0 DTLS/SCTP 5000\r\n';
4522              } else {
4523                sdp += 'm=application 0 ' + transceiver.protocol +
4524                    ' webrtc-datachannel\r\n';
4525              }
4526            } else if (transceiver.kind === 'audio') {
4527              sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' +
4528                  'a=rtpmap:0 PCMU/8000\r\n';
4529            } else if (transceiver.kind === 'video') {
4530              sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' +
4531                  'a=rtpmap:120 VP8/90000\r\n';
4532            }
4533            sdp += 'c=IN IP4 0.0.0.0\r\n' +
4534                'a=inactive\r\n' +
4535                'a=mid:' + transceiver.mid + '\r\n';
4536            return;
4537          }
4538    
4539          // FIXME: look at direction.
4540          if (transceiver.stream) {
4541            var localTrack;
4542            if (transceiver.kind === 'audio') {
4543              localTrack = transceiver.stream.getAudioTracks()[0];
4544            } else if (transceiver.kind === 'video') {
4545              localTrack = transceiver.stream.getVideoTracks()[0];
4546            }
4547            if (localTrack) {
4548              // add RTX
4549              if (edgeVersion >= 15019 && transceiver.kind === 'video' &&
4550                  !transceiver.sendEncodingParameters[0].rtx) {
4551                transceiver.sendEncodingParameters[0].rtx = {
4552                  ssrc: transceiver.sendEncodingParameters[0].ssrc + 1
4553                };
4554              }
4555            }
4556          }
4557    
4558          // Calculate intersection of capabilities.
4559          var commonCapabilities = getCommonCapabilities(
4560              transceiver.localCapabilities,
4561              transceiver.remoteCapabilities);
4562    
4563          var hasRtx = commonCapabilities.codecs.filter(function(c) {
4564            return c.name.toLowerCase() === 'rtx';
4565          }).length;
4566          if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
4567            delete transceiver.sendEncodingParameters[0].rtx;
4568          }
4569    
4570          sdp += writeMediaSection(transceiver, commonCapabilities,
4571              'answer', transceiver.stream, pc._dtlsRole);
4572          if (transceiver.rtcpParameters &&
4573              transceiver.rtcpParameters.reducedSize) {
4574            sdp += 'a=rtcp-rsize\r\n';
4575          }
4576        });
4577    
4578        var desc = new window.RTCSessionDescription({
4579          type: 'answer',
4580          sdp: sdp
4581        });
4582        return Promise.resolve(desc);
4583      };
4584    
4585      RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
4586        var pc = this;
4587        var sections;
4588        if (candidate && !(candidate.sdpMLineIndex !== undefined ||
4589            candidate.sdpMid)) {
4590          return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));
4591        }
4592    
4593        // TODO: needs to go into ops queue.
4594        return new Promise(function(resolve, reject) {
4595          if (!pc._remoteDescription) {
4596            return reject(makeError('InvalidStateError',
4597                'Can not add ICE candidate without a remote description'));
4598          } else if (!candidate || candidate.candidate === '') {
4599            for (var j = 0; j < pc.transceivers.length; j++) {
4600              if (pc.transceivers[j].rejected) {
4601                continue;
4602              }
4603              pc.transceivers[j].iceTransport.addRemoteCandidate({});
4604              sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);
4605              sections[j] += 'a=end-of-candidates\r\n';
4606              pc._remoteDescription.sdp =
4607                  SDPUtils.getDescription(pc._remoteDescription.sdp) +
4608                  sections.join('');
4609              if (pc.usingBundle) {
4610                break;
4611              }
4612            }
4613          } else {
4614            var sdpMLineIndex = candidate.sdpMLineIndex;
4615            if (candidate.sdpMid) {
4616              for (var i = 0; i < pc.transceivers.length; i++) {
4617                if (pc.transceivers[i].mid === candidate.sdpMid) {
4618                  sdpMLineIndex = i;
4619                  break;
4620                }
4621              }
4622            }
4623            var transceiver = pc.transceivers[sdpMLineIndex];
4624            if (transceiver) {
4625              if (transceiver.rejected) {
4626                return resolve();
4627              }
4628              var cand = Object.keys(candidate.candidate).length > 0 ?
4629                  SDPUtils.parseCandidate(candidate.candidate) : {};
4630              // Ignore Chrome's invalid candidates since Edge does not like them.
4631              if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
4632                return resolve();
4633              }
4634              // Ignore RTCP candidates, we assume RTCP-MUX.
4635              if (cand.component && cand.component !== 1) {
4636                return resolve();
4637              }
4638              // when using bundle, avoid adding candidates to the wrong
4639              // ice transport. And avoid adding candidates added in the SDP.
4640              if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&
4641                  transceiver.iceTransport !== pc.transceivers[0].iceTransport)) {
4642                if (!maybeAddCandidate(transceiver.iceTransport, cand)) {
4643                  return reject(makeError('OperationError',
4644                      'Can not add ICE candidate'));
4645                }
4646              }
4647    
4648              // update the remoteDescription.
4649              var candidateString = candidate.candidate.trim();
4650              if (candidateString.indexOf('a=') === 0) {
4651                candidateString = candidateString.substr(2);
4652              }
4653              sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);
4654              sections[sdpMLineIndex] += 'a=' +
4655                  (cand.type ? candidateString : 'end-of-candidates')
4656                  + '\r\n';
4657              pc._remoteDescription.sdp =
4658                  SDPUtils.getDescription(pc._remoteDescription.sdp) +
4659                  sections.join('');
4660            } else {
4661              return reject(makeError('OperationError',
4662                  'Can not add ICE candidate'));
4663            }
4664          }
4665          resolve();
4666        });
4667      };
4668    
4669      RTCPeerConnection.prototype.getStats = function(selector) {
4670        if (selector && selector instanceof window.MediaStreamTrack) {
4671          var senderOrReceiver = null;
4672          this.transceivers.forEach(function(transceiver) {
4673            if (transceiver.rtpSender &&
4674                transceiver.rtpSender.track === selector) {
4675              senderOrReceiver = transceiver.rtpSender;
4676            } else if (transceiver.rtpReceiver &&
4677                transceiver.rtpReceiver.track === selector) {
4678              senderOrReceiver = transceiver.rtpReceiver;
4679            }
4680          });
4681          if (!senderOrReceiver) {
4682            throw makeError('InvalidAccessError', 'Invalid selector.');
4683          }
4684          return senderOrReceiver.getStats();
4685        }
4686    
4687        var promises = [];
4688        this.transceivers.forEach(function(transceiver) {
4689          ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
4690              'dtlsTransport'].forEach(function(method) {
4691                if (transceiver[method]) {
4692                  promises.push(transceiver[method].getStats());
4693                }
4694              });
4695        });
4696        return Promise.all(promises).then(function(allStats) {
4697          var results = new Map();
4698          allStats.forEach(function(stats) {
4699            stats.forEach(function(stat) {
4700              results.set(stat.id, stat);
4701            });
4702          });
4703          return results;
4704        });
4705      };
4706    
4707      // fix low-level stat names and return Map instead of object.
4708      var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer',
4709        'RTCIceTransport', 'RTCDtlsTransport'];
4710      ortcObjects.forEach(function(ortcObjectName) {
4711        var obj = window[ortcObjectName];
4712        if (obj && obj.prototype && obj.prototype.getStats) {
4713          var nativeGetstats = obj.prototype.getStats;
4714          obj.prototype.getStats = function() {
4715            return nativeGetstats.apply(this)
4716            .then(function(nativeStats) {
4717              var mapStats = new Map();
4718              Object.keys(nativeStats).forEach(function(id) {
4719                nativeStats[id].type = fixStatsType(nativeStats[id]);
4720                mapStats.set(id, nativeStats[id]);
4721              });
4722              return mapStats;
4723            });
4724          };
4725        }
4726      });
4727    
4728      // legacy callback shims. Should be moved to adapter.js some days.
4729      var methods = ['createOffer', 'createAnswer'];
4730      methods.forEach(function(method) {
4731        var nativeMethod = RTCPeerConnection.prototype[method];
4732        RTCPeerConnection.prototype[method] = function() {
4733          var args = arguments;
4734          if (typeof args[0] === 'function' ||
4735              typeof args[1] === 'function') { // legacy
4736            return nativeMethod.apply(this, [arguments[2]])
4737            .then(function(description) {
4738              if (typeof args[0] === 'function') {
4739                args[0].apply(null, [description]);
4740              }
4741            }, function(error) {
4742              if (typeof args[1] === 'function') {
4743                args[1].apply(null, [error]);
4744              }
4745            });
4746          }
4747          return nativeMethod.apply(this, arguments);
4748        };
4749      });
4750    
4751      methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];
4752      methods.forEach(function(method) {
4753        var nativeMethod = RTCPeerConnection.prototype[method];
4754        RTCPeerConnection.prototype[method] = function() {
4755          var args = arguments;
4756          if (typeof args[1] === 'function' ||
4757              typeof args[2] === 'function') { // legacy
4758            return nativeMethod.apply(this, arguments)
4759            .then(function() {
4760              if (typeof args[1] === 'function') {
4761                args[1].apply(null);
4762              }
4763            }, function(error) {
4764              if (typeof args[2] === 'function') {
4765                args[2].apply(null, [error]);
4766              }
4767            });
4768          }
4769          return nativeMethod.apply(this, arguments);
4770        };
4771      });
4772    
4773      // getStats is special. It doesn't have a spec legacy method yet we support
4774      // getStats(something, cb) without error callbacks.
4775      ['getStats'].forEach(function(method) {
4776        var nativeMethod = RTCPeerConnection.prototype[method];
4777        RTCPeerConnection.prototype[method] = function() {
4778          var args = arguments;
4779          if (typeof args[1] === 'function') {
4780            return nativeMethod.apply(this, arguments)
4781            .then(function() {
4782              if (typeof args[1] === 'function') {
4783                args[1].apply(null);
4784              }
4785            });
4786          }
4787          return nativeMethod.apply(this, arguments);
4788        };
4789      });
4790    
4791      return RTCPeerConnection;
4792    };
4793    
4794    },{"sdp":17}],17:[function(require,module,exports){
4795    /* eslint-env node */
4796    'use strict';
4797    
4798    // SDP helpers.
4799    var SDPUtils = {};
4800    
4801    // Generate an alphanumeric identifier for cname or mids.
4802    // TODO: use UUIDs instead? https://gist.github.com/jed/982883
4803    SDPUtils.generateIdentifier = function() {
4804      return Math.random().toString(36).substr(2, 10);
4805    };
4806    
4807    // The RTCP CNAME used by all peerconnections from the same JS.
4808    SDPUtils.localCName = SDPUtils.generateIdentifier();
4809    
4810    // Splits SDP into lines, dealing with both CRLF and LF.
4811    SDPUtils.splitLines = function(blob) {
4812      return blob.trim().split('\n').map(function(line) {
4813        return line.trim();
4814      });
4815    };
4816    // Splits SDP into sessionpart and mediasections. Ensures CRLF.
4817    SDPUtils.splitSections = function(blob) {
4818      var parts = blob.split('\nm=');
4819      return parts.map(function(part, index) {
4820        return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
4821      });
4822    };
4823    
4824    // returns the session description.
4825    SDPUtils.getDescription = function(blob) {
4826      var sections = SDPUtils.splitSections(blob);
4827      return sections && sections[0];
4828    };
4829    
4830    // returns the individual media sections.
4831    SDPUtils.getMediaSections = function(blob) {
4832      var sections = SDPUtils.splitSections(blob);
4833      sections.shift();
4834      return sections;
4835    };
4836    
4837    // Returns lines that start with a certain prefix.
4838    SDPUtils.matchPrefix = function(blob, prefix) {
4839      return SDPUtils.splitLines(blob).filter(function(line) {
4840        return line.indexOf(prefix) === 0;
4841      });
4842    };
4843    
4844    // Parses an ICE candidate line. Sample input:
4845    // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
4846    // rport 55996"
4847    SDPUtils.parseCandidate = function(line) {
4848      var parts;
4849      // Parse both variants.
4850      if (line.indexOf('a=candidate:') === 0) {
4851        parts = line.substring(12).split(' ');
4852      } else {
4853        parts = line.substring(10).split(' ');
4854      }
4855    
4856      var candidate = {
4857        foundation: parts[0],
4858        component: parseInt(parts[1], 10),
4859        protocol: parts[2].toLowerCase(),
4860        priority: parseInt(parts[3], 10),
4861        ip: parts[4],
4862        address: parts[4], // address is an alias for ip.
4863        port: parseInt(parts[5], 10),
4864        // skip parts[6] == 'typ'
4865        type: parts[7]
4866      };
4867    
4868      for (var i = 8; i < parts.length; i += 2) {
4869        switch (parts[i]) {
4870          case 'raddr':
4871            candidate.relatedAddress = parts[i + 1];
4872            break;
4873          case 'rport':
4874            candidate.relatedPort = parseInt(parts[i + 1], 10);
4875            break;
4876          case 'tcptype':
4877            candidate.tcpType = parts[i + 1];
4878            break;
4879          case 'ufrag':
4880            candidate.ufrag = parts[i + 1]; // for backward compability.
4881            candidate.usernameFragment = parts[i + 1];
4882            break;
4883          default: // extension handling, in particular ufrag
4884            candidate[parts[i]] = parts[i + 1];
4885            break;
4886        }
4887      }
4888      return candidate;
4889    };
4890    
4891    // Translates a candidate object into SDP candidate attribute.
4892    SDPUtils.writeCandidate = function(candidate) {
4893      var sdp = [];
4894      sdp.push(candidate.foundation);
4895      sdp.push(candidate.component);
4896      sdp.push(candidate.protocol.toUpperCase());
4897      sdp.push(candidate.priority);
4898      sdp.push(candidate.address || candidate.ip);
4899      sdp.push(candidate.port);
4900    
4901      var type = candidate.type;
4902      sdp.push('typ');
4903      sdp.push(type);
4904      if (type !== 'host' && candidate.relatedAddress &&
4905          candidate.relatedPort) {
4906        sdp.push('raddr');
4907        sdp.push(candidate.relatedAddress);
4908        sdp.push('rport');
4909        sdp.push(candidate.relatedPort);
4910      }
4911      if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
4912        sdp.push('tcptype');
4913        sdp.push(candidate.tcpType);
4914      }
4915      if (candidate.usernameFragment || candidate.ufrag) {
4916        sdp.push('ufrag');
4917        sdp.push(candidate.usernameFragment || candidate.ufrag);
4918      }
4919      return 'candidate:' + sdp.join(' ');
4920    };
4921    
4922    // Parses an ice-options line, returns an array of option tags.
4923    // a=ice-options:foo bar
4924    SDPUtils.parseIceOptions = function(line) {
4925      return line.substr(14).split(' ');
4926    };
4927    
4928    // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
4929    // a=rtpmap:111 opus/48000/2
4930    SDPUtils.parseRtpMap = function(line) {
4931      var parts = line.substr(9).split(' ');
4932      var parsed = {
4933        payloadType: parseInt(parts.shift(), 10) // was: id
4934      };
4935    
4936      parts = parts[0].split('/');
4937    
4938      parsed.name = parts[0];
4939      parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
4940      parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
4941      // legacy alias, got renamed back to channels in ORTC.
4942      parsed.numChannels = parsed.channels;
4943      return parsed;
4944    };
4945    
4946    // Generate an a=rtpmap line from RTCRtpCodecCapability or
4947    // RTCRtpCodecParameters.
4948    SDPUtils.writeRtpMap = function(codec) {
4949      var pt = codec.payloadType;
4950      if (codec.preferredPayloadType !== undefined) {
4951        pt = codec.preferredPayloadType;
4952      }
4953      var channels = codec.channels || codec.numChannels || 1;
4954      return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
4955          (channels !== 1 ? '/' + channels : '') + '\r\n';
4956    };
4957    
4958    // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
4959    // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
4960    // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
4961    SDPUtils.parseExtmap = function(line) {
4962      var parts = line.substr(9).split(' ');
4963      return {
4964        id: parseInt(parts[0], 10),
4965        direction: parts[0].indexOf('/') >
4965 0 ? parts[0].split('/')[1] : 'sendrecv',
4966        uri: parts[1]
4967      };
4968    };
4969    
4970    // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
4971    // RTCRtpHeaderExtension.
4972    SDPUtils.writeExtmap = function(headerExtension) {
4973      return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
4974          (headerExtension.direction && headerExtension.direction !== 'sendrecv'
4975            ? '/' + headerExtension.direction
4976            : '') +
4977          ' ' + headerExtension.uri + '\r\n';
4978    };
4979    
4980    // Parses an ftmp line, returns dictionary. Sample input:
4981    // a=fmtp:96 vbr=on;cng=on
4982    // Also deals with vbr=on; cng=on
4983    SDPUtils.parseFmtp = function(line) {
4984      var parsed = {};
4985      var kv;
4986      var parts = line.substr(line.indexOf(' ') + 1).split(';');
4987      for (var j = 0; j < parts.length; j++) {
4988        kv = parts[j].trim().split('=');
4989        parsed[kv[0].trim()] = kv[1];
4990      }
4991      return parsed;
4992    };
4993    
4994    // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
4995    SDPUtils.writeFmtp = function(codec) {
4996      var line = '';
4997      var pt = codec.payloadType;
4998      if (codec.preferredPayloadType !== undefined) {
4999        pt = codec.preferredPayloadType;
5000      }
5001      if (codec.parameters && Object.keys(codec.parameters).length) {
5002        var params = [];
5003        Object.keys(codec.parameters).forEach(function(param) {
5004          if (codec.parameters[param]) {
5005            params.push(param + '=' + codec.parameters[param]);
5006          } else {
5007            params.push(param);
5008          }
5009        });
5010        line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
5011      }
5012      return line;
5013    };
5014    
5015    // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
5016    // a=rtcp-fb:98 nack rpsi
5017    SDPUtils.parseRtcpFb = function(line) {
5018      var parts = line.substr(line.indexOf(' ') + 1).split(' ');
5019      return {
5020        type: parts.shift(),
5021        parameter: parts.join(' ')
5022      };
5023    };
5024    // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
5025    SDPUtils.writeRtcpFb = function(codec) {
5026      var lines = '';
5027      var pt = codec.payloadType;
5028      if (codec.preferredPayloadType !== undefined) {
5029        pt = codec.preferredPayloadType;
5030      }
5031      if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
5032        // FIXME: special handling for trr-int?
5033        codec.rtcpFeedback.forEach(function(fb) {
5034          lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
5035          (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
5036              '\r\n';
5037        });
5038      }
5039      return lines;
5040    };
5041    
5042    // Parses an RFC 5576 ssrc media attribute. Sample input:
5043    // a=ssrc:3735928559 cname:something
5044    SDPUtils.parseSsrcMedia = function(line) {
5045      var sp = line.indexOf(' ');
5046      var parts = {
5047        ssrc: parseInt(line.substr(7, sp - 7), 10)
5048      };
5049      var colon = line.indexOf(':', sp);
5050      if (colon > -1) {
5051        parts.attribute = line.substr(sp + 1, colon - sp - 1);
5052        parts.value = line.substr(colon + 1);
5053      } else {
5054        parts.attribute = line.substr(sp + 1);
5055      }
5056      return parts;
5057    };
5058    
5059    SDPUtils.parseSsrcGroup = function(line) {
5060      var parts = line.substr(13).split(' ');
5061      return {
5062        semantics: parts.shift(),
5063        ssrcs: parts.map(function(ssrc) {
5064          return parseInt(ssrc, 10);
5065        })
5066      };
5067    };
5068    
5069    // Extracts the MID (RFC 5888) from a media section.
5070    // returns the MID or undefined if no mid line was found.
5071    SDPUtils.getMid = function(mediaSection) {
5072      var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
5073      if (mid) {
5074        return mid.substr(6);
5075      }
5076    };
5077    
5078    SDPUtils.parseFingerprint = function(line) {
5079      var parts = line.substr(14).split(' ');
5080      return {
5081        algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
5082        value: parts[1]
5083      };
5084    };
5085    
5086    // Extracts DTLS parameters from SDP media section or sessionpart.
5087    // FIXME: for consistency with other functions this should only
5088    //   get the fingerprint line as input. See also getIceParameters.
5089    SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
5090      var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
5091        'a=fingerprint:');
5092      // Note: a=setup line is ignored since we use the 'auto' role.
5093      // Note2: 'algorithm' is not case sensitive except in Edge.
5094      return {
5095        role: 'auto',
5096        fingerprints: lines.map(SDPUtils.parseFingerprint)
5097      };
5098    };
5099    
5100    // Serializes DTLS parameters to SDP.
5101    SDPUtils.writeDtlsParameters = function(params, setupType) {
5102      var sdp = 'a=setup:' + setupType + '\r\n';
5103      params.fingerprints.forEach(function(fp) {
5104        sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
5105      });
5106      return sdp;
5107    };
5108    
5109    // Parses a=crypto lines into
5110    //   https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
5111    SDPUtils.parseCryptoLine = function(line) {
5112      var parts = line.substr(9).split(' ');
5113      return {
5114        tag: parseInt(parts[0], 10),
5115        cryptoSuite: parts[1],
5116        keyParams: parts[2],
5117        sessionParams: parts.slice(3),
5118      };
5119    };
5120    
5121    SDPUtils.writeCryptoLine = function(parameters) {
5122      return 'a=crypto:' + parameters.tag + ' ' +
5123        parameters.cryptoSuite + ' ' +
5124        (typeof parameters.keyParams === 'object'
5125          ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)
5126          : parameters.keyParams) +
5127        (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +
5128        '\r\n';
5129    };
5130    
5131    // Parses the crypto key parameters into
5132    //   https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
5133    SDPUtils.parseCryptoKeyParams = function(keyParams) {
5134      if (keyParams.indexOf('inline:') !== 0) {
5135        return null;
5136      }
5137      var parts = keyParams.substr(7).split('|');
5138      return {
5139        keyMethod: 'inline',
5140        keySalt: parts[0],
5141        lifeTime: parts[1],
5142        mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
5143        mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,
5144      };
5145    };
5146    
5147    SDPUtils.writeCryptoKeyParams = function(keyParams) {
5148      return keyParams.keyMethod + ':'
5149        + keyParams.keySalt +
5150        (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +
5151        (keyParams.mkiValue && keyParams.mkiLength
5152          ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength
5153          : '');
5154    };
5155    
5156    // Extracts all SDES paramters.
5157    SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {
5158      var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
5159        'a=crypto:');
5160      return lines.map(SDPUtils.parseCryptoLine);
5161    };
5162    
5163    // Parses ICE information from SDP media section or sessionpart.
5164    // FIXME: for consistency with other functions this should only
5165    //   get the ice-ufrag and ice-pwd lines as input.
5166    SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
5167      var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,
5168        'a=ice-ufrag:')[0];
5169      var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,
5170        'a=ice-pwd:')[0];
5171      if (!(ufrag && pwd)) {
5172        return null;
5173      }
5174      return {
5175        usernameFragment: ufrag.substr(12),
5176        password: pwd.substr(10),
5177      };
5178    };
5179    
5180    // Serializes ICE parameters to SDP.
5181    SDPUtils.writeIceParameters = function(params) {
5182      return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
5183          'a=ice-pwd:' + params.password + '\r\n';
5184    };
5185    
5186    // Parses the SDP media section and returns RTCRtpParameters.
5187    SDPUtils.parseRtpParameters = function(mediaSection) {
5188      var description = {
5189        codecs: [],
5190        headerExtensions: [],
5191        fecMechanisms: [],
5192        rtcp: []
5193      };
5194      var lines = SDPUtils.splitLines(mediaSection);
5195      var mline = lines[0].split(' ');
5196      for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
5197        var pt = mline[i];
5198        var rtpmapline = SDPUtils.matchPrefix(
5199          mediaSection, 'a=rtpmap:' + pt + ' ')[0];
5200        if (rtpmapline) {
5201          var codec = SDPUtils.parseRtpMap(rtpmapline);
5202          var fmtps = SDPUtils.matchPrefix(
5203            mediaSection, 'a=fmtp:' + pt + ' ');
5204          // Only the first a=fmtp:<pt> is considered.
5205          codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
5206          codec.rtcpFeedback = SDPUtils.matchPrefix(
5207            mediaSection, 'a=rtcp-fb:' + pt + ' ')
5208            .map(SDPUtils.parseRtcpFb);
5209          description.codecs.push(codec);
5210          // parse FEC mechanisms from rtpmap lines.
5211          switch (codec.name.toUpperCase()) {
5212            case 'RED':
5213            case 'ULPFEC':
5214              description.fecMechanisms.push(codec.name.toUpperCase());
5215              break;
5216            default: // only RED and ULPFEC are recognized as FEC mechanisms.
5217              break;
5218          }
5219        }
5220      }
5221      SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
5222        description.headerExtensions.push(SDPUtils.parseExtmap(line));
5223      });
5224      // FIXME: parse rtcp.
5225      return description;
5226    };
5227    
5228    // Generates parts of the SDP media section describing the capabilities /
5229    // parameters.
5230    SDPUtils.writeRtpDescription = function(kind, caps) {
5231      var sdp = '';
5232    
5233      // Build the mline.
5234      sdp += 'm=' + kind + ' ';
5235      sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
5236      sdp += ' UDP/TLS/RTP/SAVPF ';
5237      sdp += caps.codecs.map(function(codec) {
5238        if (codec.preferredPayloadType !== undefined) {
5239          return codec.preferredPayloadType;
5240        }
5241        return codec.payloadType;
5242      }).join(' ') + '\r\n';
5243    
5244      sdp += 'c=IN IP4 0.0.0.0\r\n';
5245      sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
5246    
5247      // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
5248      caps.codecs.forEach(function(codec) {
5249        sdp += SDPUtils.writeRtpMap(codec);
5250        sdp += SDPUtils.writeFmtp(codec);
5251        sdp += SDPUtils.writeRtcpFb(codec);
5252      });
5253      var maxptime = 0;
5254      caps.codecs.forEach(function(codec) {
5255        if (codec.maxptime > maxptime) {
5256          maxptime = codec.maxptime;
5257        }
5258      });
5259      if (maxptime > 0) {
5260        sdp += 'a=maxptime:' + maxptime + '\r\n';
5261      }
5262      sdp += 'a=rtcp-mux\r\n';
5263    
5264      if (caps.headerExtensions) {
5265        caps.headerExtensions.forEach(function(extension) {
5266          sdp += SDPUtils.writeExtmap(extension);
5267        });
5268      }
5269      // FIXME: write fecMechanisms.
5270      return sdp;
5271    };
5272    
5273    // Parses the SDP media section and returns an array of
5274    // RTCRtpEncodingParameters.
5275    SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
5276      var encodingParameters = [];
5277      var description = SDPUtils.parseRtpParameters(mediaSection);
5278      var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
5279      var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
5280    
5281      // filter a=ssrc:... cname:, ignore PlanB-msid
5282      var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
5283        .map(function(line) {
5284          return SDPUtils.parseSsrcMedia(line);
5285        })
5286        .filter(function(parts) {
5287          return parts.attribute === 'cname';
5288        });
5289      var primarySsrc = ssrcs.length >
5289 0 && ssrcs[0].ssrc;
5290      var secondarySsrc;
5291    
5292      var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
5293        .map(function(line) {
5294          var parts = line.substr(17).split(' ');
5295          return parts.map(function(part) {
5296            return parseInt(part, 10);
5297          });
5298        });
5299      if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
5300        secondarySsrc = flows[0][1];
5301      }
5302    
5303      description.codecs.forEach(function(codec) {
5304        if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
5305          var encParam = {
5306            ssrc: primarySsrc,
5307            codecPayloadType: parseInt(codec.parameters.apt, 10)
5308          };
5309          if (primarySsrc && secondarySsrc) {
5310            encParam.rtx = {ssrc: secondarySsrc};
5311          }
5312          encodingParameters.push(encParam);
5313          if (hasRed) {
5314            encParam = JSON.parse(JSON.stringify(encParam));
5315            encParam.fec = {
5316              ssrc: primarySsrc,
5317              mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
5318            };
5319            encodingParameters.push(encParam);
5320          }
5321        }
5322      });
5323      if (encodingParameters.length === 0 && primarySsrc) {
5324        encodingParameters.push({
5325          ssrc: primarySsrc
5326        });
5327      }
5328    
5329      // we support both b=AS and b=TIAS but interpret AS as TIAS.
5330      var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
5331      if (bandwidth.length) {
5332        if (bandwidth[0].indexOf('b=TIAS:') === 0) {
5333          bandwidth = parseInt(bandwidth[0].substr(7), 10);
5334        } else if (bandwidth[0].indexOf('b=AS:') === 0) {
5335          // use formula from JSEP to convert b=AS to TIAS value.
5336          bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
5337              - (50 * 40 * 8);
5338        } else {
5339          bandwidth = undefined;
5340        }
5341        encodingParameters.forEach(function(params) {
5342          params.maxBitrate = bandwidth;
5343        });
5344      }
5345      return encodingParameters;
5346    };
5347    
5348    // parses http://draft.ortc.org/#rtcrtcpparameters*
5349    SDPUtils.parseRtcpParameters = function(mediaSection) {
5350      var rtcpParameters = {};
5351    
5352      // Gets the first SSRC. Note tha with RTX there might be multiple
5353      // SSRCs.
5354      var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
5355        .map(function(line) {
5356          return SDPUtils.parseSsrcMedia(line);
5357        })
5358        .filter(function(obj) {
5359          return obj.attribute === 'cname';
5360        })[0];
5361      if (remoteSsrc) {
5362        rtcpParameters.cname = remoteSsrc.value;
5363        rtcpParameters.ssrc = remoteSsrc.ssrc;
5364      }
5365    
5366      // Edge uses the compound attribute instead of reducedSize
5367      // compound is !reducedSize
5368      var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
5369      rtcpParameters.reducedSize = rsize.length > 0;
5370      rtcpParameters.compound = rsize.length === 0;
5371    
5372      // parses the rtcp-mux attrіbute.
5373      // Note that Edge does not support unmuxed RTCP.
5374      var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
5375      rtcpParameters.mux = mux.length > 0;
5376    
5377      return rtcpParameters;
5378    };
5379    
5380    // parses either a=msid: or a=ssrc:... msid lines and returns
5381    // the id of the MediaStream and MediaStreamTrack.
5382    SDPUtils.parseMsid = function(mediaSection) {
5383      var parts;
5384      var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
5385      if (spec.length === 1) {
5386        parts = spec[0].substr(7).split(' ');
5387        return {stream: parts[0], track: parts[1]};
5388      }
5389      var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
5390        .map(function(line) {
5391          return SDPUtils.parseSsrcMedia(line);
5392        })
5393        .filter(function(msidParts) {
5394          return msidParts.attribute === 'msid';
5395        });
5396      if (planB.length > 0) {
5397        parts = planB[0].value.split(' ');
5398        return {stream: parts[0], track: parts[1]};
5399      }
5400    };
5401    
5402    // SCTP
5403    // parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
5404    // to draft-ietf-mmusic-sctp-sdp-05
5405    SDPUtils.parseSctpDescription = function(mediaSection) {
5406      var mline = SDPUtils.parseMLine(mediaSection);
5407      var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
5408      var maxMessageSize;
5409      if (maxSizeLine.length > 0) {
5410        maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);
5411      }
5412      if (isNaN(maxMessageSize)) {
5413        maxMessageSize = 65536;
5414      }
5415      var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
5416      if (sctpPort.length > 0) {
5417        return {
5418          port: parseInt(sctpPort[0].substr(12), 10),
5419          protocol: mline.fmt,
5420          maxMessageSize: maxMessageSize
5421        };
5422      }
5423      var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
5424      if (sctpMapLines.length > 0) {
5425        var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0]
5426          .substr(10)
5427          .split(' ');
5428        return {
5429          port: parseInt(parts[0], 10),
5430          protocol: parts[1],
5431          maxMessageSize: maxMessageSize
5432        };
5433      }
5434    };
5435    
5436    // SCTP
5437    // outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
5438    // support by now receiving in this format, unless we originally parsed
5439    // as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line
5440    // protocol of DTLS/SCTP -- without UDP/ or TCP/)
5441    SDPUtils.writeSctpDescription = function(media, sctp) {
5442      var output = [];
5443      if (media.protocol !== 'DTLS/SCTP') {
5444        output = [
5445          'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n',
5446          'c=IN IP4 0.0.0.0\r\n',
5447          'a=sctp-port:' + sctp.port + '\r\n'
5448        ];
5449      } else {
5450        output = [
5451          'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n',
5452          'c=IN IP4 0.0.0.0\r\n',
5453          'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n'
5454        ];
5455      }
5456      if (sctp.maxMessageSize !== undefined) {
5457        output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
5458      }
5459      return output.join('');
5460    };
5461    
5462    // Generate a session ID for SDP.
5463    // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
5464    // recommends using a cryptographically random +ve 64-bit value
5465    // but right now this should be acceptable and within the right range
5466    SDPUtils.generateSessionId = function() {
5467      return Math.random().toString().substr(2, 21);
5468    };
5469    
5470    // Write boilder plate for start of SDP
5471    // sessId argument is optional - if not supplied it will
5472    // be generated randomly
5473    // sessVersion is optional and defaults to 2
5474    // sessUser is optional and defaults to 'thisisadapterortc'
5475    SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {
5476      var sessionId;
5477      var version = sessVer !== undefined ? sessVer : 2;
5478      if (sessId) {
5479        sessionId = sessId;
5480      } else {
5481        sessionId = SDPUtils.generateSessionId();
5482      }
5483      var user = sessUser || 'thisisadapterortc';
5484      // FIXME: sess-id should be an NTP timestamp.
5485      return 'v=0\r\n' +
5486          'o=' + user + ' ' + sessionId + ' ' + version +
5487            ' IN IP4 127.0.0.1\r\n' +
5488          's=-\r\n' +
5489          't=0 0\r\n';
5490    };
5491    
5492    SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
5493      var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
5494    
5495      // Map ICE parameters (ufrag, pwd) to SDP.
5496      sdp += SDPUtils.writeIceParameters(
5497        transceiver.iceGatherer.getLocalParameters());
5498    
5499      // Map DTLS parameters to SDP.
5500      sdp += SDPUtils.writeDtlsParameters(
5501        transceiver.dtlsTransport.getLocalParameters(),
5502        type === 'offer' ? 'actpass' : 'active');
5503    
5504      sdp += 'a=mid:' + transceiver.mid + '\r\n';
5505    
5506      if (transceiver.direction) {
5507        sdp += 'a=' + transceiver.direction + '\r\n';
5508      } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
5509        sdp += 'a=sendrecv\r\n';
5510      } else if (transceiver.rtpSender) {
5511        sdp += 'a=sendonly\r\n';
5512      } else if (transceiver.rtpReceiver) {
5513        sdp += 'a=recvonly\r\n';
5514      } else {
5515        sdp += 'a=inactive\r\n';
5516      }
5517    
5518      if (transceiver.rtpSender) {
5519        // spec.
5520        var msid = 'msid:' + stream.id + ' ' +
5521            transceiver.rtpSender.track.id + '\r\n';
5522        sdp += 'a=' + msid;
5523    
5524        // for Chrome.
5525        sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
5526            ' ' + msid;
5527        if (transceiver.sendEncodingParameters[0].rtx) {
5528          sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
5529              ' ' + msid;
5530          sdp += 'a=ssrc-group:FID ' +
5531              transceiver.sendEncodingParameters[0].ssrc + ' ' +
5532              transceiver.sendEncodingParameters[0].rtx.ssrc +
5533              '\r\n';
5534        }
5535      }
5536      // FIXME: this should be written by writeRtpDescription.
5537      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
5538          ' cname:' + SDPUtils.localCName + '\r\n';
5539      if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
5540        sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
5541            ' cname:' + SDPUtils.localCName + '\r\n';
5542      }
5543      return sdp;
5544    };
5545    
5546    // Gets the direction from the mediaSection or the sessionpart.
5547    SDPUtils.getDirection = function(mediaSection, sessionpart) {
5548      // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
5549      var lines = SDPUtils.splitLines(mediaSection);
5550      for (var i = 0; i < lines.length; i++) {
5551        switch (lines[i]) {
5552          case 'a=sendrecv':
5553          case 'a=sendonly':
5554          case 'a=recvonly':
5555          case 'a=inactive':
5556            return lines[i].substr(2);
5557          default:
5558            // FIXME: What should happen here?
5559        }
5560      }
5561      if (sessionpart) {
5562        return SDPUtils.getDirection(sessionpart);
5563      }
5564      return 'sendrecv';
5565    };
5566    
5567    SDPUtils.getKind = function(mediaSection) {
5568      var lines = SDPUtils.splitLines(mediaSection);
5569      var mline = lines[0].split(' ');
5570      return mline[0].substr(2);
5571    };
5572    
5573    SDPUtils.isRejected = function(mediaSection) {
5574      return mediaSection.split(' ', 2)[1] === '0';
5575    };
5576    
5577    SDPUtils.parseMLine = function(mediaSection) {
5578      var lines = SDPUtils.splitLines(mediaSection);
5579      var parts = lines[0].substr(2).split(' ');
5580      return {
5581        kind: parts[0],
5582        port: parseInt(parts[1], 10),
5583        protocol: parts[2],
5584        fmt: parts.slice(3).join(' ')
5585      };
5586    };
5587    
5588    SDPUtils.parseOLine = function(mediaSection) {
5589      var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
5590      var parts = line.substr(2).split(' ');
5591      return {
5592        username: parts[0],
5593        sessionId: parts[1],
5594        sessionVersion: parseInt(parts[2], 10),
5595        netType: parts[3],
5596        addressType: parts[4],
5597        address: parts[5]
5598      };
5599    };
5600    
5601    // a very naive interpretation of a valid SDP.
5602    SDPUtils.isValidSDP = function(blob) {
5603      if (typeof blob !== 'string' || blob.length === 0) {
5604        return false;
5605      }
5606      var lines = SDPUtils.splitLines(blob);
5607      for (var i = 0; i < lines.length; i++) {
5608        if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
5609          return false;
5610        }
5611        // TODO: check the modifier a bit more.
5612      }
5613      return true;
5614    };
5615    
5616    // Expose public methods.
5617    if (typeof module === 'object') {
5618      module.exports = SDPUtils;
5619    }
5620    
5621    },{}]},{},[1])(1)
5622    });

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.