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.